@webiny/app-record-locking 0.0.0-unstable.3bc8100a7f

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +12 -0
  3. package/components/HeadlessCmsActionsAcoCell.d.ts +2 -0
  4. package/components/HeadlessCmsActionsAcoCell.js +60 -0
  5. package/components/HeadlessCmsActionsAcoCell.js.map +1 -0
  6. package/components/HeadlessCmsContentEntry/ContentEntryGuard.d.ts +9 -0
  7. package/components/HeadlessCmsContentEntry/ContentEntryGuard.js +40 -0
  8. package/components/HeadlessCmsContentEntry/ContentEntryGuard.js.map +1 -0
  9. package/components/HeadlessCmsContentEntry/ContentEntryLocker.d.ts +18 -0
  10. package/components/HeadlessCmsContentEntry/ContentEntryLocker.js +106 -0
  11. package/components/HeadlessCmsContentEntry/ContentEntryLocker.js.map +1 -0
  12. package/components/HeadlessCmsContentEntry/HeadlessCmsContentEntry.d.ts +2 -0
  13. package/components/HeadlessCmsContentEntry/HeadlessCmsContentEntry.js +90 -0
  14. package/components/HeadlessCmsContentEntry/HeadlessCmsContentEntry.js.map +1 -0
  15. package/components/HeadlessCmsContentEntry/index.d.ts +1 -0
  16. package/components/HeadlessCmsContentEntry/index.js +3 -0
  17. package/components/HeadlessCmsContentEntry/index.js.map +1 -0
  18. package/components/LockedRecord/LockedRecord.d.ts +6 -0
  19. package/components/LockedRecord/LockedRecord.js +66 -0
  20. package/components/LockedRecord/LockedRecord.js.map +1 -0
  21. package/components/LockedRecord/LockedRecordForceUnlock.d.ts +9 -0
  22. package/components/LockedRecord/LockedRecordForceUnlock.js +75 -0
  23. package/components/LockedRecord/LockedRecordForceUnlock.js.map +1 -0
  24. package/components/LockedRecord/index.d.ts +1 -0
  25. package/components/LockedRecord/index.js +3 -0
  26. package/components/LockedRecord/index.js.map +1 -0
  27. package/components/RecordLockingProvider.d.ts +7 -0
  28. package/components/RecordLockingProvider.js +115 -0
  29. package/components/RecordLockingProvider.js.map +1 -0
  30. package/components/decorators/UseContentEntriesListHookDecorator.d.ts +2 -0
  31. package/components/decorators/UseContentEntriesListHookDecorator.js +21 -0
  32. package/components/decorators/UseContentEntriesListHookDecorator.js.map +1 -0
  33. package/components/decorators/UseRecordsDecorator.d.ts +9 -0
  34. package/components/decorators/UseRecordsDecorator.js +25 -0
  35. package/components/decorators/UseRecordsDecorator.js.map +1 -0
  36. package/components/decorators/UseSaveEntryDecorator.d.ts +2 -0
  37. package/components/decorators/UseSaveEntryDecorator.js +55 -0
  38. package/components/decorators/UseSaveEntryDecorator.js.map +1 -0
  39. package/components/permissionRenderer/RecordLockingPermissions.d.ts +7 -0
  40. package/components/permissionRenderer/RecordLockingPermissions.js +71 -0
  41. package/components/permissionRenderer/RecordLockingPermissions.js.map +1 -0
  42. package/components/permissionRenderer/index.d.ts +2 -0
  43. package/components/permissionRenderer/index.js +21 -0
  44. package/components/permissionRenderer/index.js.map +1 -0
  45. package/domain/RecordLocking.d.ts +28 -0
  46. package/domain/RecordLocking.js +320 -0
  47. package/domain/RecordLocking.js.map +1 -0
  48. package/domain/RecordLockingClient.d.ts +12 -0
  49. package/domain/RecordLockingClient.js +19 -0
  50. package/domain/RecordLockingClient.js.map +1 -0
  51. package/domain/RecordLockingGetLockRecord.d.ts +11 -0
  52. package/domain/RecordLockingGetLockRecord.js +21 -0
  53. package/domain/RecordLockingGetLockRecord.js.map +1 -0
  54. package/domain/RecordLockingGetLockedEntryLockRecord.d.ts +11 -0
  55. package/domain/RecordLockingGetLockedEntryLockRecord.js +19 -0
  56. package/domain/RecordLockingGetLockedEntryLockRecord.js.map +1 -0
  57. package/domain/RecordLockingIsEntryLocked.d.ts +11 -0
  58. package/domain/RecordLockingIsEntryLocked.js +19 -0
  59. package/domain/RecordLockingIsEntryLocked.js.map +1 -0
  60. package/domain/RecordLockingListLockRecords.d.ts +12 -0
  61. package/domain/RecordLockingListLockRecords.js +31 -0
  62. package/domain/RecordLockingListLockRecords.js.map +1 -0
  63. package/domain/RecordLockingLockEntry.d.ts +11 -0
  64. package/domain/RecordLockingLockEntry.js +15 -0
  65. package/domain/RecordLockingLockEntry.js.map +1 -0
  66. package/domain/RecordLockingUnlockEntry.d.ts +11 -0
  67. package/domain/RecordLockingUnlockEntry.js +19 -0
  68. package/domain/RecordLockingUnlockEntry.js.map +1 -0
  69. package/domain/RecordLockingUpdateEntryLock.d.ts +11 -0
  70. package/domain/RecordLockingUpdateEntryLock.js +19 -0
  71. package/domain/RecordLockingUpdateEntryLock.js.map +1 -0
  72. package/domain/abstractions/IRecordLocking.d.ts +25 -0
  73. package/domain/abstractions/IRecordLocking.js +3 -0
  74. package/domain/abstractions/IRecordLocking.js.map +1 -0
  75. package/domain/abstractions/IRecordLockingClient.d.ts +6 -0
  76. package/domain/abstractions/IRecordLockingClient.js +3 -0
  77. package/domain/abstractions/IRecordLockingClient.js.map +1 -0
  78. package/domain/abstractions/IRecordLockingGetLockRecord.d.ts +12 -0
  79. package/domain/abstractions/IRecordLockingGetLockRecord.js +3 -0
  80. package/domain/abstractions/IRecordLockingGetLockRecord.js.map +1 -0
  81. package/domain/abstractions/IRecordLockingGetLockedEntryLockRecord.d.ts +12 -0
  82. package/domain/abstractions/IRecordLockingGetLockedEntryLockRecord.js +3 -0
  83. package/domain/abstractions/IRecordLockingGetLockedEntryLockRecord.js.map +1 -0
  84. package/domain/abstractions/IRecordLockingIsEntryLocked.d.ts +8 -0
  85. package/domain/abstractions/IRecordLockingIsEntryLocked.js +3 -0
  86. package/domain/abstractions/IRecordLockingIsEntryLocked.js.map +1 -0
  87. package/domain/abstractions/IRecordLockingListLockRecords.d.ts +19 -0
  88. package/domain/abstractions/IRecordLockingListLockRecords.js +3 -0
  89. package/domain/abstractions/IRecordLockingListLockRecords.js.map +1 -0
  90. package/domain/abstractions/IRecordLockingLockEntry.d.ts +12 -0
  91. package/domain/abstractions/IRecordLockingLockEntry.js +3 -0
  92. package/domain/abstractions/IRecordLockingLockEntry.js.map +1 -0
  93. package/domain/abstractions/IRecordLockingUnlockEntry.d.ts +13 -0
  94. package/domain/abstractions/IRecordLockingUnlockEntry.js +3 -0
  95. package/domain/abstractions/IRecordLockingUnlockEntry.js.map +1 -0
  96. package/domain/abstractions/IRecordLockingUpdateEntryLock.d.ts +12 -0
  97. package/domain/abstractions/IRecordLockingUpdateEntryLock.js +3 -0
  98. package/domain/abstractions/IRecordLockingUpdateEntryLock.js.map +1 -0
  99. package/domain/graphql/fields.d.ts +2 -0
  100. package/domain/graphql/fields.js +30 -0
  101. package/domain/graphql/fields.js.map +1 -0
  102. package/domain/graphql/getLockRecord.d.ts +12 -0
  103. package/domain/graphql/getLockRecord.js +18 -0
  104. package/domain/graphql/getLockRecord.js.map +1 -0
  105. package/domain/graphql/getLockedEntryLockRecord.d.ts +12 -0
  106. package/domain/graphql/getLockedEntryLockRecord.js +18 -0
  107. package/domain/graphql/getLockedEntryLockRecord.js.map +1 -0
  108. package/domain/graphql/isEntryLocked.d.ts +12 -0
  109. package/domain/graphql/isEntryLocked.js +16 -0
  110. package/domain/graphql/isEntryLocked.js.map +1 -0
  111. package/domain/graphql/listLockRecords.d.ts +17 -0
  112. package/domain/graphql/listLockRecords.js +26 -0
  113. package/domain/graphql/listLockRecords.js.map +1 -0
  114. package/domain/graphql/lockEntry.d.ts +12 -0
  115. package/domain/graphql/lockEntry.js +20 -0
  116. package/domain/graphql/lockEntry.js.map +1 -0
  117. package/domain/graphql/unlockEntry.d.ts +12 -0
  118. package/domain/graphql/unlockEntry.js +18 -0
  119. package/domain/graphql/unlockEntry.js.map +1 -0
  120. package/domain/graphql/updateEntryLock.d.ts +12 -0
  121. package/domain/graphql/updateEntryLock.js +18 -0
  122. package/domain/graphql/updateEntryLock.js.map +1 -0
  123. package/domain/utils/createRecordLockingClient.d.ts +3 -0
  124. package/domain/utils/createRecordLockingClient.js +12 -0
  125. package/domain/utils/createRecordLockingClient.js.map +1 -0
  126. package/domain/utils/createRecordLockingError.d.ts +6 -0
  127. package/domain/utils/createRecordLockingError.js +16 -0
  128. package/domain/utils/createRecordLockingError.js.map +1 -0
  129. package/hooks/index.d.ts +2 -0
  130. package/hooks/index.js +4 -0
  131. package/hooks/index.js.map +1 -0
  132. package/hooks/usePermission.d.ts +3 -0
  133. package/hooks/usePermission.js +21 -0
  134. package/hooks/usePermission.js.map +1 -0
  135. package/hooks/useRecordLocking.d.ts +2 -0
  136. package/hooks/useRecordLocking.js +12 -0
  137. package/hooks/useRecordLocking.js.map +1 -0
  138. package/index.d.ts +7 -0
  139. package/index.js +29 -0
  140. package/index.js.map +1 -0
  141. package/package.json +52 -0
  142. package/types.d.ts +77 -0
  143. package/types.js +3 -0
  144. package/types.js.map +1 -0
  145. package/utils/createCacheKey.d.ts +3 -0
  146. package/utils/createCacheKey.js +15 -0
  147. package/utils/createCacheKey.js.map +1 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Webiny
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,12 @@
1
+ # @webiny/app-record-locking
2
+ [![](https://img.shields.io/npm/dw/@webiny/app-record-locking.svg)](https://www.npmjs.com/package/@webiny/app-record-locking)
3
+ [![](https://img.shields.io/npm/v/@webiny/app-record-locking.svg)](https://www.npmjs.com/package/@webiny/app-record-locking)
4
+ [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
5
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
6
+
7
+ Exposes a simple `SocketsProvider` React provider component and enables you to quickly send socket messages via the `useSockets` React hook.
8
+
9
+ ## Install
10
+ ```
11
+ yarn add @webiny/app-record-locking
12
+ ```
@@ -0,0 +1,2 @@
1
+ import React from "react";
2
+ export declare const HeadlessCmsActionsAcoCell: () => React.JSX.Element;
@@ -0,0 +1,60 @@
1
+ import React from "react";
2
+ import { ContentEntryListConfig, useModel } from "@webiny/app-headless-cms";
3
+ import { ReactComponent as LockedIcon } from "@webiny/icons/lock.svg";
4
+ import { useRecordLocking } from "../hooks";
5
+ import { UseContentEntriesListHookDecorator } from "./decorators/UseContentEntriesListHookDecorator";
6
+ import { UseSaveEntryDecorator } from "./decorators/UseSaveEntryDecorator";
7
+ import { UseRecordsDecorator } from "./decorators/UseRecordsDecorator";
8
+ import { Icon, Tooltip } from "@webiny/admin-ui";
9
+ const {
10
+ Browser
11
+ } = ContentEntryListConfig;
12
+ const ActionsCell = ({
13
+ children
14
+ }) => {
15
+ const {
16
+ model
17
+ } = useModel();
18
+ const {
19
+ getLockRecordEntry,
20
+ isRecordLocked
21
+ } = useRecordLocking();
22
+ const {
23
+ useTableRow,
24
+ isFolderRow
25
+ } = Browser.Table.Column;
26
+ const {
27
+ row
28
+ } = useTableRow();
29
+ if (isFolderRow(row)) {
30
+ return /*#__PURE__*/React.createElement(React.Fragment, null, children);
31
+ }
32
+ const entry = getLockRecordEntry(row.id);
33
+ if (!isRecordLocked(entry) || !entry?.$locked) {
34
+ return /*#__PURE__*/React.createElement(React.Fragment, null, children);
35
+ }
36
+ return /*#__PURE__*/React.createElement(Tooltip, {
37
+ side: "left",
38
+ content: `This ${model.name} is currently locked by ${entry.$locked.lockedBy.displayName}.`,
39
+ trigger: /*#__PURE__*/React.createElement(Icon, {
40
+ icon: /*#__PURE__*/React.createElement(LockedIcon, null),
41
+ label: "Locked entry",
42
+ color: "neutral-light"
43
+ })
44
+ });
45
+ };
46
+ const RecordLockingCellActions = Browser.Table.Column.createDecorator(Original => {
47
+ return function RecordLockingCellActions(props) {
48
+ if (props.name === "actions" && props.cell) {
49
+ return /*#__PURE__*/React.createElement(Original, Object.assign({}, props, {
50
+ cell: /*#__PURE__*/React.createElement(ActionsCell, null, props.cell)
51
+ }));
52
+ }
53
+ return /*#__PURE__*/React.createElement(Original, props);
54
+ };
55
+ });
56
+ export const HeadlessCmsActionsAcoCell = () => {
57
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(UseContentEntriesListHookDecorator, null), /*#__PURE__*/React.createElement(UseSaveEntryDecorator, null), /*#__PURE__*/React.createElement(RecordLockingCellActions, null), /*#__PURE__*/React.createElement(UseRecordsDecorator, null));
58
+ };
59
+
60
+ //# sourceMappingURL=HeadlessCmsActionsAcoCell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["React","ContentEntryListConfig","useModel","ReactComponent","LockedIcon","useRecordLocking","UseContentEntriesListHookDecorator","UseSaveEntryDecorator","UseRecordsDecorator","Icon","Tooltip","Browser","ActionsCell","children","model","getLockRecordEntry","isRecordLocked","useTableRow","isFolderRow","Table","Column","row","createElement","Fragment","entry","id","$locked","side","content","name","lockedBy","displayName","trigger","icon","label","color","RecordLockingCellActions","createDecorator","Original","props","cell","Object","assign","HeadlessCmsActionsAcoCell"],"sources":["HeadlessCmsActionsAcoCell.tsx"],"sourcesContent":["import React from \"react\";\nimport { ContentEntryListConfig, useModel } from \"@webiny/app-headless-cms\";\nimport { ReactComponent as LockedIcon } from \"@webiny/icons/lock.svg\";\nimport { useRecordLocking } from \"~/hooks\";\nimport { UseContentEntriesListHookDecorator } from \"./decorators/UseContentEntriesListHookDecorator\";\nimport { UseSaveEntryDecorator } from \"~/components/decorators/UseSaveEntryDecorator\";\nimport { UseRecordsDecorator } from \"./decorators/UseRecordsDecorator\";\nimport { Icon, Tooltip } from \"@webiny/admin-ui\";\n\nconst { Browser } = ContentEntryListConfig;\n\ninterface ActionsCellProps {\n children: React.ReactNode;\n}\n\nconst ActionsCell = ({ children }: ActionsCellProps) => {\n const { model } = useModel();\n const { getLockRecordEntry, isRecordLocked } = useRecordLocking();\n\n const { useTableRow, isFolderRow } = Browser.Table.Column;\n const { row } = useTableRow();\n\n if (isFolderRow(row)) {\n return <>{children}</>;\n }\n\n const entry = getLockRecordEntry(row.id);\n\n if (!isRecordLocked(entry) || !entry?.$locked) {\n return <>{children}</>;\n }\n return (\n <Tooltip\n side={\"left\"}\n content={`This ${model.name} is currently locked by ${entry.$locked.lockedBy.displayName}.`}\n trigger={<Icon icon={<LockedIcon />} label={\"Locked entry\"} color={\"neutral-light\"} />}\n />\n );\n};\n\nconst RecordLockingCellActions = Browser.Table.Column.createDecorator(Original => {\n return function RecordLockingCellActions(props) {\n if (props.name === \"actions\" && props.cell) {\n return <Original {...props} cell={<ActionsCell>{props.cell}</ActionsCell>} />;\n }\n\n return <Original {...props} />;\n };\n});\n\nexport const HeadlessCmsActionsAcoCell = () => {\n return (\n <>\n <UseContentEntriesListHookDecorator />\n <UseSaveEntryDecorator />\n <RecordLockingCellActions />\n <UseRecordsDecorator />\n </>\n );\n};\n"],"mappings":"AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,SAASC,sBAAsB,EAAEC,QAAQ,QAAQ,0BAA0B;AAC3E,SAASC,cAAc,IAAIC,UAAU,QAAQ,wBAAwB;AACrE,SAASC,gBAAgB;AACzB,SAASC,kCAAkC;AAC3C,SAASC,qBAAqB;AAC9B,SAASC,mBAAmB;AAC5B,SAASC,IAAI,EAAEC,OAAO,QAAQ,kBAAkB;AAEhD,MAAM;EAAEC;AAAQ,CAAC,GAAGV,sBAAsB;AAM1C,MAAMW,WAAW,GAAGA,CAAC;EAAEC;AAA2B,CAAC,KAAK;EACpD,MAAM;IAAEC;EAAM,CAAC,GAAGZ,QAAQ,CAAC,CAAC;EAC5B,MAAM;IAAEa,kBAAkB;IAAEC;EAAe,CAAC,GAAGX,gBAAgB,CAAC,CAAC;EAEjE,MAAM;IAAEY,WAAW;IAAEC;EAAY,CAAC,GAAGP,OAAO,CAACQ,KAAK,CAACC,MAAM;EACzD,MAAM;IAAEC;EAAI,CAAC,GAAGJ,WAAW,CAAC,CAAC;EAE7B,IAAIC,WAAW,CAACG,GAAG,CAAC,EAAE;IAClB,oBAAOrB,KAAA,CAAAsB,aAAA,CAAAtB,KAAA,CAAAuB,QAAA,QAAGV,QAAW,CAAC;EAC1B;EAEA,MAAMW,KAAK,GAAGT,kBAAkB,CAACM,GAAG,CAACI,EAAE,CAAC;EAExC,IAAI,CAACT,cAAc,CAACQ,KAAK,CAAC,IAAI,CAACA,KAAK,EAAEE,OAAO,EAAE;IAC3C,oBAAO1B,KAAA,CAAAsB,aAAA,CAAAtB,KAAA,CAAAuB,QAAA,QAAGV,QAAW,CAAC;EAC1B;EACA,oBACIb,KAAA,CAAAsB,aAAA,CAACZ,OAAO;IACJiB,IAAI,EAAE,MAAO;IACbC,OAAO,EAAE,QAAQd,KAAK,CAACe,IAAI,2BAA2BL,KAAK,CAACE,OAAO,CAACI,QAAQ,CAACC,WAAW,GAAI;IAC5FC,OAAO,eAAEhC,KAAA,CAAAsB,aAAA,CAACb,IAAI;MAACwB,IAAI,eAAEjC,KAAA,CAAAsB,aAAA,CAAClB,UAAU,MAAE,CAAE;MAAC8B,KAAK,EAAE,cAAe;MAACC,KAAK,EAAE;IAAgB,CAAE;EAAE,CAC1F,CAAC;AAEV,CAAC;AAED,MAAMC,wBAAwB,GAAGzB,OAAO,CAACQ,KAAK,CAACC,MAAM,CAACiB,eAAe,CAACC,QAAQ,IAAI;EAC9E,OAAO,SAASF,wBAAwBA,CAACG,KAAK,EAAE;IAC5C,IAAIA,KAAK,CAACV,IAAI,KAAK,SAAS,IAAIU,KAAK,CAACC,IAAI,EAAE;MACxC,oBAAOxC,KAAA,CAAAsB,aAAA,CAACgB,QAAQ,EAAAG,MAAA,CAAAC,MAAA,KAAKH,KAAK;QAAEC,IAAI,eAAExC,KAAA,CAAAsB,aAAA,CAACV,WAAW,QAAE2B,KAAK,CAACC,IAAkB;MAAE,EAAE,CAAC;IACjF;IAEA,oBAAOxC,KAAA,CAAAsB,aAAA,CAACgB,QAAQ,EAAKC,KAAQ,CAAC;EAClC,CAAC;AACL,CAAC,CAAC;AAEF,OAAO,MAAMI,yBAAyB,GAAGA,CAAA,KAAM;EAC3C,oBACI3C,KAAA,CAAAsB,aAAA,CAAAtB,KAAA,CAAAuB,QAAA,qBACIvB,KAAA,CAAAsB,aAAA,CAAChB,kCAAkC,MAAE,CAAC,eACtCN,KAAA,CAAAsB,aAAA,CAACf,qBAAqB,MAAE,CAAC,eACzBP,KAAA,CAAAsB,aAAA,CAACc,wBAAwB,MAAE,CAAC,eAC5BpC,KAAA,CAAAsB,aAAA,CAACd,mBAAmB,MAAE,CACxB,CAAC;AAEX,CAAC","ignoreList":[]}
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ import type { CmsContentEntry, CmsModel } from "@webiny/app-headless-cms/types";
3
+ export interface IContentEntryGuardProps {
4
+ loading: boolean;
5
+ entry: CmsContentEntry;
6
+ model: CmsModel;
7
+ children: React.ReactElement;
8
+ }
9
+ export declare const ContentEntryGuard: (props: IContentEntryGuardProps) => React.JSX.Element;
@@ -0,0 +1,40 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { useRecordLocking } from "../../hooks";
3
+ import { LockedRecord } from "../LockedRecord";
4
+ import { OverlayLoader } from "@webiny/admin-ui";
5
+ export const ContentEntryGuard = props => {
6
+ const {
7
+ loading,
8
+ entry,
9
+ model,
10
+ children
11
+ } = props;
12
+ const {
13
+ fetchLockedEntryLockRecord
14
+ } = useRecordLocking();
15
+ const [locked, setLocked] = useState(undefined);
16
+ useEffect(() => {
17
+ if (!entry.id || loading || locked !== undefined) {
18
+ return;
19
+ }
20
+ (async () => {
21
+ const result = await fetchLockedEntryLockRecord({
22
+ id: entry.id,
23
+ $lockingType: model.modelId
24
+ });
25
+ setLocked(result);
26
+ })();
27
+ }, [entry.id, loading]);
28
+ if (locked === undefined) {
29
+ return /*#__PURE__*/React.createElement("div", {
30
+ className: "wby-h-screen wby-w-screen wby-fixed wby-top-0 wby-left-0 wby-z-20"
31
+ }, /*#__PURE__*/React.createElement(OverlayLoader, null));
32
+ } else if (locked) {
33
+ return /*#__PURE__*/React.createElement(LockedRecord, {
34
+ record: locked
35
+ });
36
+ }
37
+ return children;
38
+ };
39
+
40
+ //# sourceMappingURL=ContentEntryGuard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["React","useEffect","useState","useRecordLocking","LockedRecord","OverlayLoader","ContentEntryGuard","props","loading","entry","model","children","fetchLockedEntryLockRecord","locked","setLocked","undefined","id","result","$lockingType","modelId","createElement","className","record"],"sources":["ContentEntryGuard.tsx"],"sourcesContent":["import React, { useEffect, useState } from \"react\";\nimport { useRecordLocking } from \"~/hooks\";\nimport { LockedRecord } from \"../LockedRecord\";\nimport type { IRecordLockingLockRecord } from \"~/types\";\nimport type { CmsContentEntry, CmsModel } from \"@webiny/app-headless-cms/types\";\nimport { OverlayLoader } from \"@webiny/admin-ui\";\n\nexport interface IContentEntryGuardProps {\n loading: boolean;\n entry: CmsContentEntry;\n model: CmsModel;\n children: React.ReactElement;\n}\n\nexport const ContentEntryGuard = (props: IContentEntryGuardProps) => {\n const { loading, entry, model, children } = props;\n const { fetchLockedEntryLockRecord } = useRecordLocking();\n\n const [locked, setLocked] = useState<IRecordLockingLockRecord | null | undefined>(undefined);\n\n useEffect(() => {\n if (!entry.id || loading || locked !== undefined) {\n return;\n }\n (async () => {\n const result = await fetchLockedEntryLockRecord({\n id: entry.id,\n $lockingType: model.modelId\n });\n setLocked(result);\n })();\n }, [entry.id, loading]);\n\n if (locked === undefined) {\n return (\n <div className={\"wby-h-screen wby-w-screen wby-fixed wby-top-0 wby-left-0 wby-z-20\"}>\n <OverlayLoader />\n </div>\n );\n } else if (locked) {\n return <LockedRecord record={locked} />;\n }\n\n return children;\n};\n"],"mappings":"AAAA,OAAOA,KAAK,IAAIC,SAAS,EAAEC,QAAQ,QAAQ,OAAO;AAClD,SAASC,gBAAgB;AACzB,SAASC,YAAY;AAGrB,SAASC,aAAa,QAAQ,kBAAkB;AAShD,OAAO,MAAMC,iBAAiB,GAAIC,KAA8B,IAAK;EACjE,MAAM;IAAEC,OAAO;IAAEC,KAAK;IAAEC,KAAK;IAAEC;EAAS,CAAC,GAAGJ,KAAK;EACjD,MAAM;IAAEK;EAA2B,CAAC,GAAGT,gBAAgB,CAAC,CAAC;EAEzD,MAAM,CAACU,MAAM,EAAEC,SAAS,CAAC,GAAGZ,QAAQ,CAA8Ca,SAAS,CAAC;EAE5Fd,SAAS,CAAC,MAAM;IACZ,IAAI,CAACQ,KAAK,CAACO,EAAE,IAAIR,OAAO,IAAIK,MAAM,KAAKE,SAAS,EAAE;MAC9C;IACJ;IACA,CAAC,YAAY;MACT,MAAME,MAAM,GAAG,MAAML,0BAA0B,CAAC;QAC5CI,EAAE,EAAEP,KAAK,CAACO,EAAE;QACZE,YAAY,EAAER,KAAK,CAACS;MACxB,CAAC,CAAC;MACFL,SAAS,CAACG,MAAM,CAAC;IACrB,CAAC,EAAE,CAAC;EACR,CAAC,EAAE,CAACR,KAAK,CAACO,EAAE,EAAER,OAAO,CAAC,CAAC;EAEvB,IAAIK,MAAM,KAAKE,SAAS,EAAE;IACtB,oBACIf,KAAA,CAAAoB,aAAA;MAAKC,SAAS,EAAE;IAAoE,gBAChFrB,KAAA,CAAAoB,aAAA,CAACf,aAAa,MAAE,CACf,CAAC;EAEd,CAAC,MAAM,IAAIQ,MAAM,EAAE;IACf,oBAAOb,KAAA,CAAAoB,aAAA,CAAChB,YAAY;MAACkB,MAAM,EAAET;IAAO,CAAE,CAAC;EAC3C;EAEA,OAAOF,QAAQ;AACnB,CAAC","ignoreList":[]}
@@ -0,0 +1,18 @@
1
+ import React from "react";
2
+ import type { IRecordLockingIdentity, IRecordLockingLockRecord } from "../../types";
3
+ import type { IncomingGenericData } from "@webiny/app-websockets";
4
+ import type { CmsContentEntry, CmsModel } from "@webiny/app-headless-cms/types";
5
+ export interface IContentEntryLockerProps {
6
+ entry: CmsContentEntry;
7
+ model: CmsModel;
8
+ onEntryUnlocked: () => void;
9
+ onDisablePrompt: (flag: boolean) => void;
10
+ children: React.ReactElement;
11
+ }
12
+ export interface IKickOutWebsocketsMessage extends IncomingGenericData {
13
+ data: {
14
+ record: IRecordLockingLockRecord;
15
+ user: IRecordLockingIdentity;
16
+ };
17
+ }
18
+ export declare const ContentEntryLocker: ({ onEntryUnlocked, onDisablePrompt, entry, model, children }: IContentEntryLockerProps) => React.JSX.Element;
@@ -0,0 +1,106 @@
1
+ import React, { useEffect, useRef } from "react";
2
+ import { useRecordLocking } from "../../hooks";
3
+ import { useWebsockets } from "@webiny/app-websockets";
4
+ import { parseIdentifier } from "@webiny/utils";
5
+ import { useDialogs } from "@webiny/app-admin";
6
+ const autoUpdateTimeout = 20;
7
+ const ForceUnlocked = ({
8
+ user
9
+ }) => {
10
+ return /*#__PURE__*/React.createElement(React.Fragment, null, "The entry you were editing was forcefully unlocked by", " ", /*#__PURE__*/React.createElement("strong", null, user.displayName || "Unknown user"), ". Unfortunately, this means you lost the unsaved changes.");
11
+ };
12
+ export const ContentEntryLocker = ({
13
+ onEntryUnlocked,
14
+ onDisablePrompt,
15
+ entry,
16
+ model,
17
+ children
18
+ }) => {
19
+ const {
20
+ updateEntryLock,
21
+ removeEntryLock
22
+ } = useRecordLocking();
23
+ const websockets = useWebsockets();
24
+ const {
25
+ showDialog
26
+ } = useDialogs();
27
+ const entryLockerTimeout = useRef(null);
28
+ useEffect(() => {
29
+ if (!entry.id) {
30
+ return;
31
+ }
32
+ const {
33
+ id: entryId
34
+ } = parseIdentifier(entry.id);
35
+ let onMessageSub = websockets.onMessage(`recordLocking.entry.kickOut.${entryId}`, async incoming => {
36
+ const {
37
+ user
38
+ } = incoming.data;
39
+ onDisablePrompt(true);
40
+ removeEntryLock({
41
+ id: entryId,
42
+ $lockingType: model.modelId
43
+ });
44
+ showDialog({
45
+ title: "Entry was forcefully unlocked!",
46
+ content: /*#__PURE__*/React.createElement(ForceUnlocked, {
47
+ user: user
48
+ }),
49
+ acceptLabel: "Ok",
50
+ onClose: undefined,
51
+ cancelLabel: undefined
52
+ });
53
+ onEntryUnlocked();
54
+ });
55
+ return () => {
56
+ onMessageSub.off();
57
+ /**
58
+ * Lets null subscriptions, just in case it...
59
+ */
60
+ // @ts-expect-error
61
+ onMessageSub = null;
62
+ };
63
+ }, [entry.id, onEntryUnlocked, model.modelId]);
64
+ useEffect(() => {
65
+ if (!entry.id) {
66
+ return;
67
+ }
68
+ if (entryLockerTimeout.current) {
69
+ return;
70
+ }
71
+ const updateLock = async () => {
72
+ const result = await updateEntryLock({
73
+ id: entry.id,
74
+ $lockingType: model.modelId
75
+ });
76
+ if (result.error) {
77
+ showDialog({
78
+ title: "There was an error while updating the entry lock.",
79
+ content: result.error.message,
80
+ acceptLabel: "Ok",
81
+ onClose: undefined,
82
+ cancelLabel: undefined
83
+ });
84
+ onEntryUnlocked();
85
+ return;
86
+ }
87
+ createTimeout();
88
+ };
89
+ const createTimeout = () => {
90
+ entryLockerTimeout.current = window.setTimeout(() => {
91
+ updateLock();
92
+ }, autoUpdateTimeout * 1000);
93
+ };
94
+ updateLock();
95
+ return () => {
96
+ if (!entryLockerTimeout.current) {
97
+ return;
98
+ }
99
+ clearTimeout(entryLockerTimeout.current);
100
+ entryLockerTimeout.current = null;
101
+ };
102
+ }, [entry.id, onEntryUnlocked]);
103
+ return /*#__PURE__*/React.createElement(React.Fragment, null, children);
104
+ };
105
+
106
+ //# sourceMappingURL=ContentEntryLocker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["React","useEffect","useRef","useRecordLocking","useWebsockets","parseIdentifier","useDialogs","autoUpdateTimeout","ForceUnlocked","user","createElement","Fragment","displayName","ContentEntryLocker","onEntryUnlocked","onDisablePrompt","entry","model","children","updateEntryLock","removeEntryLock","websockets","showDialog","entryLockerTimeout","id","entryId","onMessageSub","onMessage","incoming","data","$lockingType","modelId","title","content","acceptLabel","onClose","undefined","cancelLabel","off","current","updateLock","result","error","message","createTimeout","window","setTimeout","clearTimeout"],"sources":["ContentEntryLocker.tsx"],"sourcesContent":["import React, { useEffect, useRef } from \"react\";\nimport { useRecordLocking } from \"~/hooks\";\nimport type { IRecordLockingIdentity, IRecordLockingLockRecord } from \"~/types\";\nimport type { IncomingGenericData } from \"@webiny/app-websockets\";\nimport { useWebsockets } from \"@webiny/app-websockets\";\nimport { parseIdentifier } from \"@webiny/utils\";\nimport { useDialogs } from \"@webiny/app-admin\";\nimport type { CmsContentEntry, CmsModel } from \"@webiny/app-headless-cms/types\";\n\nconst autoUpdateTimeout = 20;\n\nexport interface IContentEntryLockerProps {\n entry: CmsContentEntry;\n model: CmsModel;\n onEntryUnlocked: () => void;\n onDisablePrompt: (flag: boolean) => void;\n children: React.ReactElement;\n}\n\nexport interface IKickOutWebsocketsMessage extends IncomingGenericData {\n data: {\n record: IRecordLockingLockRecord;\n user: IRecordLockingIdentity;\n };\n}\ninterface IForceUnlockedProps {\n user: IRecordLockingIdentity;\n}\nconst ForceUnlocked = ({ user }: IForceUnlockedProps) => {\n return (\n <>\n The entry you were editing was forcefully unlocked by{\" \"}\n <strong>{user.displayName || \"Unknown user\"}</strong>. Unfortunately, this means you\n lost the unsaved changes.\n </>\n );\n};\n\nexport const ContentEntryLocker = ({\n onEntryUnlocked,\n onDisablePrompt,\n entry,\n model,\n children\n}: IContentEntryLockerProps) => {\n const { updateEntryLock, removeEntryLock } = useRecordLocking();\n const websockets = useWebsockets();\n const { showDialog } = useDialogs();\n\n const entryLockerTimeout = useRef<number | null>(null);\n\n useEffect(() => {\n if (!entry.id) {\n return;\n }\n const { id: entryId } = parseIdentifier(entry.id);\n\n let onMessageSub = websockets.onMessage<IKickOutWebsocketsMessage>(\n `recordLocking.entry.kickOut.${entryId}`,\n async incoming => {\n const { user } = incoming.data;\n onDisablePrompt(true);\n removeEntryLock({\n id: entryId,\n $lockingType: model.modelId\n });\n showDialog({\n title: \"Entry was forcefully unlocked!\",\n content: <ForceUnlocked user={user} />,\n acceptLabel: \"Ok\",\n onClose: undefined,\n cancelLabel: undefined\n });\n onEntryUnlocked();\n }\n );\n\n return () => {\n onMessageSub.off();\n /**\n * Lets null subscriptions, just in case it...\n */\n // @ts-expect-error\n onMessageSub = null;\n };\n }, [entry.id, onEntryUnlocked, model.modelId]);\n\n useEffect(() => {\n if (!entry.id) {\n return;\n }\n\n if (entryLockerTimeout.current) {\n return;\n }\n\n const updateLock = async () => {\n const result = await updateEntryLock({\n id: entry.id,\n $lockingType: model.modelId\n });\n if (result.error) {\n showDialog({\n title: \"There was an error while updating the entry lock.\",\n content: result.error.message,\n acceptLabel: \"Ok\",\n onClose: undefined,\n cancelLabel: undefined\n });\n onEntryUnlocked();\n return;\n }\n createTimeout();\n };\n\n const createTimeout = () => {\n entryLockerTimeout.current = window.setTimeout(() => {\n updateLock();\n }, autoUpdateTimeout * 1000);\n };\n\n updateLock();\n return () => {\n if (!entryLockerTimeout.current) {\n return;\n }\n clearTimeout(entryLockerTimeout.current);\n entryLockerTimeout.current = null;\n };\n }, [entry.id, onEntryUnlocked]);\n\n return <>{children}</>;\n};\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,2 @@
1
+ import React from "react";
2
+ export declare const HeadlessCmsContentEntry: () => React.JSX.Element;
@@ -0,0 +1,90 @@
1
+ import React, { useState } from "react";
2
+ import { CompositionScope, createGenericContext } from "@webiny/app-admin";
3
+ import { ContentEntryEditorConfig, ContentEntryListConfig } from "@webiny/app-headless-cms";
4
+ import { Prompt } from "@webiny/react-router";
5
+ import { ContentEntryGuard } from "./ContentEntryGuard";
6
+ import { ContentEntryLocker } from "./ContentEntryLocker";
7
+ const {
8
+ ContentEntry,
9
+ SingletonContentEntry
10
+ } = ContentEntryEditorConfig;
11
+ const DisablePrompt = createGenericContext("DisablePrompt");
12
+ const PromptDecorator = Prompt.createDecorator(Original => {
13
+ return function Prompt(props) {
14
+ const {
15
+ disablePrompt
16
+ } = DisablePrompt.useHook();
17
+ const when = disablePrompt === true ? false : props.when;
18
+ return /*#__PURE__*/React.createElement(Original, {
19
+ message: props.message,
20
+ when: when
21
+ });
22
+ };
23
+ });
24
+ const ContentEntryDecorator = ContentEntry.createDecorator(Original => {
25
+ return function RecordLockingContentEntry() {
26
+ const [disablePrompt, setDisablePrompt] = useState(false);
27
+ const {
28
+ entry,
29
+ contentModel,
30
+ loading
31
+ } = ContentEntry.useContentEntry();
32
+ const {
33
+ navigateTo
34
+ } = ContentEntryListConfig.ContentEntries.useContentEntriesList();
35
+ /**
36
+ * New entry does not have ID yet.
37
+ */
38
+ if (!entry?.id) {
39
+ return /*#__PURE__*/React.createElement(DisablePrompt.Provider, {
40
+ disablePrompt: disablePrompt
41
+ }, /*#__PURE__*/React.createElement(Original, null));
42
+ }
43
+ /**
44
+ * Continue with existing entry.
45
+ */
46
+ const props = {
47
+ entry,
48
+ model: contentModel
49
+ };
50
+ return /*#__PURE__*/React.createElement(ContentEntryGuard, Object.assign({}, props, {
51
+ loading: loading
52
+ }), /*#__PURE__*/React.createElement(ContentEntryLocker, Object.assign({}, props, {
53
+ onDisablePrompt: flag => setDisablePrompt(flag),
54
+ onEntryUnlocked: navigateTo
55
+ }), /*#__PURE__*/React.createElement(DisablePrompt.Provider, {
56
+ disablePrompt: disablePrompt
57
+ }, /*#__PURE__*/React.createElement(Original, null))));
58
+ };
59
+ });
60
+ const SingletonContentEntryDecorator = SingletonContentEntry.createDecorator(Original => {
61
+ return function RecordLockingSingletonContentEntry() {
62
+ const [disablePrompt, setDisablePrompt] = useState(false);
63
+ const {
64
+ entry,
65
+ contentModel,
66
+ loading
67
+ } = SingletonContentEntry.useSingletonContentEntry();
68
+ const props = {
69
+ entry,
70
+ model: contentModel
71
+ };
72
+ return /*#__PURE__*/React.createElement(ContentEntryGuard, Object.assign({}, props, {
73
+ loading: loading
74
+ }), /*#__PURE__*/React.createElement(ContentEntryLocker, Object.assign({}, props, {
75
+ onDisablePrompt: flag => setDisablePrompt(flag),
76
+ onEntryUnlocked: () => {
77
+ // There's nowhere to go, since singleton entry doesn't have a list view.
78
+ }
79
+ }), /*#__PURE__*/React.createElement(DisablePrompt.Provider, {
80
+ disablePrompt: disablePrompt
81
+ }, /*#__PURE__*/React.createElement(Original, null))));
82
+ };
83
+ });
84
+ export const HeadlessCmsContentEntry = () => {
85
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ContentEntryDecorator, null), /*#__PURE__*/React.createElement(SingletonContentEntryDecorator, null), /*#__PURE__*/React.createElement(CompositionScope, {
86
+ name: "cms.contentEntryForm"
87
+ }, /*#__PURE__*/React.createElement(PromptDecorator, null)));
88
+ };
89
+
90
+ //# sourceMappingURL=HeadlessCmsContentEntry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["React","useState","CompositionScope","createGenericContext","ContentEntryEditorConfig","ContentEntryListConfig","Prompt","ContentEntryGuard","ContentEntryLocker","ContentEntry","SingletonContentEntry","DisablePrompt","PromptDecorator","createDecorator","Original","props","disablePrompt","useHook","when","createElement","message","ContentEntryDecorator","RecordLockingContentEntry","setDisablePrompt","entry","contentModel","loading","useContentEntry","navigateTo","ContentEntries","useContentEntriesList","id","Provider","model","Object","assign","onDisablePrompt","flag","onEntryUnlocked","SingletonContentEntryDecorator","RecordLockingSingletonContentEntry","useSingletonContentEntry","HeadlessCmsContentEntry","Fragment","name"],"sources":["HeadlessCmsContentEntry.tsx"],"sourcesContent":["import React, { useState } from \"react\";\nimport { CompositionScope, createGenericContext } from \"@webiny/app-admin\";\nimport { ContentEntryEditorConfig, ContentEntryListConfig } from \"@webiny/app-headless-cms\";\nimport { Prompt } from \"@webiny/react-router\";\nimport { ContentEntryGuard } from \"./ContentEntryGuard\";\nimport { ContentEntryLocker } from \"./ContentEntryLocker\";\n\nconst { ContentEntry, SingletonContentEntry } = ContentEntryEditorConfig;\n\nconst DisablePrompt = createGenericContext<{ disablePrompt: boolean }>(\"DisablePrompt\");\n\nconst PromptDecorator = Prompt.createDecorator(Original => {\n return function Prompt(props) {\n const { disablePrompt } = DisablePrompt.useHook();\n const when = disablePrompt === true ? false : props.when;\n return <Original message={props.message} when={when} />;\n };\n});\n\nconst ContentEntryDecorator = ContentEntry.createDecorator(Original => {\n return function RecordLockingContentEntry() {\n const [disablePrompt, setDisablePrompt] = useState(false);\n const { entry, contentModel, loading } = ContentEntry.useContentEntry();\n const { navigateTo } = ContentEntryListConfig.ContentEntries.useContentEntriesList();\n /**\n * New entry does not have ID yet.\n */\n if (!entry?.id) {\n return (\n <DisablePrompt.Provider disablePrompt={disablePrompt}>\n <Original />\n </DisablePrompt.Provider>\n );\n }\n /**\n * Continue with existing entry.\n */\n const props = { entry, model: contentModel };\n\n return (\n <ContentEntryGuard {...props} loading={loading}>\n <ContentEntryLocker\n {...props}\n onDisablePrompt={flag => setDisablePrompt(flag)}\n onEntryUnlocked={navigateTo}\n >\n <DisablePrompt.Provider disablePrompt={disablePrompt}>\n <Original />\n </DisablePrompt.Provider>\n </ContentEntryLocker>\n </ContentEntryGuard>\n );\n };\n});\n\nconst SingletonContentEntryDecorator = SingletonContentEntry.createDecorator(Original => {\n return function RecordLockingSingletonContentEntry() {\n const [disablePrompt, setDisablePrompt] = useState(false);\n const { entry, contentModel, loading } = SingletonContentEntry.useSingletonContentEntry();\n\n const props = { entry, model: contentModel };\n\n return (\n <ContentEntryGuard {...props} loading={loading}>\n <ContentEntryLocker\n {...props}\n onDisablePrompt={flag => setDisablePrompt(flag)}\n onEntryUnlocked={() => {\n // There's nowhere to go, since singleton entry doesn't have a list view.\n }}\n >\n <DisablePrompt.Provider disablePrompt={disablePrompt}>\n <Original />\n </DisablePrompt.Provider>\n </ContentEntryLocker>\n </ContentEntryGuard>\n );\n };\n});\n\nexport const HeadlessCmsContentEntry = () => {\n return (\n <>\n <ContentEntryDecorator />\n <SingletonContentEntryDecorator />\n <CompositionScope name={\"cms.contentEntryForm\"}>\n <PromptDecorator />\n </CompositionScope>\n </>\n );\n};\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1 @@
1
+ export * from "./HeadlessCmsContentEntry";
@@ -0,0 +1,3 @@
1
+ export * from "./HeadlessCmsContentEntry";
2
+
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["index.ts"],"sourcesContent":["export * from \"./HeadlessCmsContentEntry\";\n"],"mappings":"AAAA","ignoreList":[]}
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ import type { IRecordLockingLockRecord } from "../../types";
3
+ export interface ILockedRecordProps {
4
+ record: IRecordLockingLockRecord;
5
+ }
6
+ export declare const LockedRecord: ({ record: lockRecordEntry }: ILockedRecordProps) => React.JSX.Element;
@@ -0,0 +1,66 @@
1
+ import React from "react";
2
+ import { ReactComponent as LockIcon } from "@webiny/icons/lock.svg";
3
+ import { Grid, Heading, Icon, Text } from "@webiny/admin-ui";
4
+ import { useRecordLocking } from "../../hooks";
5
+ import { useContentEntry } from "@webiny/app-headless-cms";
6
+ import { LockedRecordForceUnlock } from "./LockedRecordForceUnlock";
7
+ const Wrapper = ({
8
+ children
9
+ }) => {
10
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
11
+ className: "wby-w-5/12 wby-absolute wby-top-1/2 wby-left-1/2 wby--translate-x-1/2 wby--translate-y-1/2 wby-z-50"
12
+ }, /*#__PURE__*/React.createElement("div", {
13
+ className: "wby-flex wby-p-lg wby-border-sm wby-border-neutral-dimmed-darker wby-rounded-3xl wby-bg-neutral-base"
14
+ }, /*#__PURE__*/React.createElement(Grid, null, /*#__PURE__*/React.createElement(Grid.Column, {
15
+ span: 3
16
+ }, /*#__PURE__*/React.createElement("div", {
17
+ className: "wby-h-full wby-flex wby-items-center wby-justify-center wby-bg-neutral-dimmed wby-rounded-md wby-p-lg"
18
+ }, /*#__PURE__*/React.createElement(Icon, {
19
+ style: {
20
+ width: "64px",
21
+ height: "64px"
22
+ },
23
+ icon: /*#__PURE__*/React.createElement(LockIcon, null),
24
+ label: "Locked Record",
25
+ color: "accent",
26
+ size: "lg"
27
+ }))), /*#__PURE__*/React.createElement(Grid.Column, {
28
+ span: 9
29
+ }, /*#__PURE__*/React.createElement("div", {
30
+ className: "wby-flex wby-flex-col wby-justify-center"
31
+ }, children))))));
32
+ };
33
+ const Title = () => {
34
+ const {
35
+ entry
36
+ } = useContentEntry();
37
+ return /*#__PURE__*/React.createElement(Heading, {
38
+ level: 4,
39
+ className: "wby-mb-sm"
40
+ }, "Record (", entry.meta.title, ") is locked!");
41
+ };
42
+ export const LockedRecord = ({
43
+ record: lockRecordEntry
44
+ }) => {
45
+ const {
46
+ getLockRecordEntry
47
+ } = useRecordLocking();
48
+ const record = getLockRecordEntry(lockRecordEntry.id);
49
+ if (!record) {
50
+ return /*#__PURE__*/React.createElement(Wrapper, null, /*#__PURE__*/React.createElement(Text, null, "Could not find the lock record. Please refresh the Admin UI."));
51
+ } else if (!lockRecordEntry?.lockedBy) {
52
+ return /*#__PURE__*/React.createElement(Wrapper, null, /*#__PURE__*/React.createElement(Title, null), /*#__PURE__*/React.createElement(Text, null, "This record is locked, but the system cannot find the user that created the record lock. A force-unlock is required to regain editing capabilities for this record."), /*#__PURE__*/React.createElement(LockedRecordForceUnlock, {
53
+ id: lockRecordEntry.id,
54
+ type: record.$lockingType,
55
+ title: record.meta.title
56
+ }));
57
+ }
58
+ return /*#__PURE__*/React.createElement(Wrapper, null, /*#__PURE__*/React.createElement(Title, null), /*#__PURE__*/React.createElement(Text, null, "It is locked because ", /*#__PURE__*/React.createElement("strong", null, lockRecordEntry.lockedBy.displayName), " is currently editing this record. You can either contact the user and ask them to unlock the record, or you can wait for the lock to expire."), /*#__PURE__*/React.createElement(LockedRecordForceUnlock, {
59
+ id: lockRecordEntry.id,
60
+ type: record.$lockingType,
61
+ lockedBy: lockRecordEntry.lockedBy,
62
+ title: record.meta.title
63
+ }));
64
+ };
65
+
66
+ //# sourceMappingURL=LockedRecord.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["React","ReactComponent","LockIcon","Grid","Heading","Icon","Text","useRecordLocking","useContentEntry","LockedRecordForceUnlock","Wrapper","children","createElement","Fragment","className","Column","span","style","width","height","icon","label","color","size","Title","entry","level","meta","title","LockedRecord","record","lockRecordEntry","getLockRecordEntry","id","lockedBy","type","$lockingType","displayName"],"sources":["LockedRecord.tsx"],"sourcesContent":["import React from \"react\";\nimport { ReactComponent as LockIcon } from \"@webiny/icons/lock.svg\";\nimport { Grid, Heading, Icon, Text } from \"@webiny/admin-ui\";\nimport { useRecordLocking } from \"~/hooks\";\nimport { useContentEntry } from \"@webiny/app-headless-cms\";\nimport { LockedRecordForceUnlock } from \"./LockedRecordForceUnlock\";\nimport type { IRecordLockingLockRecord } from \"~/types\";\n\ninterface IWrapperProps {\n children: React.ReactNode;\n}\n\nconst Wrapper = ({ children }: IWrapperProps) => {\n return (\n <>\n <div className=\"wby-w-5/12 wby-absolute wby-top-1/2 wby-left-1/2 wby--translate-x-1/2 wby--translate-y-1/2 wby-z-50\">\n <div\n className={\n \"wby-flex wby-p-lg wby-border-sm wby-border-neutral-dimmed-darker wby-rounded-3xl wby-bg-neutral-base\"\n }\n >\n <Grid>\n <Grid.Column span={3}>\n <div className=\"wby-h-full wby-flex wby-items-center wby-justify-center wby-bg-neutral-dimmed wby-rounded-md wby-p-lg\">\n <Icon\n style={{\n width: \"64px\",\n height: \"64px\"\n }}\n icon={<LockIcon />}\n label={\"Locked Record\"}\n color={\"accent\"}\n size={\"lg\"}\n />\n </div>\n </Grid.Column>\n <Grid.Column span={9}>\n <div className={\"wby-flex wby-flex-col wby-justify-center\"}>\n {children}\n </div>\n </Grid.Column>\n </Grid>\n </div>\n </div>\n {/*<div className=\"wby-absolute wby-inset-0 wby-bg-neutral-dark/50 wby-z-45\"></div>*/}\n </>\n );\n};\n\nconst Title = () => {\n const { entry } = useContentEntry();\n return (\n <Heading level={4} className={\"wby-mb-sm\"}>\n Record ({entry.meta.title}) is locked!\n </Heading>\n );\n};\n\nexport interface ILockedRecordProps {\n record: IRecordLockingLockRecord;\n}\n\nexport const LockedRecord = ({ record: lockRecordEntry }: ILockedRecordProps) => {\n const { getLockRecordEntry } = useRecordLocking();\n\n const record = getLockRecordEntry(lockRecordEntry.id);\n\n if (!record) {\n return (\n <Wrapper>\n <Text>Could not find the lock record. Please refresh the Admin UI.</Text>\n </Wrapper>\n );\n } else if (!lockRecordEntry?.lockedBy) {\n return (\n <Wrapper>\n <Title />\n <Text>\n This record is locked, but the system cannot find the user that created the\n record lock. A force-unlock is required to regain editing capabilities for this\n record.\n </Text>\n <LockedRecordForceUnlock\n id={lockRecordEntry.id}\n type={record.$lockingType}\n title={record.meta.title}\n />\n </Wrapper>\n );\n }\n return (\n <Wrapper>\n <Title />\n <Text>\n It is locked because <strong>{lockRecordEntry.lockedBy.displayName}</strong> is\n currently editing this record. You can either contact the user and ask them to\n unlock the record, or you can wait for the lock to expire.\n </Text>\n <LockedRecordForceUnlock\n id={lockRecordEntry.id}\n type={record.$lockingType}\n lockedBy={lockRecordEntry.lockedBy}\n title={record.meta.title}\n />\n </Wrapper>\n );\n};\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ import type { IRecordLockingIdentity } from "../../types";
3
+ export interface ILockedRecordForceUnlockProps {
4
+ id: string;
5
+ type: string;
6
+ title: string;
7
+ lockedBy?: IRecordLockingIdentity;
8
+ }
9
+ export declare const LockedRecordForceUnlock: (props: ILockedRecordForceUnlockProps) => React.JSX.Element | null;
@@ -0,0 +1,75 @@
1
+ import React, { useCallback, useEffect, useState } from "react";
2
+ import { Alert, Button, Text } from "@webiny/admin-ui";
3
+ import { useConfirmationDialog, useSnackbar } from "@webiny/app-admin";
4
+ import { useRecordLocking, usePermission } from "../../hooks";
5
+ import { useRouter } from "@webiny/react-router";
6
+ import { useContentEntriesList } from "@webiny/app-headless-cms";
7
+ const ErrorMessage = props => {
8
+ const {
9
+ title,
10
+ lockedBy
11
+ } = props;
12
+ return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Alert, {
13
+ type: "warning",
14
+ title: "Warning",
15
+ className: "wby-mb-md"
16
+ }, /*#__PURE__*/React.createElement("strong", null, lockedBy?.displayName || "Unknown user"), " is currently editing this record.", /*#__PURE__*/React.createElement("br", null), " If you force unlock it, they could potentially lose their changes."), /*#__PURE__*/React.createElement(Text, null, "You are about to forcefully unlock the ", /*#__PURE__*/React.createElement("strong", null, title), " entry. Are you sure you want to continue?"));
17
+ };
18
+ export const LockedRecordForceUnlock = props => {
19
+ const {
20
+ unlockEntryForce
21
+ } = useRecordLocking();
22
+ const {
23
+ navigateTo
24
+ } = useContentEntriesList();
25
+ const {
26
+ showConfirmation: showForceUnlockConfirmation
27
+ } = useConfirmationDialog({
28
+ title: "Force unlock the entry",
29
+ message: /*#__PURE__*/React.createElement(ErrorMessage, props)
30
+ });
31
+ const {
32
+ showSnackbar
33
+ } = useSnackbar();
34
+ const {
35
+ history
36
+ } = useRouter();
37
+ const [error, setError] = useState();
38
+ useEffect(() => {
39
+ if (!error?.message) {
40
+ return;
41
+ }
42
+ console.error(error);
43
+ showSnackbar(error.message);
44
+ }, [error?.message]);
45
+ const onClick = useCallback(() => {
46
+ showForceUnlockConfirmation(async () => {
47
+ const result = await unlockEntryForce({
48
+ id: props.id,
49
+ $lockingType: props.type
50
+ });
51
+ if (!result.error) {
52
+ navigateTo();
53
+ return;
54
+ }
55
+ setError(result.error);
56
+ });
57
+ }, [props.id, history, navigateTo]);
58
+ const {
59
+ canForceUnlock
60
+ } = usePermission();
61
+ if (!canForceUnlock) {
62
+ return null;
63
+ }
64
+ return /*#__PURE__*/React.createElement("div", {
65
+ className: "wby-mt-md"
66
+ }, /*#__PURE__*/React.createElement(Text, {
67
+ as: "div",
68
+ className: "wby-mb-md"
69
+ }, "Because you have a full access to the system, you can force unlock the record."), /*#__PURE__*/React.createElement(Button, {
70
+ onClick: onClick,
71
+ text: "Unlock and go back"
72
+ }));
73
+ };
74
+
75
+ //# sourceMappingURL=LockedRecordForceUnlock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["React","useCallback","useEffect","useState","Alert","Button","Text","useConfirmationDialog","useSnackbar","useRecordLocking","usePermission","useRouter","useContentEntriesList","ErrorMessage","props","title","lockedBy","createElement","type","className","displayName","LockedRecordForceUnlock","unlockEntryForce","navigateTo","showConfirmation","showForceUnlockConfirmation","message","showSnackbar","history","error","setError","console","onClick","result","id","$lockingType","canForceUnlock","as","text"],"sources":["LockedRecordForceUnlock.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useState } from \"react\";\nimport { Alert, Button, Text } from \"@webiny/admin-ui\";\nimport type { IRecordLockingError, IRecordLockingIdentity } from \"~/types\";\nimport { useConfirmationDialog, useSnackbar } from \"@webiny/app-admin\";\nimport { useRecordLocking, usePermission } from \"~/hooks\";\nimport { useRouter } from \"@webiny/react-router\";\nimport { useContentEntriesList } from \"@webiny/app-headless-cms\";\n\nexport interface ILockedRecordForceUnlockProps {\n id: string;\n type: string;\n title: string;\n lockedBy?: IRecordLockingIdentity;\n}\n\nconst ErrorMessage = (props: ILockedRecordForceUnlockProps) => {\n const { title, lockedBy } = props;\n return (\n <div>\n <Alert type=\"warning\" title=\"Warning\" className={\"wby-mb-md\"}>\n <strong>{lockedBy?.displayName || \"Unknown user\"}</strong> is currently editing this\n record.\n <br /> If you force unlock it, they could potentially lose their changes.\n </Alert>\n\n <Text>\n You are about to forcefully unlock the <strong>{title}</strong> entry. Are you sure\n you want to continue?\n </Text>\n </div>\n );\n};\n\nexport const LockedRecordForceUnlock = (props: ILockedRecordForceUnlockProps) => {\n const { unlockEntryForce } = useRecordLocking();\n\n const { navigateTo } = useContentEntriesList();\n const { showConfirmation: showForceUnlockConfirmation } = useConfirmationDialog({\n title: \"Force unlock the entry\",\n message: <ErrorMessage {...props} />\n });\n const { showSnackbar } = useSnackbar();\n\n const { history } = useRouter();\n\n const [error, setError] = useState<IRecordLockingError>();\n\n useEffect(() => {\n if (!error?.message) {\n return;\n }\n console.error(error);\n showSnackbar(error.message);\n }, [error?.message]);\n\n const onClick = useCallback(() => {\n showForceUnlockConfirmation(async () => {\n const result = await unlockEntryForce({\n id: props.id,\n $lockingType: props.type\n });\n if (!result.error) {\n navigateTo();\n return;\n }\n setError(result.error);\n });\n }, [props.id, history, navigateTo]);\n\n const { canForceUnlock } = usePermission();\n if (!canForceUnlock) {\n return null;\n }\n\n return (\n <div className=\"wby-mt-md\">\n <Text as={\"div\"} className={\"wby-mb-md\"}>\n Because you have a full access to the system, you can force unlock the record.\n </Text>\n <Button onClick={onClick} text={\"Unlock and go back\"} />\n </div>\n );\n};\n"],"mappings":"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","ignoreList":[]}