@webiny/app-record-locking 0.0.0-unstable.e53eceafb5
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.
- package/LICENSE +21 -0
- package/README.md +12 -0
- package/components/HeadlessCmsActionsAcoCell.d.ts +2 -0
- package/components/HeadlessCmsActionsAcoCell.js +60 -0
- package/components/HeadlessCmsActionsAcoCell.js.map +1 -0
- package/components/HeadlessCmsContentEntry/ContentEntryGuard.d.ts +9 -0
- package/components/HeadlessCmsContentEntry/ContentEntryGuard.js +40 -0
- package/components/HeadlessCmsContentEntry/ContentEntryGuard.js.map +1 -0
- package/components/HeadlessCmsContentEntry/ContentEntryLocker.d.ts +18 -0
- package/components/HeadlessCmsContentEntry/ContentEntryLocker.js +106 -0
- package/components/HeadlessCmsContentEntry/ContentEntryLocker.js.map +1 -0
- package/components/HeadlessCmsContentEntry/HeadlessCmsContentEntry.d.ts +2 -0
- package/components/HeadlessCmsContentEntry/HeadlessCmsContentEntry.js +90 -0
- package/components/HeadlessCmsContentEntry/HeadlessCmsContentEntry.js.map +1 -0
- package/components/HeadlessCmsContentEntry/index.d.ts +1 -0
- package/components/HeadlessCmsContentEntry/index.js +3 -0
- package/components/HeadlessCmsContentEntry/index.js.map +1 -0
- package/components/LockedRecord/LockedRecord.d.ts +6 -0
- package/components/LockedRecord/LockedRecord.js +66 -0
- package/components/LockedRecord/LockedRecord.js.map +1 -0
- package/components/LockedRecord/LockedRecordForceUnlock.d.ts +9 -0
- package/components/LockedRecord/LockedRecordForceUnlock.js +75 -0
- package/components/LockedRecord/LockedRecordForceUnlock.js.map +1 -0
- package/components/LockedRecord/index.d.ts +1 -0
- package/components/LockedRecord/index.js +3 -0
- package/components/LockedRecord/index.js.map +1 -0
- package/components/RecordLockingProvider.d.ts +7 -0
- package/components/RecordLockingProvider.js +115 -0
- package/components/RecordLockingProvider.js.map +1 -0
- package/components/decorators/UseContentEntriesListHookDecorator.d.ts +2 -0
- package/components/decorators/UseContentEntriesListHookDecorator.js +21 -0
- package/components/decorators/UseContentEntriesListHookDecorator.js.map +1 -0
- package/components/decorators/UseRecordsDecorator.d.ts +9 -0
- package/components/decorators/UseRecordsDecorator.js +25 -0
- package/components/decorators/UseRecordsDecorator.js.map +1 -0
- package/components/decorators/UseSaveEntryDecorator.d.ts +2 -0
- package/components/decorators/UseSaveEntryDecorator.js +55 -0
- package/components/decorators/UseSaveEntryDecorator.js.map +1 -0
- package/components/permissionRenderer/RecordLockingPermissions.d.ts +7 -0
- package/components/permissionRenderer/RecordLockingPermissions.js +71 -0
- package/components/permissionRenderer/RecordLockingPermissions.js.map +1 -0
- package/components/permissionRenderer/index.d.ts +2 -0
- package/components/permissionRenderer/index.js +21 -0
- package/components/permissionRenderer/index.js.map +1 -0
- package/domain/RecordLocking.d.ts +28 -0
- package/domain/RecordLocking.js +320 -0
- package/domain/RecordLocking.js.map +1 -0
- package/domain/RecordLockingClient.d.ts +12 -0
- package/domain/RecordLockingClient.js +19 -0
- package/domain/RecordLockingClient.js.map +1 -0
- package/domain/RecordLockingGetLockRecord.d.ts +11 -0
- package/domain/RecordLockingGetLockRecord.js +21 -0
- package/domain/RecordLockingGetLockRecord.js.map +1 -0
- package/domain/RecordLockingGetLockedEntryLockRecord.d.ts +11 -0
- package/domain/RecordLockingGetLockedEntryLockRecord.js +19 -0
- package/domain/RecordLockingGetLockedEntryLockRecord.js.map +1 -0
- package/domain/RecordLockingIsEntryLocked.d.ts +11 -0
- package/domain/RecordLockingIsEntryLocked.js +19 -0
- package/domain/RecordLockingIsEntryLocked.js.map +1 -0
- package/domain/RecordLockingListLockRecords.d.ts +12 -0
- package/domain/RecordLockingListLockRecords.js +31 -0
- package/domain/RecordLockingListLockRecords.js.map +1 -0
- package/domain/RecordLockingLockEntry.d.ts +11 -0
- package/domain/RecordLockingLockEntry.js +15 -0
- package/domain/RecordLockingLockEntry.js.map +1 -0
- package/domain/RecordLockingUnlockEntry.d.ts +11 -0
- package/domain/RecordLockingUnlockEntry.js +19 -0
- package/domain/RecordLockingUnlockEntry.js.map +1 -0
- package/domain/RecordLockingUpdateEntryLock.d.ts +11 -0
- package/domain/RecordLockingUpdateEntryLock.js +19 -0
- package/domain/RecordLockingUpdateEntryLock.js.map +1 -0
- package/domain/abstractions/IRecordLocking.d.ts +25 -0
- package/domain/abstractions/IRecordLocking.js +3 -0
- package/domain/abstractions/IRecordLocking.js.map +1 -0
- package/domain/abstractions/IRecordLockingClient.d.ts +6 -0
- package/domain/abstractions/IRecordLockingClient.js +3 -0
- package/domain/abstractions/IRecordLockingClient.js.map +1 -0
- package/domain/abstractions/IRecordLockingGetLockRecord.d.ts +12 -0
- package/domain/abstractions/IRecordLockingGetLockRecord.js +3 -0
- package/domain/abstractions/IRecordLockingGetLockRecord.js.map +1 -0
- package/domain/abstractions/IRecordLockingGetLockedEntryLockRecord.d.ts +12 -0
- package/domain/abstractions/IRecordLockingGetLockedEntryLockRecord.js +3 -0
- package/domain/abstractions/IRecordLockingGetLockedEntryLockRecord.js.map +1 -0
- package/domain/abstractions/IRecordLockingIsEntryLocked.d.ts +8 -0
- package/domain/abstractions/IRecordLockingIsEntryLocked.js +3 -0
- package/domain/abstractions/IRecordLockingIsEntryLocked.js.map +1 -0
- package/domain/abstractions/IRecordLockingListLockRecords.d.ts +19 -0
- package/domain/abstractions/IRecordLockingListLockRecords.js +3 -0
- package/domain/abstractions/IRecordLockingListLockRecords.js.map +1 -0
- package/domain/abstractions/IRecordLockingLockEntry.d.ts +12 -0
- package/domain/abstractions/IRecordLockingLockEntry.js +3 -0
- package/domain/abstractions/IRecordLockingLockEntry.js.map +1 -0
- package/domain/abstractions/IRecordLockingUnlockEntry.d.ts +13 -0
- package/domain/abstractions/IRecordLockingUnlockEntry.js +3 -0
- package/domain/abstractions/IRecordLockingUnlockEntry.js.map +1 -0
- package/domain/abstractions/IRecordLockingUpdateEntryLock.d.ts +12 -0
- package/domain/abstractions/IRecordLockingUpdateEntryLock.js +3 -0
- package/domain/abstractions/IRecordLockingUpdateEntryLock.js.map +1 -0
- package/domain/graphql/fields.d.ts +2 -0
- package/domain/graphql/fields.js +30 -0
- package/domain/graphql/fields.js.map +1 -0
- package/domain/graphql/getLockRecord.d.ts +12 -0
- package/domain/graphql/getLockRecord.js +18 -0
- package/domain/graphql/getLockRecord.js.map +1 -0
- package/domain/graphql/getLockedEntryLockRecord.d.ts +12 -0
- package/domain/graphql/getLockedEntryLockRecord.js +18 -0
- package/domain/graphql/getLockedEntryLockRecord.js.map +1 -0
- package/domain/graphql/isEntryLocked.d.ts +12 -0
- package/domain/graphql/isEntryLocked.js +16 -0
- package/domain/graphql/isEntryLocked.js.map +1 -0
- package/domain/graphql/listLockRecords.d.ts +17 -0
- package/domain/graphql/listLockRecords.js +26 -0
- package/domain/graphql/listLockRecords.js.map +1 -0
- package/domain/graphql/lockEntry.d.ts +12 -0
- package/domain/graphql/lockEntry.js +20 -0
- package/domain/graphql/lockEntry.js.map +1 -0
- package/domain/graphql/unlockEntry.d.ts +12 -0
- package/domain/graphql/unlockEntry.js +18 -0
- package/domain/graphql/unlockEntry.js.map +1 -0
- package/domain/graphql/updateEntryLock.d.ts +12 -0
- package/domain/graphql/updateEntryLock.js +18 -0
- package/domain/graphql/updateEntryLock.js.map +1 -0
- package/domain/utils/createRecordLockingClient.d.ts +3 -0
- package/domain/utils/createRecordLockingClient.js +12 -0
- package/domain/utils/createRecordLockingClient.js.map +1 -0
- package/domain/utils/createRecordLockingError.d.ts +6 -0
- package/domain/utils/createRecordLockingError.js +16 -0
- package/domain/utils/createRecordLockingError.js.map +1 -0
- package/hooks/index.d.ts +2 -0
- package/hooks/index.js +4 -0
- package/hooks/index.js.map +1 -0
- package/hooks/usePermission.d.ts +3 -0
- package/hooks/usePermission.js +21 -0
- package/hooks/usePermission.js.map +1 -0
- package/hooks/useRecordLocking.d.ts +2 -0
- package/hooks/useRecordLocking.js +12 -0
- package/hooks/useRecordLocking.js.map +1 -0
- package/index.d.ts +7 -0
- package/index.js +29 -0
- package/index.js.map +1 -0
- package/package.json +52 -0
- package/types.d.ts +77 -0
- package/types.js +3 -0
- package/types.js.map +1 -0
- package/utils/createCacheKey.d.ts +3 -0
- package/utils/createCacheKey.js +15 -0
- 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://www.npmjs.com/package/@webiny/app-record-locking)
|
|
3
|
+
[](https://www.npmjs.com/package/@webiny/app-record-locking)
|
|
4
|
+
[](https://github.com/prettier/prettier)
|
|
5
|
+
[](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,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,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 @@
|
|
|
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":[]}
|