@webiny/app-record-locking 6.4.12-beta.5 → 6.5.0-beta.0
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/RecordLockingModule.d.ts +2 -0
- package/RecordLockingModule.js +74 -0
- package/RecordLockingModule.js.map +1 -0
- package/{components/SecurityPermissions.js → SecurityPermissions.js} +1 -1
- package/SecurityPermissions.js.map +1 -0
- package/features/acquireLock/AcquireLockGateway.d.ts +12 -0
- package/features/acquireLock/AcquireLockGateway.js +43 -0
- package/features/acquireLock/AcquireLockGateway.js.map +1 -0
- package/features/acquireLock/AcquireLockUseCase.d.ts +10 -0
- package/features/acquireLock/AcquireLockUseCase.js +18 -0
- package/features/acquireLock/AcquireLockUseCase.js.map +1 -0
- package/features/acquireLock/abstractions.d.ts +19 -0
- package/features/acquireLock/abstractions.js +6 -0
- package/features/acquireLock/abstractions.js.map +1 -0
- package/features/acquireLock/feature.d.ts +3 -0
- package/features/acquireLock/feature.js +19 -0
- package/features/acquireLock/feature.js.map +1 -0
- package/features/checkLockStatus/CheckLockStatusGateway.d.ts +12 -0
- package/features/checkLockStatus/CheckLockStatusGateway.js +43 -0
- package/features/checkLockStatus/CheckLockStatusGateway.js.map +1 -0
- package/features/checkLockStatus/CheckLockStatusUseCase.d.ts +10 -0
- package/features/checkLockStatus/CheckLockStatusUseCase.js +18 -0
- package/features/checkLockStatus/CheckLockStatusUseCase.js.map +1 -0
- package/features/checkLockStatus/abstractions.d.ts +19 -0
- package/features/checkLockStatus/abstractions.js +6 -0
- package/features/checkLockStatus/abstractions.js.map +1 -0
- package/features/checkLockStatus/feature.d.ts +3 -0
- package/features/checkLockStatus/feature.js +19 -0
- package/features/checkLockStatus/feature.js.map +1 -0
- package/features/feature.d.ts +1 -0
- package/features/feature.js +19 -0
- package/features/feature.js.map +1 -0
- package/features/forceUnlock/ForceUnlockGateway.d.ts +12 -0
- package/features/forceUnlock/ForceUnlockGateway.js +44 -0
- package/features/forceUnlock/ForceUnlockGateway.js.map +1 -0
- package/features/forceUnlock/ForceUnlockUseCase.d.ts +10 -0
- package/features/forceUnlock/ForceUnlockUseCase.js +18 -0
- package/features/forceUnlock/ForceUnlockUseCase.js.map +1 -0
- package/features/forceUnlock/abstractions.d.ts +19 -0
- package/features/forceUnlock/abstractions.js +6 -0
- package/features/forceUnlock/abstractions.js.map +1 -0
- package/features/forceUnlock/feature.d.ts +3 -0
- package/features/forceUnlock/feature.js +19 -0
- package/features/forceUnlock/feature.js.map +1 -0
- package/{domain/graphql/fields.js → features/graphqlFields.js} +1 -1
- package/features/graphqlFields.js.map +1 -0
- package/features/listLockRecords/ListLockRecordsGateway.d.ts +12 -0
- package/features/listLockRecords/ListLockRecordsGateway.js +48 -0
- package/features/listLockRecords/ListLockRecordsGateway.js.map +1 -0
- package/features/listLockRecords/ListLockRecordsUseCase.d.ts +10 -0
- package/features/listLockRecords/ListLockRecordsUseCase.js +18 -0
- package/features/listLockRecords/ListLockRecordsUseCase.js.map +1 -0
- package/features/listLockRecords/abstractions.d.ts +22 -0
- package/features/listLockRecords/abstractions.js +6 -0
- package/features/listLockRecords/abstractions.js.map +1 -0
- package/features/listLockRecords/feature.d.ts +3 -0
- package/features/listLockRecords/feature.js +19 -0
- package/features/listLockRecords/feature.js.map +1 -0
- package/features/permissions/abstractions.d.ts +1 -1
- package/features/permissions/abstractions.js +1 -1
- package/features/permissions/abstractions.js.map +1 -1
- package/features/permissions/feature.d.ts +1 -1
- package/features/permissions/feature.js +1 -1
- package/features/permissions/feature.js.map +1 -1
- package/features/releaseLock/ReleaseLockGateway.d.ts +12 -0
- package/features/releaseLock/ReleaseLockGateway.js +44 -0
- package/features/releaseLock/ReleaseLockGateway.js.map +1 -0
- package/features/releaseLock/ReleaseLockUseCase.d.ts +10 -0
- package/features/releaseLock/ReleaseLockUseCase.js +18 -0
- package/features/releaseLock/ReleaseLockUseCase.js.map +1 -0
- package/features/releaseLock/abstractions.d.ts +19 -0
- package/features/releaseLock/abstractions.js +6 -0
- package/features/releaseLock/abstractions.js.map +1 -0
- package/features/releaseLock/feature.d.ts +3 -0
- package/features/releaseLock/feature.js +19 -0
- package/features/releaseLock/feature.js.map +1 -0
- package/index.d.ts +0 -5
- package/index.js +12 -13
- package/index.js.map +1 -1
- package/package.json +13 -17
- package/permissions/permissionsSchema.js.map +1 -0
- package/presentation/entryLocking/ContentEntryFormPresenterDecorator.d.ts +23 -0
- package/presentation/entryLocking/ContentEntryFormPresenterDecorator.js +64 -0
- package/presentation/entryLocking/ContentEntryFormPresenterDecorator.js.map +1 -0
- package/presentation/entryLocking/RecordLockingPresenter.d.ts +32 -0
- package/presentation/entryLocking/RecordLockingPresenter.js +164 -0
- package/presentation/entryLocking/RecordLockingPresenter.js.map +1 -0
- package/presentation/entryLocking/SingleEntryContentDecorator.d.ts +2 -0
- package/presentation/entryLocking/SingleEntryContentDecorator.js +72 -0
- package/presentation/entryLocking/SingleEntryContentDecorator.js.map +1 -0
- package/presentation/entryLocking/SingleEntryPresenterDecorator.d.ts +15 -0
- package/presentation/entryLocking/SingleEntryPresenterDecorator.js +40 -0
- package/presentation/entryLocking/SingleEntryPresenterDecorator.js.map +1 -0
- package/presentation/entryLocking/abstractions.d.ts +26 -0
- package/presentation/entryLocking/abstractions.js +5 -0
- package/presentation/entryLocking/abstractions.js.map +1 -0
- package/presentation/entryLocking/components/ForceUnlockButton.d.ts +11 -0
- package/presentation/entryLocking/components/ForceUnlockButton.js +37 -0
- package/presentation/entryLocking/components/ForceUnlockButton.js.map +1 -0
- package/presentation/entryLocking/components/LockedEntryOverlay.d.ts +10 -0
- package/presentation/entryLocking/components/LockedEntryOverlay.js +52 -0
- package/presentation/entryLocking/components/LockedEntryOverlay.js.map +1 -0
- package/presentation/entryLocking/components/RecordLockingGuard.d.ts +12 -0
- package/presentation/entryLocking/components/RecordLockingGuard.js +74 -0
- package/presentation/entryLocking/components/RecordLockingGuard.js.map +1 -0
- package/presentation/entryLocking/feature.d.ts +3 -0
- package/presentation/entryLocking/feature.js +21 -0
- package/presentation/entryLocking/feature.js.map +1 -0
- package/presentation/listLocking/ContentEntriesPresenterDecorator.d.ts +24 -0
- package/presentation/listLocking/ContentEntriesPresenterDecorator.js +67 -0
- package/presentation/listLocking/ContentEntriesPresenterDecorator.js.map +1 -0
- package/presentation/listLocking/ListLockRecordsPresenter.d.ts +17 -0
- package/presentation/listLocking/ListLockRecordsPresenter.js +52 -0
- package/presentation/listLocking/ListLockRecordsPresenter.js.map +1 -0
- package/presentation/listLocking/abstractions.d.ts +16 -0
- package/presentation/listLocking/abstractions.js +5 -0
- package/presentation/listLocking/abstractions.js.map +1 -0
- package/presentation/listLocking/components/ListLockRecordsSync.d.ts +3 -0
- package/presentation/listLocking/components/ListLockRecordsSync.js +26 -0
- package/presentation/listLocking/components/ListLockRecordsSync.js.map +1 -0
- package/presentation/listLocking/components/LockIndicatorCell.d.ts +2 -0
- package/presentation/listLocking/components/LockIndicatorCell.js +39 -0
- package/presentation/listLocking/components/LockIndicatorCell.js.map +1 -0
- package/presentation/listLocking/feature.d.ts +3 -0
- package/presentation/listLocking/feature.js +19 -0
- package/presentation/listLocking/feature.js.map +1 -0
- package/types.d.ts +1 -62
- package/components/HeadlessCmsActionsAcoCell.d.ts +0 -2
- package/components/HeadlessCmsActionsAcoCell.js +0 -38
- package/components/HeadlessCmsActionsAcoCell.js.map +0 -1
- package/components/HeadlessCmsContentEntry/ContentEntryGuard.d.ts +0 -9
- package/components/HeadlessCmsContentEntry/ContentEntryGuard.js +0 -34
- package/components/HeadlessCmsContentEntry/ContentEntryGuard.js.map +0 -1
- package/components/HeadlessCmsContentEntry/ContentEntryLocker.d.ts +0 -18
- package/components/HeadlessCmsContentEntry/ContentEntryLocker.js +0 -80
- package/components/HeadlessCmsContentEntry/ContentEntryLocker.js.map +0 -1
- package/components/HeadlessCmsContentEntry/HeadlessCmsContentEntry.d.ts +0 -2
- package/components/HeadlessCmsContentEntry/HeadlessCmsContentEntry.js +0 -44
- package/components/HeadlessCmsContentEntry/HeadlessCmsContentEntry.js.map +0 -1
- package/components/HeadlessCmsContentEntry/index.d.ts +0 -1
- package/components/HeadlessCmsContentEntry/index.js +0 -1
- package/components/LockedRecord/LockedRecord.d.ts +0 -6
- package/components/LockedRecord/LockedRecord.js +0 -54
- package/components/LockedRecord/LockedRecord.js.map +0 -1
- package/components/LockedRecord/LockedRecordForceUnlock.d.ts +0 -9
- package/components/LockedRecord/LockedRecordForceUnlock.js +0 -57
- package/components/LockedRecord/LockedRecordForceUnlock.js.map +0 -1
- package/components/LockedRecord/index.d.ts +0 -1
- package/components/LockedRecord/index.js +0 -1
- package/components/RecordLockingProvider.d.ts +0 -7
- package/components/RecordLockingProvider.js +0 -111
- package/components/RecordLockingProvider.js.map +0 -1
- package/components/SecurityPermissions.js.map +0 -1
- package/components/decorators/UseContentEntriesListHookDecorator.d.ts +0 -1
- package/components/decorators/UseContentEntriesListHookDecorator.js +0 -23
- package/components/decorators/UseContentEntriesListHookDecorator.js.map +0 -1
- package/components/decorators/UseRecordsDecorator.d.ts +0 -8
- package/components/decorators/UseRecordsDecorator.js +0 -16
- package/components/decorators/UseRecordsDecorator.js.map +0 -1
- package/components/decorators/UseSaveEntryDecorator.d.ts +0 -1
- package/components/decorators/UseSaveEntryDecorator.js +0 -41
- package/components/decorators/UseSaveEntryDecorator.js.map +0 -1
- package/domain/RecordLocking.d.ts +0 -28
- package/domain/RecordLocking.js +0 -259
- package/domain/RecordLocking.js.map +0 -1
- package/domain/RecordLockingClient.d.ts +0 -12
- package/domain/RecordLockingClient.js +0 -20
- package/domain/RecordLockingClient.js.map +0 -1
- package/domain/RecordLockingGetLockRecord.d.ts +0 -11
- package/domain/RecordLockingGetLockRecord.js +0 -19
- package/domain/RecordLockingGetLockRecord.js.map +0 -1
- package/domain/RecordLockingGetLockedEntryLockRecord.d.ts +0 -11
- package/domain/RecordLockingGetLockedEntryLockRecord.js +0 -18
- package/domain/RecordLockingGetLockedEntryLockRecord.js.map +0 -1
- package/domain/RecordLockingIsEntryLocked.d.ts +0 -11
- package/domain/RecordLockingIsEntryLocked.js +0 -20
- package/domain/RecordLockingIsEntryLocked.js.map +0 -1
- package/domain/RecordLockingListLockRecords.d.ts +0 -12
- package/domain/RecordLockingListLockRecords.js +0 -25
- package/domain/RecordLockingListLockRecords.js.map +0 -1
- package/domain/RecordLockingLockEntry.d.ts +0 -11
- package/domain/RecordLockingLockEntry.js +0 -12
- package/domain/RecordLockingLockEntry.js.map +0 -1
- package/domain/RecordLockingUnlockEntry.d.ts +0 -11
- package/domain/RecordLockingUnlockEntry.js +0 -18
- package/domain/RecordLockingUnlockEntry.js.map +0 -1
- package/domain/RecordLockingUpdateEntryLock.d.ts +0 -11
- package/domain/RecordLockingUpdateEntryLock.js +0 -18
- package/domain/RecordLockingUpdateEntryLock.js.map +0 -1
- package/domain/abstractions/IRecordLocking.d.ts +0 -25
- package/domain/abstractions/IRecordLocking.js +0 -0
- package/domain/abstractions/IRecordLockingClient.d.ts +0 -6
- package/domain/abstractions/IRecordLockingClient.js +0 -0
- package/domain/abstractions/IRecordLockingGetLockRecord.d.ts +0 -12
- package/domain/abstractions/IRecordLockingGetLockRecord.js +0 -0
- package/domain/abstractions/IRecordLockingGetLockedEntryLockRecord.d.ts +0 -12
- package/domain/abstractions/IRecordLockingGetLockedEntryLockRecord.js +0 -0
- package/domain/abstractions/IRecordLockingIsEntryLocked.d.ts +0 -8
- package/domain/abstractions/IRecordLockingIsEntryLocked.js +0 -0
- package/domain/abstractions/IRecordLockingListLockRecords.d.ts +0 -19
- package/domain/abstractions/IRecordLockingListLockRecords.js +0 -0
- package/domain/abstractions/IRecordLockingLockEntry.d.ts +0 -12
- package/domain/abstractions/IRecordLockingLockEntry.js +0 -0
- package/domain/abstractions/IRecordLockingUnlockEntry.d.ts +0 -13
- package/domain/abstractions/IRecordLockingUnlockEntry.js +0 -0
- package/domain/abstractions/IRecordLockingUpdateEntryLock.d.ts +0 -12
- package/domain/abstractions/IRecordLockingUpdateEntryLock.js +0 -0
- package/domain/graphql/fields.js.map +0 -1
- package/domain/graphql/getLockRecord.d.ts +0 -12
- package/domain/graphql/getLockRecord.js +0 -19
- package/domain/graphql/getLockRecord.js.map +0 -1
- package/domain/graphql/getLockedEntryLockRecord.d.ts +0 -12
- package/domain/graphql/getLockedEntryLockRecord.js +0 -19
- package/domain/graphql/getLockedEntryLockRecord.js.map +0 -1
- package/domain/graphql/isEntryLocked.d.ts +0 -12
- package/domain/graphql/isEntryLocked.js +0 -17
- package/domain/graphql/isEntryLocked.js.map +0 -1
- package/domain/graphql/listLockRecords.d.ts +0 -17
- package/domain/graphql/listLockRecords.js +0 -25
- package/domain/graphql/listLockRecords.js.map +0 -1
- package/domain/graphql/lockEntry.d.ts +0 -12
- package/domain/graphql/lockEntry.js +0 -19
- package/domain/graphql/lockEntry.js.map +0 -1
- package/domain/graphql/unlockEntry.d.ts +0 -12
- package/domain/graphql/unlockEntry.js +0 -19
- package/domain/graphql/unlockEntry.js.map +0 -1
- package/domain/graphql/updateEntryLock.d.ts +0 -12
- package/domain/graphql/updateEntryLock.js +0 -19
- package/domain/graphql/updateEntryLock.js.map +0 -1
- package/domain/permissionsSchema.js.map +0 -1
- package/domain/utils/createRecordLockingClient.d.ts +0 -3
- package/domain/utils/createRecordLockingClient.js +0 -11
- package/domain/utils/createRecordLockingClient.js.map +0 -1
- package/domain/utils/createRecordLockingError.d.ts +0 -6
- package/domain/utils/createRecordLockingError.js +0 -15
- package/domain/utils/createRecordLockingError.js.map +0 -1
- package/hooks/index.d.ts +0 -2
- package/hooks/index.js +0 -2
- package/hooks/usePermission.d.ts +0 -3
- package/hooks/usePermission.js +0 -19
- package/hooks/usePermission.js.map +0 -1
- package/hooks/useRecordLocking.d.ts +0 -2
- package/hooks/useRecordLocking.js +0 -11
- package/hooks/useRecordLocking.js.map +0 -1
- package/utils/createCacheKey.d.ts +0 -3
- package/utils/createCacheKey.js +0 -14
- package/utils/createCacheKey.js.map +0 -1
- /package/{components/SecurityPermissions.d.ts → SecurityPermissions.d.ts} +0 -0
- /package/{domain/graphql/fields.d.ts → features/graphqlFields.d.ts} +0 -0
- /package/{domain → permissions}/permissionsSchema.d.ts +0 -0
- /package/{domain → permissions}/permissionsSchema.js +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
interface RecordLockingGuardProps {
|
|
3
|
+
entryId: string;
|
|
4
|
+
modelId: string;
|
|
5
|
+
entryTitle?: string;
|
|
6
|
+
onNavigateBack: () => void;
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
}
|
|
9
|
+
export declare const RecordLockingGuard: (({ entryId, modelId, entryTitle, onNavigateBack, children }: RecordLockingGuardProps) => React.JSX.Element) & {
|
|
10
|
+
displayName: string;
|
|
11
|
+
};
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import react, { useEffect } from "react";
|
|
2
|
+
import { observer } from "mobx-react-lite";
|
|
3
|
+
import { useContainer, useRouter } from "@webiny/app";
|
|
4
|
+
import { useDialogs } from "@webiny/app-admin";
|
|
5
|
+
import { OverlayLoader } from "@webiny/admin-ui";
|
|
6
|
+
import { useWebsockets } from "@webiny/app-websockets";
|
|
7
|
+
import { parseIdentifier } from "@webiny/utils";
|
|
8
|
+
import { RecordLockingPresenter } from "../abstractions.js";
|
|
9
|
+
import { LockedEntryOverlay } from "./LockedEntryOverlay.js";
|
|
10
|
+
const RecordLockingGuard = observer(({ entryId, modelId, entryTitle, onNavigateBack, children })=>{
|
|
11
|
+
const container = useContainer();
|
|
12
|
+
const presenter = react.useMemo(()=>container.resolve(RecordLockingPresenter), [
|
|
13
|
+
container
|
|
14
|
+
]);
|
|
15
|
+
const websockets = useWebsockets();
|
|
16
|
+
const router = useRouter();
|
|
17
|
+
const { showDialog } = useDialogs();
|
|
18
|
+
useEffect(()=>{
|
|
19
|
+
presenter.init(entryId, modelId);
|
|
20
|
+
return ()=>{
|
|
21
|
+
presenter.dispose();
|
|
22
|
+
};
|
|
23
|
+
}, [
|
|
24
|
+
entryId,
|
|
25
|
+
modelId
|
|
26
|
+
]);
|
|
27
|
+
useEffect(()=>{
|
|
28
|
+
const { id } = parseIdentifier(entryId);
|
|
29
|
+
const sub = websockets.onMessage(`recordLocking.entry.kickOut.${id}`, (incoming)=>{
|
|
30
|
+
presenter.handleKickOut(incoming.data);
|
|
31
|
+
});
|
|
32
|
+
return ()=>{
|
|
33
|
+
sub.off();
|
|
34
|
+
};
|
|
35
|
+
}, [
|
|
36
|
+
entryId,
|
|
37
|
+
modelId
|
|
38
|
+
]);
|
|
39
|
+
useEffect(()=>{
|
|
40
|
+
if ("kicked-out" !== presenter.vm.status) return;
|
|
41
|
+
router.unblockTransition();
|
|
42
|
+
showDialog({
|
|
43
|
+
title: "Entry was forcefully unlocked!",
|
|
44
|
+
content: /*#__PURE__*/ react.createElement(react.Fragment, null, "The entry you were editing was forcefully unlocked by", " ", /*#__PURE__*/ react.createElement("strong", null, presenter.vm.lockRecord?.lockedBy?.displayName || "Unknown user"), ". Unfortunately, this means you lost the unsaved changes."),
|
|
45
|
+
acceptLabel: "Ok",
|
|
46
|
+
onClose: void 0,
|
|
47
|
+
cancelLabel: null
|
|
48
|
+
});
|
|
49
|
+
onNavigateBack();
|
|
50
|
+
}, [
|
|
51
|
+
presenter.vm.status
|
|
52
|
+
]);
|
|
53
|
+
const { vm } = presenter;
|
|
54
|
+
if ("checking" === vm.status) return /*#__PURE__*/ react.createElement("div", {
|
|
55
|
+
className: "h-screen w-screen fixed top-0 left-0 z-20"
|
|
56
|
+
}, /*#__PURE__*/ react.createElement(OverlayLoader, {
|
|
57
|
+
text: "Checking record status..."
|
|
58
|
+
}));
|
|
59
|
+
if ("locked" === vm.status) return /*#__PURE__*/ react.createElement(LockedEntryOverlay, {
|
|
60
|
+
vm: vm,
|
|
61
|
+
entryTitle: entryTitle,
|
|
62
|
+
onForceUnlock: ()=>presenter.forceUnlock(),
|
|
63
|
+
onNavigateBack: onNavigateBack
|
|
64
|
+
});
|
|
65
|
+
if ("error" === vm.status) return /*#__PURE__*/ react.createElement("div", {
|
|
66
|
+
className: "h-screen w-screen fixed top-0 left-0 z-20"
|
|
67
|
+
}, /*#__PURE__*/ react.createElement(OverlayLoader, {
|
|
68
|
+
text: "Failed to check record lock status."
|
|
69
|
+
}));
|
|
70
|
+
return /*#__PURE__*/ react.createElement(react.Fragment, null, children);
|
|
71
|
+
});
|
|
72
|
+
export { RecordLockingGuard };
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=RecordLockingGuard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation/entryLocking/components/RecordLockingGuard.js","sources":["../../../../src/presentation/entryLocking/components/RecordLockingGuard.tsx"],"sourcesContent":["import React, { useEffect } from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport { useContainer, useRouter } from \"@webiny/app\";\nimport { useDialogs } from \"@webiny/app-admin\";\nimport { OverlayLoader } from \"@webiny/admin-ui\";\nimport { useWebsockets } from \"@webiny/app-websockets\";\nimport { parseIdentifier } from \"@webiny/utils\";\nimport { RecordLockingPresenter } from \"../abstractions.js\";\nimport { LockedEntryOverlay } from \"./LockedEntryOverlay.js\";\nimport type { IKickOutData } from \"../abstractions.js\";\nimport type { IncomingGenericData } from \"@webiny/app-websockets\";\n\ninterface KickOutMessage extends IncomingGenericData {\n data: IKickOutData;\n}\n\ninterface RecordLockingGuardProps {\n entryId: string;\n modelId: string;\n entryTitle?: string;\n onNavigateBack: () => void;\n children: React.ReactNode;\n}\n\nexport const RecordLockingGuard = observer(\n ({ entryId, modelId, entryTitle, onNavigateBack, children }: RecordLockingGuardProps) => {\n const container = useContainer();\n const presenter = React.useMemo(\n () => container.resolve(RecordLockingPresenter),\n [container]\n );\n const websockets = useWebsockets();\n const router = useRouter();\n const { showDialog } = useDialogs();\n\n useEffect(() => {\n presenter.init(entryId, modelId);\n\n return () => {\n presenter.dispose();\n };\n }, [entryId, modelId]);\n\n useEffect(() => {\n const { id } = parseIdentifier(entryId);\n\n const sub = websockets.onMessage<KickOutMessage>(\n `recordLocking.entry.kickOut.${id}`,\n incoming => {\n presenter.handleKickOut(incoming.data);\n }\n );\n\n return () => {\n sub.off();\n };\n }, [entryId, modelId]);\n\n useEffect(() => {\n if (presenter.vm.status !== \"kicked-out\") {\n return;\n }\n\n router.unblockTransition();\n\n showDialog({\n title: \"Entry was forcefully unlocked!\",\n content: (\n <>\n The entry you were editing was forcefully unlocked by{\" \"}\n <strong>\n {presenter.vm.lockRecord?.lockedBy?.displayName || \"Unknown user\"}\n </strong>\n . Unfortunately, this means you lost the unsaved changes.\n </>\n ),\n acceptLabel: \"Ok\",\n onClose: undefined,\n cancelLabel: null\n });\n\n onNavigateBack();\n }, [presenter.vm.status]);\n\n const { vm } = presenter;\n\n if (vm.status === \"checking\") {\n return (\n <div className={\"h-screen w-screen fixed top-0 left-0 z-20\"}>\n <OverlayLoader text={\"Checking record status...\"} />\n </div>\n );\n }\n\n if (vm.status === \"locked\") {\n return (\n <LockedEntryOverlay\n vm={vm}\n entryTitle={entryTitle}\n onForceUnlock={() => presenter.forceUnlock()}\n onNavigateBack={onNavigateBack}\n />\n );\n }\n\n if (vm.status === \"error\") {\n return (\n <div className={\"h-screen w-screen fixed top-0 left-0 z-20\"}>\n <OverlayLoader text={\"Failed to check record lock status.\"} />\n </div>\n );\n }\n\n return <>{children}</>;\n }\n);\n"],"names":["RecordLockingGuard","observer","entryId","modelId","entryTitle","onNavigateBack","children","container","useContainer","presenter","React","RecordLockingPresenter","websockets","useWebsockets","router","useRouter","showDialog","useDialogs","useEffect","id","parseIdentifier","sub","incoming","undefined","vm","OverlayLoader","LockedEntryOverlay"],"mappings":";;;;;;;;;AAwBO,MAAMA,qBAAqBC,SAC9B,CAAC,EAAEC,OAAO,EAAEC,OAAO,EAAEC,UAAU,EAAEC,cAAc,EAAEC,QAAQ,EAA2B;IAChF,MAAMC,YAAYC;IAClB,MAAMC,YAAYC,MAAAA,OAAa,CAC3B,IAAMH,UAAU,OAAO,CAACI,yBACxB;QAACJ;KAAU;IAEf,MAAMK,aAAaC;IACnB,MAAMC,SAASC;IACf,MAAM,EAAEC,UAAU,EAAE,GAAGC;IAEvBC,UAAU;QACNT,UAAU,IAAI,CAACP,SAASC;QAExB,OAAO;YACHM,UAAU,OAAO;QACrB;IACJ,GAAG;QAACP;QAASC;KAAQ;IAErBe,UAAU;QACN,MAAM,EAAEC,EAAE,EAAE,GAAGC,gBAAgBlB;QAE/B,MAAMmB,MAAMT,WAAW,SAAS,CAC5B,CAAC,4BAA4B,EAAEO,IAAI,EACnCG,CAAAA;YACIb,UAAU,aAAa,CAACa,SAAS,IAAI;QACzC;QAGJ,OAAO;YACHD,IAAI,GAAG;QACX;IACJ,GAAG;QAACnB;QAASC;KAAQ;IAErBe,UAAU;QACN,IAAIT,AAAwB,iBAAxBA,UAAU,EAAE,CAAC,MAAM,EACnB;QAGJK,OAAO,iBAAiB;QAExBE,WAAW;YACP,OAAO;YACP,SAAS,WAAT,GACI,0CAAE,yDACwD,mBACtD,oBAAC,gBACIP,UAAU,EAAE,CAAC,UAAU,EAAE,UAAU,eAAe,iBAC9C;YAIjB,aAAa;YACb,SAASc;YACT,aAAa;QACjB;QAEAlB;IACJ,GAAG;QAACI,UAAU,EAAE,CAAC,MAAM;KAAC;IAExB,MAAM,EAAEe,EAAE,EAAE,GAAGf;IAEf,IAAIe,AAAc,eAAdA,GAAG,MAAM,EACT,OAAO,WAAP,GACI,oBAAC;QAAI,WAAW;qBACZ,oBAACC,eAAaA;QAAC,MAAM;;IAKjC,IAAID,AAAc,aAAdA,GAAG,MAAM,EACT,OAAO,WAAP,GACI,oBAACE,oBAAkBA;QACf,IAAIF;QACJ,YAAYpB;QACZ,eAAe,IAAMK,UAAU,WAAW;QAC1C,gBAAgBJ;;IAK5B,IAAImB,AAAc,YAAdA,GAAG,MAAM,EACT,OAAO,WAAP,GACI,oBAAC;QAAI,WAAW;qBACZ,oBAACC,eAAaA;QAAC,MAAM;;IAKjC,OAAO,WAAP,GAAO,0CAAGnB;AACd"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createFeature } from "@webiny/feature/admin";
|
|
2
|
+
import { RecordLockingPresenter } from "./abstractions.js";
|
|
3
|
+
import { RecordLockingPresenter as external_RecordLockingPresenter_js_RecordLockingPresenter } from "./RecordLockingPresenter.js";
|
|
4
|
+
import { ContentEntryFormPresenterLockingDecorator } from "./ContentEntryFormPresenterDecorator.js";
|
|
5
|
+
import { SingleEntryPresenterLockingDecorator } from "./SingleEntryPresenterDecorator.js";
|
|
6
|
+
const RecordLockingPresenterFeature = createFeature({
|
|
7
|
+
name: "RecordLocking/EntryLockingPresenter",
|
|
8
|
+
register (container) {
|
|
9
|
+
container.register(external_RecordLockingPresenter_js_RecordLockingPresenter).inSingletonScope();
|
|
10
|
+
container.registerDecorator(ContentEntryFormPresenterLockingDecorator);
|
|
11
|
+
container.registerDecorator(SingleEntryPresenterLockingDecorator);
|
|
12
|
+
},
|
|
13
|
+
resolve (container) {
|
|
14
|
+
return {
|
|
15
|
+
presenter: container.resolve(RecordLockingPresenter)
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
export { RecordLockingPresenterFeature };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=feature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation/entryLocking/feature.js","sources":["../../../src/presentation/entryLocking/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/admin\";\nimport { RecordLockingPresenter as Abstraction } from \"./abstractions.js\";\nimport { RecordLockingPresenter } from \"./RecordLockingPresenter.js\";\nimport { ContentEntryFormPresenterLockingDecorator } from \"./ContentEntryFormPresenterDecorator.js\";\nimport { SingleEntryPresenterLockingDecorator } from \"./SingleEntryPresenterDecorator.js\";\n\nexport const RecordLockingPresenterFeature = createFeature({\n name: \"RecordLocking/EntryLockingPresenter\",\n register(container) {\n container.register(RecordLockingPresenter).inSingletonScope();\n container.registerDecorator(ContentEntryFormPresenterLockingDecorator);\n container.registerDecorator(SingleEntryPresenterLockingDecorator);\n },\n resolve(container) {\n return {\n presenter: container.resolve(Abstraction)\n };\n }\n});\n"],"names":["RecordLockingPresenterFeature","createFeature","container","RecordLockingPresenter","ContentEntryFormPresenterLockingDecorator","SingleEntryPresenterLockingDecorator","Abstraction"],"mappings":";;;;;AAMO,MAAMA,gCAAgCC,cAAc;IACvD,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC,2DAAwB,gBAAgB;QAC3DD,UAAU,iBAAiB,CAACE;QAC5BF,UAAU,iBAAiB,CAACG;IAChC;IACA,SAAQH,SAAS;QACb,OAAO;YACH,WAAWA,UAAU,OAAO,CAACI;QACjC;IACJ;AACJ"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type IContentEntriesPresenter, type IContentEntriesInitConfig } from "@webiny/app-headless-cms/presentation/contentEntries/list/abstractions.js";
|
|
2
|
+
import { ListLockRecordsPresenter } from "./abstractions.js";
|
|
3
|
+
declare class ContentEntriesPresenterWithLocking implements IContentEntriesPresenter {
|
|
4
|
+
private lockRecordsPresenter;
|
|
5
|
+
private original;
|
|
6
|
+
private _disposeReaction;
|
|
7
|
+
constructor(lockRecordsPresenter: ListLockRecordsPresenter.Interface, original: IContentEntriesPresenter);
|
|
8
|
+
get vm(): import("@webiny/app-headless-cms/presentation/contentEntries/list/abstractions.js").IContentEntriesViewModel;
|
|
9
|
+
get list(): import("@webiny/app-admin/presentation/listPresenter/abstractions.js").IListPresenter<import("@webiny/app-headless-cms/types.js").CmsContentEntry<import("@webiny/app/types.js").GenericRecord>>;
|
|
10
|
+
get folders(): import("@webiny/app-aco/index.js").IFolderTreePresenter;
|
|
11
|
+
init(config?: IContentEntriesInitConfig): void;
|
|
12
|
+
dispose(): void;
|
|
13
|
+
selectEntry(id: string): void;
|
|
14
|
+
deselectEntry(): void;
|
|
15
|
+
createEntry(): void;
|
|
16
|
+
deleteEntry(id: string): Promise<boolean>;
|
|
17
|
+
publishEntry(id: string): Promise<boolean>;
|
|
18
|
+
unpublishEntry(id: string): Promise<boolean>;
|
|
19
|
+
moveEntry(id: string, folderId: string): Promise<boolean>;
|
|
20
|
+
}
|
|
21
|
+
export declare const ContentEntriesPresenterLockingDecorator: typeof ContentEntriesPresenterWithLocking & {
|
|
22
|
+
__abstraction: import("@webiny/di").Abstraction<IContentEntriesPresenter>;
|
|
23
|
+
};
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { comparer, reaction } from "mobx";
|
|
2
|
+
import { ContentEntriesPresenter } from "@webiny/app-headless-cms/presentation/contentEntries/list/abstractions.js";
|
|
3
|
+
import { ListLockRecordsPresenter } from "./abstractions.js";
|
|
4
|
+
class ContentEntriesPresenterWithLocking {
|
|
5
|
+
constructor(lockRecordsPresenter, original){
|
|
6
|
+
this.lockRecordsPresenter = lockRecordsPresenter;
|
|
7
|
+
this.original = original;
|
|
8
|
+
this._disposeReaction = null;
|
|
9
|
+
}
|
|
10
|
+
get vm() {
|
|
11
|
+
return this.original.vm;
|
|
12
|
+
}
|
|
13
|
+
get list() {
|
|
14
|
+
return this.original.list;
|
|
15
|
+
}
|
|
16
|
+
get folders() {
|
|
17
|
+
return this.original.folders;
|
|
18
|
+
}
|
|
19
|
+
init(config) {
|
|
20
|
+
this.original.init(config);
|
|
21
|
+
this._disposeReaction = reaction(()=>this.original.list.vm.rows.map((r)=>r.id), (entryIds)=>{
|
|
22
|
+
if (0 === entryIds.length) return;
|
|
23
|
+
this.lockRecordsPresenter.fetchForEntries(entryIds, this.original.vm.model.modelId);
|
|
24
|
+
}, {
|
|
25
|
+
fireImmediately: true,
|
|
26
|
+
equals: comparer.structural
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
dispose() {
|
|
30
|
+
if (this._disposeReaction) {
|
|
31
|
+
this._disposeReaction();
|
|
32
|
+
this._disposeReaction = null;
|
|
33
|
+
}
|
|
34
|
+
this.lockRecordsPresenter.dispose();
|
|
35
|
+
this.original.dispose();
|
|
36
|
+
}
|
|
37
|
+
selectEntry(id) {
|
|
38
|
+
this.original.selectEntry(id);
|
|
39
|
+
}
|
|
40
|
+
deselectEntry() {
|
|
41
|
+
this.original.deselectEntry();
|
|
42
|
+
}
|
|
43
|
+
createEntry() {
|
|
44
|
+
this.original.createEntry();
|
|
45
|
+
}
|
|
46
|
+
deleteEntry(id) {
|
|
47
|
+
return this.original.deleteEntry(id);
|
|
48
|
+
}
|
|
49
|
+
publishEntry(id) {
|
|
50
|
+
return this.original.publishEntry(id);
|
|
51
|
+
}
|
|
52
|
+
unpublishEntry(id) {
|
|
53
|
+
return this.original.unpublishEntry(id);
|
|
54
|
+
}
|
|
55
|
+
moveEntry(id, folderId) {
|
|
56
|
+
return this.original.moveEntry(id, folderId);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const ContentEntriesPresenterLockingDecorator = ContentEntriesPresenter.createDecorator({
|
|
60
|
+
decorator: ContentEntriesPresenterWithLocking,
|
|
61
|
+
dependencies: [
|
|
62
|
+
ListLockRecordsPresenter
|
|
63
|
+
]
|
|
64
|
+
});
|
|
65
|
+
export { ContentEntriesPresenterLockingDecorator };
|
|
66
|
+
|
|
67
|
+
//# sourceMappingURL=ContentEntriesPresenterDecorator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation/listLocking/ContentEntriesPresenterDecorator.js","sources":["../../../src/presentation/listLocking/ContentEntriesPresenterDecorator.ts"],"sourcesContent":["import { comparer, reaction } from \"mobx\";\nimport type { IReactionDisposer } from \"mobx\";\nimport {\n ContentEntriesPresenter,\n type IContentEntriesPresenter,\n type IContentEntriesInitConfig\n} from \"@webiny/app-headless-cms/presentation/contentEntries/list/abstractions.js\";\nimport { ListLockRecordsPresenter } from \"./abstractions.js\";\n\nclass ContentEntriesPresenterWithLocking implements IContentEntriesPresenter {\n private _disposeReaction: IReactionDisposer | null = null;\n\n constructor(\n private lockRecordsPresenter: ListLockRecordsPresenter.Interface,\n private original: IContentEntriesPresenter\n ) {}\n\n get vm() {\n return this.original.vm;\n }\n\n get list() {\n return this.original.list;\n }\n\n get folders() {\n return this.original.folders;\n }\n\n init(config?: IContentEntriesInitConfig): void {\n this.original.init(config);\n\n this._disposeReaction = reaction(\n () => this.original.list.vm.rows.map(r => r.id),\n entryIds => {\n if (entryIds.length === 0) {\n return;\n }\n this.lockRecordsPresenter.fetchForEntries(entryIds, this.original.vm.model.modelId);\n },\n { fireImmediately: true, equals: comparer.structural }\n );\n }\n\n dispose(): void {\n if (this._disposeReaction) {\n this._disposeReaction();\n this._disposeReaction = null;\n }\n this.lockRecordsPresenter.dispose();\n this.original.dispose();\n }\n\n selectEntry(id: string) {\n this.original.selectEntry(id);\n }\n\n deselectEntry() {\n this.original.deselectEntry();\n }\n\n createEntry() {\n this.original.createEntry();\n }\n\n deleteEntry(id: string) {\n return this.original.deleteEntry(id);\n }\n\n publishEntry(id: string) {\n return this.original.publishEntry(id);\n }\n\n unpublishEntry(id: string) {\n return this.original.unpublishEntry(id);\n }\n\n moveEntry(id: string, folderId: string) {\n return this.original.moveEntry(id, folderId);\n }\n}\n\nexport const ContentEntriesPresenterLockingDecorator = ContentEntriesPresenter.createDecorator({\n decorator: ContentEntriesPresenterWithLocking,\n dependencies: [ListLockRecordsPresenter]\n});\n"],"names":["ContentEntriesPresenterWithLocking","lockRecordsPresenter","original","config","reaction","r","entryIds","comparer","id","folderId","ContentEntriesPresenterLockingDecorator","ContentEntriesPresenter","ListLockRecordsPresenter"],"mappings":";;;AASA,MAAMA;IAGF,YACYC,oBAAwD,EACxDC,QAAkC,CAC5C;aAFUD,oBAAoB,GAApBA;aACAC,QAAQ,GAARA;aAJJ,gBAAgB,GAA6B;IAKlD;IAEH,IAAI,KAAK;QACL,OAAO,IAAI,CAAC,QAAQ,CAAC,EAAE;IAC3B;IAEA,IAAI,OAAO;QACP,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI;IAC7B;IAEA,IAAI,UAAU;QACV,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO;IAChC;IAEA,KAAKC,MAAkC,EAAQ;QAC3C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAACA;QAEnB,IAAI,CAAC,gBAAgB,GAAGC,SACpB,IAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAACC,CAAAA,IAAKA,EAAE,EAAE,GAC9CC,CAAAA;YACI,IAAIA,AAAoB,MAApBA,SAAS,MAAM,EACf;YAEJ,IAAI,CAAC,oBAAoB,CAAC,eAAe,CAACA,UAAU,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO;QACtF,GACA;YAAE,iBAAiB;YAAM,QAAQC,SAAS,UAAU;QAAC;IAE7D;IAEA,UAAgB;QACZ,IAAI,IAAI,CAAC,gBAAgB,EAAE;YACvB,IAAI,CAAC,gBAAgB;YACrB,IAAI,CAAC,gBAAgB,GAAG;QAC5B;QACA,IAAI,CAAC,oBAAoB,CAAC,OAAO;QACjC,IAAI,CAAC,QAAQ,CAAC,OAAO;IACzB;IAEA,YAAYC,EAAU,EAAE;QACpB,IAAI,CAAC,QAAQ,CAAC,WAAW,CAACA;IAC9B;IAEA,gBAAgB;QACZ,IAAI,CAAC,QAAQ,CAAC,aAAa;IAC/B;IAEA,cAAc;QACV,IAAI,CAAC,QAAQ,CAAC,WAAW;IAC7B;IAEA,YAAYA,EAAU,EAAE;QACpB,OAAO,IAAI,CAAC,QAAQ,CAAC,WAAW,CAACA;IACrC;IAEA,aAAaA,EAAU,EAAE;QACrB,OAAO,IAAI,CAAC,QAAQ,CAAC,YAAY,CAACA;IACtC;IAEA,eAAeA,EAAU,EAAE;QACvB,OAAO,IAAI,CAAC,QAAQ,CAAC,cAAc,CAACA;IACxC;IAEA,UAAUA,EAAU,EAAEC,QAAgB,EAAE;QACpC,OAAO,IAAI,CAAC,QAAQ,CAAC,SAAS,CAACD,IAAIC;IACvC;AACJ;AAEO,MAAMC,0CAA0CC,wBAAwB,eAAe,CAAC;IAC3F,WAAWX;IACX,cAAc;QAACY;KAAyB;AAC5C"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ListLockRecordsUseCase } from "../../features/listLockRecords/abstractions.js";
|
|
2
|
+
import { type IListLockRecordsPresenter, type IListLockRecordsViewModel } from "./abstractions.js";
|
|
3
|
+
import type { IRecordLockingLockRecord } from "../../types.js";
|
|
4
|
+
declare class ListLockRecordsPresenterImpl implements IListLockRecordsPresenter {
|
|
5
|
+
private listLockRecordsUseCase;
|
|
6
|
+
private _lockMap;
|
|
7
|
+
constructor(listLockRecordsUseCase: ListLockRecordsUseCase.Interface);
|
|
8
|
+
get vm(): IListLockRecordsViewModel;
|
|
9
|
+
fetchForEntries(entryIds: string[], type: string): Promise<void>;
|
|
10
|
+
isLocked(entryId: string): boolean;
|
|
11
|
+
getLockRecord(entryId: string): IRecordLockingLockRecord | undefined;
|
|
12
|
+
dispose(): void;
|
|
13
|
+
}
|
|
14
|
+
export declare const ListLockRecordsPresenter: typeof ListLockRecordsPresenterImpl & {
|
|
15
|
+
__abstraction: import("@webiny/di").Abstraction<IListLockRecordsPresenter>;
|
|
16
|
+
};
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { computed, makeAutoObservable, observable, runInAction } from "mobx";
|
|
2
|
+
import { ListLockRecordsUseCase } from "../../features/listLockRecords/abstractions.js";
|
|
3
|
+
import { ListLockRecordsPresenter } from "./abstractions.js";
|
|
4
|
+
class ListLockRecordsPresenterImpl {
|
|
5
|
+
constructor(listLockRecordsUseCase){
|
|
6
|
+
this.listLockRecordsUseCase = listLockRecordsUseCase;
|
|
7
|
+
this._lockMap = observable.map();
|
|
8
|
+
makeAutoObservable(this, {
|
|
9
|
+
listLockRecordsUseCase: false,
|
|
10
|
+
vm: computed
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
get vm() {
|
|
14
|
+
return {
|
|
15
|
+
lockedCount: this._lockMap.size
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
async fetchForEntries(entryIds, type) {
|
|
19
|
+
if (0 === entryIds.length) return;
|
|
20
|
+
try {
|
|
21
|
+
const records = await this.listLockRecordsUseCase.execute({
|
|
22
|
+
where: {
|
|
23
|
+
id_in: entryIds,
|
|
24
|
+
type
|
|
25
|
+
},
|
|
26
|
+
limit: entryIds.length
|
|
27
|
+
});
|
|
28
|
+
runInAction(()=>{
|
|
29
|
+
this._lockMap.clear();
|
|
30
|
+
for (const record of records)this._lockMap.set(record.targetId, record);
|
|
31
|
+
});
|
|
32
|
+
} catch {}
|
|
33
|
+
}
|
|
34
|
+
isLocked(entryId) {
|
|
35
|
+
return this._lockMap.has(entryId);
|
|
36
|
+
}
|
|
37
|
+
getLockRecord(entryId) {
|
|
38
|
+
return this._lockMap.get(entryId);
|
|
39
|
+
}
|
|
40
|
+
dispose() {
|
|
41
|
+
this._lockMap.clear();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const ListLockRecordsPresenter_ListLockRecordsPresenter = ListLockRecordsPresenter.createImplementation({
|
|
45
|
+
implementation: ListLockRecordsPresenterImpl,
|
|
46
|
+
dependencies: [
|
|
47
|
+
ListLockRecordsUseCase
|
|
48
|
+
]
|
|
49
|
+
});
|
|
50
|
+
export { ListLockRecordsPresenter_ListLockRecordsPresenter as ListLockRecordsPresenter };
|
|
51
|
+
|
|
52
|
+
//# sourceMappingURL=ListLockRecordsPresenter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation/listLocking/ListLockRecordsPresenter.js","sources":["../../../src/presentation/listLocking/ListLockRecordsPresenter.ts"],"sourcesContent":["import { computed, makeAutoObservable, observable, runInAction } from \"mobx\";\nimport { ListLockRecordsUseCase } from \"~/features/listLockRecords/abstractions.js\";\nimport {\n ListLockRecordsPresenter as Abstraction,\n type IListLockRecordsPresenter,\n type IListLockRecordsViewModel\n} from \"./abstractions.js\";\nimport type { IRecordLockingLockRecord } from \"~/types.js\";\n\nclass ListLockRecordsPresenterImpl implements IListLockRecordsPresenter {\n private _lockMap = observable.map<string, IRecordLockingLockRecord>();\n\n constructor(private listLockRecordsUseCase: ListLockRecordsUseCase.Interface) {\n makeAutoObservable<ListLockRecordsPresenterImpl, \"listLockRecordsUseCase\">(this, {\n listLockRecordsUseCase: false,\n vm: computed\n });\n }\n\n get vm(): IListLockRecordsViewModel {\n return {\n lockedCount: this._lockMap.size\n };\n }\n\n async fetchForEntries(entryIds: string[], type: string): Promise<void> {\n if (entryIds.length === 0) {\n return;\n }\n\n try {\n const records = await this.listLockRecordsUseCase.execute({\n where: { id_in: entryIds, type },\n limit: entryIds.length\n });\n\n runInAction(() => {\n this._lockMap.clear();\n for (const record of records) {\n this._lockMap.set(record.targetId, record);\n }\n });\n } catch {\n // Silently fail — list still works without lock indicators\n }\n }\n\n isLocked(entryId: string): boolean {\n return this._lockMap.has(entryId);\n }\n\n getLockRecord(entryId: string): IRecordLockingLockRecord | undefined {\n return this._lockMap.get(entryId);\n }\n\n dispose(): void {\n this._lockMap.clear();\n }\n}\n\nexport const ListLockRecordsPresenter = Abstraction.createImplementation({\n implementation: ListLockRecordsPresenterImpl,\n dependencies: [ListLockRecordsUseCase]\n});\n"],"names":["ListLockRecordsPresenterImpl","listLockRecordsUseCase","observable","makeAutoObservable","computed","entryIds","type","records","runInAction","record","entryId","ListLockRecordsPresenter","Abstraction","ListLockRecordsUseCase"],"mappings":";;;AASA,MAAMA;IAGF,YAAoBC,sBAAwD,CAAE;aAA1DA,sBAAsB,GAAtBA;aAFZ,QAAQ,GAAGC,WAAW,GAAG;QAG7BC,mBAA2E,IAAI,EAAE;YAC7E,wBAAwB;YACxB,IAAIC;QACR;IACJ;IAEA,IAAI,KAAgC;QAChC,OAAO;YACH,aAAa,IAAI,CAAC,QAAQ,CAAC,IAAI;QACnC;IACJ;IAEA,MAAM,gBAAgBC,QAAkB,EAAEC,IAAY,EAAiB;QACnE,IAAID,AAAoB,MAApBA,SAAS,MAAM,EACf;QAGJ,IAAI;YACA,MAAME,UAAU,MAAM,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC;gBACtD,OAAO;oBAAE,OAAOF;oBAAUC;gBAAK;gBAC/B,OAAOD,SAAS,MAAM;YAC1B;YAEAG,YAAY;gBACR,IAAI,CAAC,QAAQ,CAAC,KAAK;gBACnB,KAAK,MAAMC,UAAUF,QACjB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAACE,OAAO,QAAQ,EAAEA;YAE3C;QACJ,EAAE,OAAM,CAER;IACJ;IAEA,SAASC,OAAe,EAAW;QAC/B,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAACA;IAC7B;IAEA,cAAcA,OAAe,EAAwC;QACjE,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAACA;IAC7B;IAEA,UAAgB;QACZ,IAAI,CAAC,QAAQ,CAAC,KAAK;IACvB;AACJ;AAEO,MAAMC,oDAA2BC,yBAAAA,oBAAgC,CAAC;IACrE,gBAAgBZ;IAChB,cAAc;QAACa;KAAuB;AAC1C"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { IRecordLockingLockRecord } from "../../types.js";
|
|
2
|
+
export interface IListLockRecordsViewModel {
|
|
3
|
+
lockedCount: number;
|
|
4
|
+
}
|
|
5
|
+
export interface IListLockRecordsPresenter {
|
|
6
|
+
readonly vm: IListLockRecordsViewModel;
|
|
7
|
+
fetchForEntries(entryIds: string[], type: string): Promise<void>;
|
|
8
|
+
isLocked(entryId: string): boolean;
|
|
9
|
+
getLockRecord(entryId: string): IRecordLockingLockRecord | undefined;
|
|
10
|
+
dispose(): void;
|
|
11
|
+
}
|
|
12
|
+
export declare const ListLockRecordsPresenter: import("@webiny/di").Abstraction<IListLockRecordsPresenter>;
|
|
13
|
+
export declare namespace ListLockRecordsPresenter {
|
|
14
|
+
type Interface = IListLockRecordsPresenter;
|
|
15
|
+
type ViewModel = IListLockRecordsViewModel;
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation/listLocking/abstractions.js","sources":["../../../src/presentation/listLocking/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/admin\";\nimport type { IRecordLockingLockRecord } from \"~/types.js\";\n\nexport interface IListLockRecordsViewModel {\n lockedCount: number;\n}\n\nexport interface IListLockRecordsPresenter {\n readonly vm: IListLockRecordsViewModel;\n fetchForEntries(entryIds: string[], type: string): Promise<void>;\n isLocked(entryId: string): boolean;\n getLockRecord(entryId: string): IRecordLockingLockRecord | undefined;\n dispose(): void;\n}\n\nexport const ListLockRecordsPresenter = createAbstraction<IListLockRecordsPresenter>(\n \"ListLockRecordsPresenter\"\n);\n\nexport namespace ListLockRecordsPresenter {\n export type Interface = IListLockRecordsPresenter;\n export type ViewModel = IListLockRecordsViewModel;\n}\n"],"names":["ListLockRecordsPresenter","createAbstraction"],"mappings":";AAeO,MAAMA,2BAA2BC,kBACpC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import react, { useEffect } from "react";
|
|
2
|
+
import { observer } from "mobx-react-lite";
|
|
3
|
+
import { useContainer } from "@webiny/app";
|
|
4
|
+
import { useContentEntriesPresenter } from "@webiny/app-headless-cms/presentation/contentEntries/list/useContentEntriesPresenter.js";
|
|
5
|
+
import { ListLockRecordsPresenter } from "../abstractions.js";
|
|
6
|
+
const ListLockRecordsSync = observer(()=>{
|
|
7
|
+
const container = useContainer();
|
|
8
|
+
const presenter = react.useMemo(()=>container.resolve(ListLockRecordsPresenter), [
|
|
9
|
+
container
|
|
10
|
+
]);
|
|
11
|
+
const entriesPresenter = useContentEntriesPresenter();
|
|
12
|
+
const rows = entriesPresenter.list.vm.rows;
|
|
13
|
+
const modelId = entriesPresenter.vm.model.modelId;
|
|
14
|
+
useEffect(()=>{
|
|
15
|
+
if (0 === rows.length) return;
|
|
16
|
+
const entryIds = rows.map((row)=>row.id);
|
|
17
|
+
presenter.fetchForEntries(entryIds, modelId);
|
|
18
|
+
}, [
|
|
19
|
+
rows,
|
|
20
|
+
modelId
|
|
21
|
+
]);
|
|
22
|
+
return null;
|
|
23
|
+
});
|
|
24
|
+
export { ListLockRecordsSync };
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=ListLockRecordsSync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation/listLocking/components/ListLockRecordsSync.js","sources":["../../../../src/presentation/listLocking/components/ListLockRecordsSync.tsx"],"sourcesContent":["import React, { useEffect } from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport { useContainer } from \"@webiny/app\";\nimport { useContentEntriesPresenter } from \"@webiny/app-headless-cms/presentation/contentEntries/list/useContentEntriesPresenter.js\";\nimport { ListLockRecordsPresenter } from \"../abstractions.js\";\n\nexport const ListLockRecordsSync = observer(() => {\n const container = useContainer();\n const presenter = React.useMemo(() => container.resolve(ListLockRecordsPresenter), [container]);\n const entriesPresenter = useContentEntriesPresenter();\n\n const rows = entriesPresenter.list.vm.rows;\n const modelId = entriesPresenter.vm.model.modelId;\n\n useEffect(() => {\n if (rows.length === 0) {\n return;\n }\n\n const entryIds = rows.map(row => row.id);\n presenter.fetchForEntries(entryIds, modelId);\n }, [rows, modelId]);\n\n return null;\n});\n"],"names":["ListLockRecordsSync","observer","container","useContainer","presenter","React","ListLockRecordsPresenter","entriesPresenter","useContentEntriesPresenter","rows","modelId","useEffect","entryIds","row"],"mappings":";;;;;AAMO,MAAMA,sBAAsBC,SAAS;IACxC,MAAMC,YAAYC;IAClB,MAAMC,YAAYC,MAAAA,OAAa,CAAC,IAAMH,UAAU,OAAO,CAACI,2BAA2B;QAACJ;KAAU;IAC9F,MAAMK,mBAAmBC;IAEzB,MAAMC,OAAOF,iBAAiB,IAAI,CAAC,EAAE,CAAC,IAAI;IAC1C,MAAMG,UAAUH,iBAAiB,EAAE,CAAC,KAAK,CAAC,OAAO;IAEjDI,UAAU;QACN,IAAIF,AAAgB,MAAhBA,KAAK,MAAM,EACX;QAGJ,MAAMG,WAAWH,KAAK,GAAG,CAACI,CAAAA,MAAOA,IAAI,EAAE;QACvCT,UAAU,eAAe,CAACQ,UAAUF;IACxC,GAAG;QAACD;QAAMC;KAAQ;IAElB,OAAO;AACX"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import react from "react";
|
|
2
|
+
import { observer } from "mobx-react-lite";
|
|
3
|
+
import { useContainer } from "@webiny/app";
|
|
4
|
+
import { ReactComponent } from "@webiny/icons/lock.svg";
|
|
5
|
+
import { Icon, Tooltip } from "@webiny/admin-ui";
|
|
6
|
+
import { ContentEntryListConfig, useModel } from "@webiny/app-headless-cms";
|
|
7
|
+
import { ListLockRecordsPresenter } from "../abstractions.js";
|
|
8
|
+
const { Browser: Browser } = ContentEntryListConfig;
|
|
9
|
+
const ActionsCell = observer(({ children })=>{
|
|
10
|
+
const { model } = useModel();
|
|
11
|
+
const container = useContainer();
|
|
12
|
+
const presenter = react.useMemo(()=>container.resolve(ListLockRecordsPresenter), [
|
|
13
|
+
container
|
|
14
|
+
]);
|
|
15
|
+
const { useTableRow, isFolderRow } = Browser.Table.Column;
|
|
16
|
+
const { row } = useTableRow();
|
|
17
|
+
if (isFolderRow(row)) return /*#__PURE__*/ react.createElement(react.Fragment, null, children);
|
|
18
|
+
const lockRecord = presenter.getLockRecord(row.id);
|
|
19
|
+
if (!lockRecord) return /*#__PURE__*/ react.createElement(react.Fragment, null, children);
|
|
20
|
+
return /*#__PURE__*/ react.createElement(Tooltip, {
|
|
21
|
+
side: "left",
|
|
22
|
+
content: `This ${model.name} is currently locked by ${lockRecord.lockedBy.displayName}.`,
|
|
23
|
+
trigger: /*#__PURE__*/ react.createElement(Icon, {
|
|
24
|
+
icon: /*#__PURE__*/ react.createElement(ReactComponent, null),
|
|
25
|
+
label: "Locked entry",
|
|
26
|
+
color: "neutral-light"
|
|
27
|
+
})
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
const RecordLockingCellActionsDecorator = Browser.Table.Column.createDecorator((Original)=>function(props) {
|
|
31
|
+
if ("actions" === props.name && props.cell) return /*#__PURE__*/ react.createElement(Original, {
|
|
32
|
+
...props,
|
|
33
|
+
cell: /*#__PURE__*/ react.createElement(ActionsCell, null, props.cell)
|
|
34
|
+
});
|
|
35
|
+
return /*#__PURE__*/ react.createElement(Original, props);
|
|
36
|
+
});
|
|
37
|
+
export { RecordLockingCellActionsDecorator };
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=LockIndicatorCell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation/listLocking/components/LockIndicatorCell.js","sources":["../../../../src/presentation/listLocking/components/LockIndicatorCell.tsx"],"sourcesContent":["import React from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport { useContainer } from \"@webiny/app\";\nimport { ReactComponent as LockedIcon } from \"@webiny/icons/lock.svg\";\nimport { Icon, Tooltip } from \"@webiny/admin-ui\";\nimport { useModel, ContentEntryListConfig } from \"@webiny/app-headless-cms\";\nimport { ListLockRecordsPresenter } from \"../abstractions.js\";\n\nconst { Browser } = ContentEntryListConfig;\n\nconst ActionsCell = observer(({ children }: { children: React.ReactNode }) => {\n const { model } = useModel();\n const container = useContainer();\n const presenter = React.useMemo(() => container.resolve(ListLockRecordsPresenter), [container]);\n const { useTableRow, isFolderRow } = Browser.Table.Column;\n const { row } = useTableRow();\n\n if (isFolderRow(row)) {\n return <>{children}</>;\n }\n\n const lockRecord = presenter.getLockRecord(row.id);\n\n if (!lockRecord) {\n return <>{children}</>;\n }\n\n return (\n <Tooltip\n side={\"left\"}\n content={`This ${model.name} is currently locked by ${lockRecord.lockedBy.displayName}.`}\n trigger={<Icon icon={<LockedIcon />} label={\"Locked entry\"} color={\"neutral-light\"} />}\n />\n );\n});\n\nexport const RecordLockingCellActionsDecorator = 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"],"names":["Browser","ContentEntryListConfig","ActionsCell","observer","children","model","useModel","container","useContainer","presenter","React","ListLockRecordsPresenter","useTableRow","isFolderRow","row","lockRecord","Tooltip","Icon","LockedIcon","RecordLockingCellActionsDecorator","Original","props"],"mappings":";;;;;;;AAQA,MAAM,EAAEA,SAAAA,OAAO,EAAE,GAAGC;AAEpB,MAAMC,cAAcC,SAAS,CAAC,EAAEC,QAAQ,EAAiC;IACrE,MAAM,EAAEC,KAAK,EAAE,GAAGC;IAClB,MAAMC,YAAYC;IAClB,MAAMC,YAAYC,MAAAA,OAAa,CAAC,IAAMH,UAAU,OAAO,CAACI,2BAA2B;QAACJ;KAAU;IAC9F,MAAM,EAAEK,WAAW,EAAEC,WAAW,EAAE,GAAGb,QAAQ,KAAK,CAAC,MAAM;IACzD,MAAM,EAAEc,GAAG,EAAE,GAAGF;IAEhB,IAAIC,YAAYC,MACZ,OAAO,WAAP,GAAO,0CAAGV;IAGd,MAAMW,aAAaN,UAAU,aAAa,CAACK,IAAI,EAAE;IAEjD,IAAI,CAACC,YACD,OAAO,WAAP,GAAO,0CAAGX;IAGd,OAAO,WAAP,GACI,oBAACY,SAAOA;QACJ,MAAM;QACN,SAAS,CAAC,KAAK,EAAEX,MAAM,IAAI,CAAC,wBAAwB,EAAEU,WAAW,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;QACxF,uBAAS,oBAACE,MAAIA;YAAC,oBAAM,oBAACC,gBAAUA;YAAK,OAAO;YAAgB,OAAO;;;AAG/E;AAEO,MAAMC,oCAAoCnB,QAAQ,KAAK,CAAC,MAAM,CAAC,eAAe,CAACoB,CAAAA,WAC3E,SAAkCC,KAAK;QAC1C,IAAIA,AAAe,cAAfA,MAAM,IAAI,IAAkBA,MAAM,IAAI,EACtC,OAAO,WAAP,GAAO,oBAACD,UAAAA;YAAU,GAAGC,KAAK;YAAE,oBAAM,oBAACnB,aAAWA,MAAEmB,MAAM,IAAI;;QAG9D,OAAO,WAAP,GAAO,oBAACD,UAAaC;IACzB"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createFeature } from "@webiny/feature/admin";
|
|
2
|
+
import { ListLockRecordsPresenter } from "./abstractions.js";
|
|
3
|
+
import { ListLockRecordsPresenter as external_ListLockRecordsPresenter_js_ListLockRecordsPresenter } from "./ListLockRecordsPresenter.js";
|
|
4
|
+
import { ContentEntriesPresenterLockingDecorator } from "./ContentEntriesPresenterDecorator.js";
|
|
5
|
+
const ListLockRecordsPresenterFeature = createFeature({
|
|
6
|
+
name: "RecordLocking/ListLockRecordsPresenter",
|
|
7
|
+
register (container) {
|
|
8
|
+
container.register(external_ListLockRecordsPresenter_js_ListLockRecordsPresenter).inSingletonScope();
|
|
9
|
+
container.registerDecorator(ContentEntriesPresenterLockingDecorator);
|
|
10
|
+
},
|
|
11
|
+
resolve (container) {
|
|
12
|
+
return {
|
|
13
|
+
presenter: container.resolve(ListLockRecordsPresenter)
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
export { ListLockRecordsPresenterFeature };
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=feature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation/listLocking/feature.js","sources":["../../../src/presentation/listLocking/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/admin\";\nimport { ListLockRecordsPresenter as Abstraction } from \"./abstractions.js\";\nimport { ListLockRecordsPresenter } from \"./ListLockRecordsPresenter.js\";\nimport { ContentEntriesPresenterLockingDecorator } from \"./ContentEntriesPresenterDecorator.js\";\n\nexport const ListLockRecordsPresenterFeature = createFeature({\n name: \"RecordLocking/ListLockRecordsPresenter\",\n register(container) {\n container.register(ListLockRecordsPresenter).inSingletonScope();\n container.registerDecorator(ContentEntriesPresenterLockingDecorator);\n },\n resolve(container) {\n return {\n presenter: container.resolve(Abstraction)\n };\n }\n});\n"],"names":["ListLockRecordsPresenterFeature","createFeature","container","ListLockRecordsPresenter","ContentEntriesPresenterLockingDecorator","Abstraction"],"mappings":";;;;AAKO,MAAMA,kCAAkCC,cAAc;IACzD,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC,+DAA0B,gBAAgB;QAC7DD,UAAU,iBAAiB,CAACE;IAChC;IACA,SAAQF,SAAS;QACb,OAAO;YACH,WAAWA,UAAU,OAAO,CAACG;QACjC;IACJ;AACJ"}
|
package/types.d.ts
CHANGED
|
@@ -1,67 +1,10 @@
|
|
|
1
|
-
import type { CmsContentEntry } from "@webiny/app-headless-cms/types.js";
|
|
2
1
|
import type { GenericRecord } from "@webiny/app/types.js";
|
|
3
|
-
import type { IRecordLockingUnlockEntryResult } from "./domain/abstractions/IRecordLockingUnlockEntry.js";
|
|
4
|
-
import type { IRecordLockingUpdateEntryLockResult } from "./domain/abstractions/IRecordLocking.js";
|
|
5
2
|
import { Identity } from "@webiny/app-admin/domain/Identity.js";
|
|
6
3
|
export interface IRecordLockingIdentity {
|
|
7
4
|
id: string;
|
|
8
5
|
displayName: string;
|
|
9
6
|
type: string;
|
|
10
7
|
}
|
|
11
|
-
export interface IRecordLockingRecordLocked {
|
|
12
|
-
lockedBy: IRecordLockingIdentity;
|
|
13
|
-
lockedOn: string;
|
|
14
|
-
expiresOn: string;
|
|
15
|
-
actions: IRecordLockingLockRecordAction[];
|
|
16
|
-
}
|
|
17
|
-
export interface IRecordLockingCmsEntryValuesAction {
|
|
18
|
-
type: string;
|
|
19
|
-
message: string;
|
|
20
|
-
createdBy: Identity;
|
|
21
|
-
createdOn: string;
|
|
22
|
-
}
|
|
23
|
-
export interface IRecordLockingCmsEntryValues {
|
|
24
|
-
lockedBy: Identity;
|
|
25
|
-
lockedOn: string;
|
|
26
|
-
updatedOn: string;
|
|
27
|
-
expiresOn: string;
|
|
28
|
-
targetId: string;
|
|
29
|
-
type: string;
|
|
30
|
-
actions: IRecordLockingCmsEntryValuesAction;
|
|
31
|
-
}
|
|
32
|
-
export interface IPossiblyRecordLockingRecord extends CmsContentEntry<IRecordLockingCmsEntryValues> {
|
|
33
|
-
$selectable?: boolean;
|
|
34
|
-
$type?: "RECORD";
|
|
35
|
-
$lockingType?: string;
|
|
36
|
-
$locked?: IRecordLockingRecordLocked | null;
|
|
37
|
-
}
|
|
38
|
-
export interface IRecordLockingRecord extends IPossiblyRecordLockingRecord {
|
|
39
|
-
$lockingType: string;
|
|
40
|
-
}
|
|
41
|
-
export type IIsRecordLockedParams = Pick<IRecordLockingRecord, "id" | "$lockingType">;
|
|
42
|
-
export type IUpdateEntryLockParams = Pick<IRecordLockingRecord, "id" | "$lockingType">;
|
|
43
|
-
export type IUnlockEntryParams = Pick<IRecordLockingRecord, "id" | "$lockingType">;
|
|
44
|
-
export type IFetchLockRecordParams = Pick<IRecordLockingRecord, "id" | "$lockingType">;
|
|
45
|
-
export type IFetchLockedEntryLockRecordParams = Pick<IRecordLockingRecord, "id" | "$lockingType">;
|
|
46
|
-
export interface IFetchLockRecordResult {
|
|
47
|
-
data: IRecordLockingLockRecord | null;
|
|
48
|
-
error: IRecordLockingError | null;
|
|
49
|
-
}
|
|
50
|
-
export interface IRecordLockingContext<T extends IPossiblyRecordLockingRecord = IPossiblyRecordLockingRecord> {
|
|
51
|
-
readonly loading: boolean;
|
|
52
|
-
readonly records: IPossiblyRecordLockingRecord[];
|
|
53
|
-
readonly error?: IRecordLockingError | null;
|
|
54
|
-
setRecords(folderId: string, type: string, records: T[]): Promise<void>;
|
|
55
|
-
updateEntryLock(params: IUpdateEntryLockParams): Promise<IRecordLockingUpdateEntryLockResult>;
|
|
56
|
-
isRecordLocked(params?: IIsRecordLockedParams): boolean;
|
|
57
|
-
getLockRecordEntry(id: string): IRecordLockingRecord | undefined;
|
|
58
|
-
fetchLockRecord(params: IFetchLockRecordParams): Promise<IFetchLockRecordResult>;
|
|
59
|
-
fetchLockedEntryLockRecord(params: IFetchLockedEntryLockRecordParams): Promise<IRecordLockingLockRecord | null>;
|
|
60
|
-
unlockEntry(params: IUnlockEntryParams): Promise<IRecordLockingUnlockEntryResult>;
|
|
61
|
-
removeEntryLock(params: IUnlockEntryParams): void;
|
|
62
|
-
unlockEntryForce(params: IUnlockEntryParams): Promise<IRecordLockingUnlockEntryResult>;
|
|
63
|
-
isLockExpired(input: Date | string): boolean;
|
|
64
|
-
}
|
|
65
8
|
export interface IRecordLockingLockRecordAction {
|
|
66
9
|
type: string;
|
|
67
10
|
message: string;
|
|
@@ -71,17 +14,13 @@ export interface IRecordLockingLockRecordAction {
|
|
|
71
14
|
export interface IRecordLockingLockRecord {
|
|
72
15
|
id: string;
|
|
73
16
|
lockedOn: string;
|
|
17
|
+
updatedOn: string;
|
|
74
18
|
expiresOn: string;
|
|
75
19
|
lockedBy: IRecordLockingIdentity;
|
|
76
20
|
targetId: string;
|
|
77
21
|
type: string;
|
|
78
22
|
actions: IRecordLockingLockRecordAction[];
|
|
79
23
|
}
|
|
80
|
-
export interface IRecordLockingMeta {
|
|
81
|
-
totalCount: number;
|
|
82
|
-
cursor: string | null;
|
|
83
|
-
hasMoreItems: boolean;
|
|
84
|
-
}
|
|
85
24
|
export interface IRecordLockingError<T = GenericRecord> {
|
|
86
25
|
message: string;
|
|
87
26
|
code: string;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import react from "react";
|
|
2
|
-
import { ContentEntryListConfig, useModel } from "@webiny/app-headless-cms";
|
|
3
|
-
import { ReactComponent } from "@webiny/icons/lock.svg";
|
|
4
|
-
import { useRecordLocking } from "../hooks/index.js";
|
|
5
|
-
import { UseContentEntriesListHookDecorator } from "./decorators/UseContentEntriesListHookDecorator.js";
|
|
6
|
-
import { UseSaveEntryDecorator } from "./decorators/UseSaveEntryDecorator.js";
|
|
7
|
-
import { UseRecordsDecorator } from "./decorators/UseRecordsDecorator.js";
|
|
8
|
-
import { Icon, Tooltip } from "@webiny/admin-ui";
|
|
9
|
-
const { Browser: Browser } = ContentEntryListConfig;
|
|
10
|
-
const ActionsCell = ({ children })=>{
|
|
11
|
-
const { model } = useModel();
|
|
12
|
-
const { getLockRecordEntry, isRecordLocked } = useRecordLocking();
|
|
13
|
-
const { useTableRow, isFolderRow } = Browser.Table.Column;
|
|
14
|
-
const { row } = useTableRow();
|
|
15
|
-
if (isFolderRow(row)) return /*#__PURE__*/ react.createElement(react.Fragment, null, children);
|
|
16
|
-
const entry = getLockRecordEntry(row.id);
|
|
17
|
-
if (!isRecordLocked(entry) || !entry?.$locked) return /*#__PURE__*/ react.createElement(react.Fragment, null, children);
|
|
18
|
-
return /*#__PURE__*/ react.createElement(Tooltip, {
|
|
19
|
-
side: "left",
|
|
20
|
-
content: `This ${model.name} is currently locked by ${entry.$locked.lockedBy.displayName}.`,
|
|
21
|
-
trigger: /*#__PURE__*/ react.createElement(Icon, {
|
|
22
|
-
icon: /*#__PURE__*/ react.createElement(ReactComponent, null),
|
|
23
|
-
label: "Locked entry",
|
|
24
|
-
color: "neutral-light"
|
|
25
|
-
})
|
|
26
|
-
});
|
|
27
|
-
};
|
|
28
|
-
const HeadlessCmsActionsAcoCell_RecordLockingCellActions = Browser.Table.Column.createDecorator((Original)=>function(props) {
|
|
29
|
-
if ("actions" === props.name && props.cell) return /*#__PURE__*/ react.createElement(Original, {
|
|
30
|
-
...props,
|
|
31
|
-
cell: /*#__PURE__*/ react.createElement(ActionsCell, null, props.cell)
|
|
32
|
-
});
|
|
33
|
-
return /*#__PURE__*/ react.createElement(Original, props);
|
|
34
|
-
});
|
|
35
|
-
const HeadlessCmsActionsAcoCell = ()=>/*#__PURE__*/ react.createElement(react.Fragment, null, /*#__PURE__*/ react.createElement(UseContentEntriesListHookDecorator, null), /*#__PURE__*/ react.createElement(UseSaveEntryDecorator, null), /*#__PURE__*/ react.createElement(HeadlessCmsActionsAcoCell_RecordLockingCellActions, null), /*#__PURE__*/ react.createElement(UseRecordsDecorator, null));
|
|
36
|
-
export { HeadlessCmsActionsAcoCell };
|
|
37
|
-
|
|
38
|
-
//# sourceMappingURL=HeadlessCmsActionsAcoCell.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"components/HeadlessCmsActionsAcoCell.js","sources":["../../src/components/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/index.js\";\nimport { UseContentEntriesListHookDecorator } from \"./decorators/UseContentEntriesListHookDecorator.js\";\nimport { UseSaveEntryDecorator } from \"~/components/decorators/UseSaveEntryDecorator.js\";\nimport { UseRecordsDecorator } from \"./decorators/UseRecordsDecorator.js\";\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"],"names":["Browser","ContentEntryListConfig","ActionsCell","children","model","useModel","getLockRecordEntry","isRecordLocked","useRecordLocking","useTableRow","isFolderRow","row","entry","Tooltip","Icon","LockedIcon","RecordLockingCellActions","Original","props","HeadlessCmsActionsAcoCell","UseContentEntriesListHookDecorator","UseSaveEntryDecorator","UseRecordsDecorator"],"mappings":";;;;;;;;AASA,MAAM,EAAEA,SAAAA,OAAO,EAAE,GAAGC;AAMpB,MAAMC,cAAc,CAAC,EAAEC,QAAQ,EAAoB;IAC/C,MAAM,EAAEC,KAAK,EAAE,GAAGC;IAClB,MAAM,EAAEC,kBAAkB,EAAEC,cAAc,EAAE,GAAGC;IAE/C,MAAM,EAAEC,WAAW,EAAEC,WAAW,EAAE,GAAGV,QAAQ,KAAK,CAAC,MAAM;IACzD,MAAM,EAAEW,GAAG,EAAE,GAAGF;IAEhB,IAAIC,YAAYC,MACZ,OAAO,WAAP,GAAO,0CAAGR;IAGd,MAAMS,QAAQN,mBAAmBK,IAAI,EAAE;IAEvC,IAAI,CAACJ,eAAeK,UAAU,CAACA,OAAO,SAClC,OAAO,WAAP,GAAO,0CAAGT;IAEd,OAAO,WAAP,GACI,oBAACU,SAAOA;QACJ,MAAM;QACN,SAAS,CAAC,KAAK,EAAET,MAAM,IAAI,CAAC,wBAAwB,EAAEQ,MAAM,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;QAC3F,uBAAS,oBAACE,MAAIA;YAAC,oBAAM,oBAACC,gBAAUA;YAAK,OAAO;YAAgB,OAAO;;;AAG/E;AAEA,MAAMC,qDAA2BhB,QAAQ,KAAK,CAAC,MAAM,CAAC,eAAe,CAACiB,CAAAA,WAC3D,SAAkCC,KAAK;QAC1C,IAAIA,AAAe,cAAfA,MAAM,IAAI,IAAkBA,MAAM,IAAI,EACtC,OAAO,WAAP,GAAO,oBAACD,UAAAA;YAAU,GAAGC,KAAK;YAAE,oBAAM,oBAAChB,aAAWA,MAAEgB,MAAM,IAAI;;QAG9D,OAAO,WAAP,GAAO,oBAACD,UAAaC;IACzB;AAGG,MAAMC,4BAA4B,IAC9B,WAAP,GACI,wDACI,oBAACC,oCAAkCA,OAAAA,WAAAA,GACnC,oBAACC,uBAAqBA,OAAAA,WAAAA,GACtB,oBAACL,oDAAwBA,OAAAA,WAAAA,GACzB,oBAACM,qBAAmBA"}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import type { CmsContentEntry, CmsModel } from "@webiny/app-headless-cms/types.js";
|
|
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;
|