@webiny/app-record-locking 0.0.0-unstable.e6f0dc8ca7 → 0.0.0-unstable.eb81deee7e
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/SecurityPermissions.js +15 -0
- 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} +4 -3
- 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 +3 -2
- package/features/permissions/abstractions.js.map +1 -1
- package/features/permissions/feature.js +3 -2
- 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 +20 -27
- package/index.js.map +1 -1
- package/package.json +18 -20
- package/permissions/permissionsSchema.js +10 -0
- 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 +25 -0
- package/presentation/listLocking/ContentEntriesPresenterDecorator.js +70 -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 +4 -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/types.js +0 -3
- package/components/HeadlessCmsActionsAcoCell.d.ts +0 -2
- package/components/HeadlessCmsActionsAcoCell.js +0 -60
- package/components/HeadlessCmsActionsAcoCell.js.map +0 -1
- package/components/HeadlessCmsContentEntry/ContentEntryGuard.d.ts +0 -9
- package/components/HeadlessCmsContentEntry/ContentEntryGuard.js +0 -42
- package/components/HeadlessCmsContentEntry/ContentEntryGuard.js.map +0 -1
- package/components/HeadlessCmsContentEntry/ContentEntryLocker.d.ts +0 -18
- package/components/HeadlessCmsContentEntry/ContentEntryLocker.js +0 -99
- package/components/HeadlessCmsContentEntry/ContentEntryLocker.js.map +0 -1
- package/components/HeadlessCmsContentEntry/HeadlessCmsContentEntry.d.ts +0 -2
- package/components/HeadlessCmsContentEntry/HeadlessCmsContentEntry.js +0 -68
- package/components/HeadlessCmsContentEntry/HeadlessCmsContentEntry.js.map +0 -1
- package/components/HeadlessCmsContentEntry/index.d.ts +0 -1
- package/components/HeadlessCmsContentEntry/index.js +0 -3
- package/components/HeadlessCmsContentEntry/index.js.map +0 -1
- package/components/LockedRecord/LockedRecord.d.ts +0 -6
- package/components/LockedRecord/LockedRecord.js +0 -66
- package/components/LockedRecord/LockedRecord.js.map +0 -1
- package/components/LockedRecord/LockedRecordForceUnlock.d.ts +0 -9
- package/components/LockedRecord/LockedRecordForceUnlock.js +0 -71
- package/components/LockedRecord/LockedRecordForceUnlock.js.map +0 -1
- package/components/LockedRecord/index.d.ts +0 -1
- package/components/LockedRecord/index.js +0 -3
- package/components/LockedRecord/index.js.map +0 -1
- package/components/RecordLockingProvider.d.ts +0 -7
- package/components/RecordLockingProvider.js +0 -125
- package/components/RecordLockingProvider.js.map +0 -1
- package/components/SecurityPermissions.js +0 -18
- 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 -25
- package/components/decorators/UseRecordsDecorator.js.map +0 -1
- package/components/decorators/UseSaveEntryDecorator.d.ts +0 -1
- package/components/decorators/UseSaveEntryDecorator.js +0 -55
- package/components/decorators/UseSaveEntryDecorator.js.map +0 -1
- package/domain/RecordLocking.d.ts +0 -28
- package/domain/RecordLocking.js +0 -321
- package/domain/RecordLocking.js.map +0 -1
- package/domain/RecordLockingClient.d.ts +0 -12
- package/domain/RecordLockingClient.js +0 -19
- package/domain/RecordLockingClient.js.map +0 -1
- package/domain/RecordLockingGetLockRecord.d.ts +0 -11
- package/domain/RecordLockingGetLockRecord.js +0 -21
- package/domain/RecordLockingGetLockRecord.js.map +0 -1
- package/domain/RecordLockingGetLockedEntryLockRecord.d.ts +0 -11
- package/domain/RecordLockingGetLockedEntryLockRecord.js +0 -19
- package/domain/RecordLockingGetLockedEntryLockRecord.js.map +0 -1
- package/domain/RecordLockingIsEntryLocked.d.ts +0 -11
- package/domain/RecordLockingIsEntryLocked.js +0 -19
- package/domain/RecordLockingIsEntryLocked.js.map +0 -1
- package/domain/RecordLockingListLockRecords.d.ts +0 -12
- package/domain/RecordLockingListLockRecords.js +0 -31
- package/domain/RecordLockingListLockRecords.js.map +0 -1
- package/domain/RecordLockingLockEntry.d.ts +0 -11
- package/domain/RecordLockingLockEntry.js +0 -13
- package/domain/RecordLockingLockEntry.js.map +0 -1
- package/domain/RecordLockingUnlockEntry.d.ts +0 -11
- package/domain/RecordLockingUnlockEntry.js +0 -19
- package/domain/RecordLockingUnlockEntry.js.map +0 -1
- package/domain/RecordLockingUpdateEntryLock.d.ts +0 -11
- package/domain/RecordLockingUpdateEntryLock.js +0 -19
- package/domain/RecordLockingUpdateEntryLock.js.map +0 -1
- package/domain/abstractions/IRecordLocking.d.ts +0 -25
- package/domain/abstractions/IRecordLocking.js +0 -3
- package/domain/abstractions/IRecordLocking.js.map +0 -1
- package/domain/abstractions/IRecordLockingClient.d.ts +0 -6
- package/domain/abstractions/IRecordLockingClient.js +0 -3
- package/domain/abstractions/IRecordLockingClient.js.map +0 -1
- package/domain/abstractions/IRecordLockingGetLockRecord.d.ts +0 -12
- package/domain/abstractions/IRecordLockingGetLockRecord.js +0 -3
- package/domain/abstractions/IRecordLockingGetLockRecord.js.map +0 -1
- package/domain/abstractions/IRecordLockingGetLockedEntryLockRecord.d.ts +0 -12
- package/domain/abstractions/IRecordLockingGetLockedEntryLockRecord.js +0 -3
- package/domain/abstractions/IRecordLockingGetLockedEntryLockRecord.js.map +0 -1
- package/domain/abstractions/IRecordLockingIsEntryLocked.d.ts +0 -8
- package/domain/abstractions/IRecordLockingIsEntryLocked.js +0 -3
- package/domain/abstractions/IRecordLockingIsEntryLocked.js.map +0 -1
- package/domain/abstractions/IRecordLockingListLockRecords.d.ts +0 -19
- package/domain/abstractions/IRecordLockingListLockRecords.js +0 -3
- package/domain/abstractions/IRecordLockingListLockRecords.js.map +0 -1
- package/domain/abstractions/IRecordLockingLockEntry.d.ts +0 -12
- package/domain/abstractions/IRecordLockingLockEntry.js +0 -3
- package/domain/abstractions/IRecordLockingLockEntry.js.map +0 -1
- package/domain/abstractions/IRecordLockingUnlockEntry.d.ts +0 -13
- package/domain/abstractions/IRecordLockingUnlockEntry.js +0 -3
- package/domain/abstractions/IRecordLockingUnlockEntry.js.map +0 -1
- package/domain/abstractions/IRecordLockingUpdateEntryLock.d.ts +0 -12
- package/domain/abstractions/IRecordLockingUpdateEntryLock.js +0 -3
- package/domain/abstractions/IRecordLockingUpdateEntryLock.js.map +0 -1
- package/domain/graphql/fields.js.map +0 -1
- package/domain/graphql/getLockRecord.d.ts +0 -12
- package/domain/graphql/getLockRecord.js +0 -18
- package/domain/graphql/getLockRecord.js.map +0 -1
- package/domain/graphql/getLockedEntryLockRecord.d.ts +0 -12
- package/domain/graphql/getLockedEntryLockRecord.js +0 -18
- package/domain/graphql/getLockedEntryLockRecord.js.map +0 -1
- package/domain/graphql/isEntryLocked.d.ts +0 -12
- package/domain/graphql/isEntryLocked.js +0 -16
- package/domain/graphql/isEntryLocked.js.map +0 -1
- package/domain/graphql/listLockRecords.d.ts +0 -17
- package/domain/graphql/listLockRecords.js +0 -26
- package/domain/graphql/listLockRecords.js.map +0 -1
- package/domain/graphql/lockEntry.d.ts +0 -12
- package/domain/graphql/lockEntry.js +0 -20
- package/domain/graphql/lockEntry.js.map +0 -1
- package/domain/graphql/unlockEntry.d.ts +0 -12
- package/domain/graphql/unlockEntry.js +0 -18
- package/domain/graphql/unlockEntry.js.map +0 -1
- package/domain/graphql/updateEntryLock.d.ts +0 -12
- package/domain/graphql/updateEntryLock.js +0 -18
- package/domain/graphql/updateEntryLock.js.map +0 -1
- package/domain/permissionsSchema.js +0 -9
- package/domain/permissionsSchema.js.map +0 -1
- package/domain/utils/createRecordLockingClient.d.ts +0 -3
- package/domain/utils/createRecordLockingClient.js +0 -12
- package/domain/utils/createRecordLockingClient.js.map +0 -1
- package/domain/utils/createRecordLockingError.d.ts +0 -6
- package/domain/utils/createRecordLockingError.js +0 -16
- package/domain/utils/createRecordLockingError.js.map +0 -1
- package/hooks/index.d.ts +0 -2
- package/hooks/index.js +0 -4
- package/hooks/index.js.map +0 -1
- package/hooks/usePermission.d.ts +0 -3
- package/hooks/usePermission.js +0 -20
- package/hooks/usePermission.js.map +0 -1
- package/hooks/useRecordLocking.d.ts +0 -2
- package/hooks/useRecordLocking.js +0 -12
- package/hooks/useRecordLocking.js.map +0 -1
- package/types.js.map +0 -1
- package/utils/createCacheKey.d.ts +0 -3
- package/utils/createCacheKey.js +0 -15
- 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
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type IContentEntryFormPresenter, type IContentEntryFormViewModel } from "@webiny/app-headless-cms/presentation/contentEntries/form/abstractions.js";
|
|
2
|
+
import { RecordLockingPresenter } from "./abstractions.js";
|
|
3
|
+
declare class ContentEntryFormPresenterWithLocking implements IContentEntryFormPresenter {
|
|
4
|
+
private lockingPresenter;
|
|
5
|
+
private original;
|
|
6
|
+
constructor(lockingPresenter: RecordLockingPresenter.Interface, original: IContentEntryFormPresenter);
|
|
7
|
+
get vm(): IContentEntryFormViewModel;
|
|
8
|
+
saveRevision(options?: {
|
|
9
|
+
skipValidation?: boolean;
|
|
10
|
+
}): Promise<boolean>;
|
|
11
|
+
publishRevision(): Promise<boolean>;
|
|
12
|
+
unpublishRevision(): Promise<boolean>;
|
|
13
|
+
deleteEntry(): Promise<boolean>;
|
|
14
|
+
loadRevision(id: string): Promise<void>;
|
|
15
|
+
setFolderId(folderId: string | null): void;
|
|
16
|
+
newEntry(initialValues?: Record<string, unknown>): void;
|
|
17
|
+
patchEntryMeta(...args: Parameters<IContentEntryFormPresenter["patchEntryMeta"]>): void;
|
|
18
|
+
reset(): void;
|
|
19
|
+
}
|
|
20
|
+
export declare const ContentEntryFormPresenterLockingDecorator: typeof ContentEntryFormPresenterWithLocking & {
|
|
21
|
+
__abstraction: import("@webiny/di").Abstraction<IContentEntryFormPresenter>;
|
|
22
|
+
};
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { computed, makeAutoObservable } from "mobx";
|
|
2
|
+
import { ContentEntryFormPresenter } from "@webiny/app-headless-cms/presentation/contentEntries/form/abstractions.js";
|
|
3
|
+
import { RecordLockingPresenter } from "./abstractions.js";
|
|
4
|
+
class ContentEntryFormPresenterWithLocking {
|
|
5
|
+
constructor(lockingPresenter, original){
|
|
6
|
+
this.lockingPresenter = lockingPresenter;
|
|
7
|
+
this.original = original;
|
|
8
|
+
makeAutoObservable(this, {
|
|
9
|
+
lockingPresenter: false,
|
|
10
|
+
original: false,
|
|
11
|
+
vm: computed
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
get vm() {
|
|
15
|
+
const base = this.original.vm;
|
|
16
|
+
if (base.isNewEntry) return base;
|
|
17
|
+
const canEdit = this.lockingPresenter.vm.canEdit;
|
|
18
|
+
return {
|
|
19
|
+
...base,
|
|
20
|
+
canSave: base.canSave && canEdit,
|
|
21
|
+
canPublish: base.canPublish && canEdit,
|
|
22
|
+
canUnpublish: base.canUnpublish && canEdit,
|
|
23
|
+
canDelete: base.canDelete && canEdit
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
async saveRevision(options) {
|
|
27
|
+
const result = await this.original.saveRevision(options);
|
|
28
|
+
if (result) await this.lockingPresenter.refreshLock();
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
publishRevision() {
|
|
32
|
+
return this.original.publishRevision();
|
|
33
|
+
}
|
|
34
|
+
unpublishRevision() {
|
|
35
|
+
return this.original.unpublishRevision();
|
|
36
|
+
}
|
|
37
|
+
deleteEntry() {
|
|
38
|
+
return this.original.deleteEntry();
|
|
39
|
+
}
|
|
40
|
+
loadRevision(id) {
|
|
41
|
+
return this.original.loadRevision(id);
|
|
42
|
+
}
|
|
43
|
+
setFolderId(folderId) {
|
|
44
|
+
this.original.setFolderId(folderId);
|
|
45
|
+
}
|
|
46
|
+
newEntry(initialValues) {
|
|
47
|
+
return this.original.newEntry(initialValues);
|
|
48
|
+
}
|
|
49
|
+
patchEntryMeta(...args) {
|
|
50
|
+
this.original.patchEntryMeta(...args);
|
|
51
|
+
}
|
|
52
|
+
reset() {
|
|
53
|
+
return this.original.reset();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const ContentEntryFormPresenterLockingDecorator = ContentEntryFormPresenter.createDecorator({
|
|
57
|
+
decorator: ContentEntryFormPresenterWithLocking,
|
|
58
|
+
dependencies: [
|
|
59
|
+
RecordLockingPresenter
|
|
60
|
+
]
|
|
61
|
+
});
|
|
62
|
+
export { ContentEntryFormPresenterLockingDecorator };
|
|
63
|
+
|
|
64
|
+
//# sourceMappingURL=ContentEntryFormPresenterDecorator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation/entryLocking/ContentEntryFormPresenterDecorator.js","sources":["../../../src/presentation/entryLocking/ContentEntryFormPresenterDecorator.ts"],"sourcesContent":["import { computed, makeAutoObservable } from \"mobx\";\nimport {\n ContentEntryFormPresenter,\n type IContentEntryFormPresenter,\n type IContentEntryFormViewModel\n} from \"@webiny/app-headless-cms/presentation/contentEntries/form/abstractions.js\";\nimport { RecordLockingPresenter } from \"./abstractions.js\";\n\nclass ContentEntryFormPresenterWithLocking implements IContentEntryFormPresenter {\n constructor(\n private lockingPresenter: RecordLockingPresenter.Interface,\n private original: IContentEntryFormPresenter\n ) {\n makeAutoObservable<ContentEntryFormPresenterWithLocking, \"lockingPresenter\" | \"original\">(\n this,\n {\n lockingPresenter: false,\n original: false,\n vm: computed\n }\n );\n }\n\n get vm(): IContentEntryFormViewModel {\n const base = this.original.vm;\n\n if (base.isNewEntry) {\n return base;\n }\n\n const canEdit = this.lockingPresenter.vm.canEdit;\n\n return {\n ...base,\n canSave: base.canSave && canEdit,\n canPublish: base.canPublish && canEdit,\n canUnpublish: base.canUnpublish && canEdit,\n canDelete: base.canDelete && canEdit\n };\n }\n\n async saveRevision(options?: { skipValidation?: boolean }): Promise<boolean> {\n const result = await this.original.saveRevision(options);\n\n if (result) {\n await this.lockingPresenter.refreshLock();\n }\n\n return result;\n }\n\n publishRevision(): Promise<boolean> {\n return this.original.publishRevision();\n }\n\n unpublishRevision(): Promise<boolean> {\n return this.original.unpublishRevision();\n }\n\n deleteEntry(): Promise<boolean> {\n return this.original.deleteEntry();\n }\n\n loadRevision(id: string): Promise<void> {\n return this.original.loadRevision(id);\n }\n\n setFolderId(folderId: string | null): void {\n this.original.setFolderId(folderId);\n }\n\n newEntry(initialValues?: Record<string, unknown>): void {\n return this.original.newEntry(initialValues);\n }\n\n patchEntryMeta(...args: Parameters<IContentEntryFormPresenter[\"patchEntryMeta\"]>): void {\n this.original.patchEntryMeta(...args);\n }\n\n reset(): void {\n return this.original.reset();\n }\n}\n\nexport const ContentEntryFormPresenterLockingDecorator = ContentEntryFormPresenter.createDecorator({\n decorator: ContentEntryFormPresenterWithLocking,\n dependencies: [RecordLockingPresenter]\n});\n"],"names":["ContentEntryFormPresenterWithLocking","lockingPresenter","original","makeAutoObservable","computed","base","canEdit","options","result","id","folderId","initialValues","args","ContentEntryFormPresenterLockingDecorator","ContentEntryFormPresenter","RecordLockingPresenter"],"mappings":";;;AAQA,MAAMA;IACF,YACYC,gBAAkD,EAClDC,QAAoC,CAC9C;aAFUD,gBAAgB,GAAhBA;aACAC,QAAQ,GAARA;QAERC,mBACI,IAAI,EACJ;YACI,kBAAkB;YAClB,UAAU;YACV,IAAIC;QACR;IAER;IAEA,IAAI,KAAiC;QACjC,MAAMC,OAAO,IAAI,CAAC,QAAQ,CAAC,EAAE;QAE7B,IAAIA,KAAK,UAAU,EACf,OAAOA;QAGX,MAAMC,UAAU,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC,OAAO;QAEhD,OAAO;YACH,GAAGD,IAAI;YACP,SAASA,KAAK,OAAO,IAAIC;YACzB,YAAYD,KAAK,UAAU,IAAIC;YAC/B,cAAcD,KAAK,YAAY,IAAIC;YACnC,WAAWD,KAAK,SAAS,IAAIC;QACjC;IACJ;IAEA,MAAM,aAAaC,OAAsC,EAAoB;QACzE,MAAMC,SAAS,MAAM,IAAI,CAAC,QAAQ,CAAC,YAAY,CAACD;QAEhD,IAAIC,QACA,MAAM,IAAI,CAAC,gBAAgB,CAAC,WAAW;QAG3C,OAAOA;IACX;IAEA,kBAAoC;QAChC,OAAO,IAAI,CAAC,QAAQ,CAAC,eAAe;IACxC;IAEA,oBAAsC;QAClC,OAAO,IAAI,CAAC,QAAQ,CAAC,iBAAiB;IAC1C;IAEA,cAAgC;QAC5B,OAAO,IAAI,CAAC,QAAQ,CAAC,WAAW;IACpC;IAEA,aAAaC,EAAU,EAAiB;QACpC,OAAO,IAAI,CAAC,QAAQ,CAAC,YAAY,CAACA;IACtC;IAEA,YAAYC,QAAuB,EAAQ;QACvC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAACA;IAC9B;IAEA,SAASC,aAAuC,EAAQ;QACpD,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAACA;IAClC;IAEA,eAAe,GAAGC,IAA8D,EAAQ;QACpF,IAAI,CAAC,QAAQ,CAAC,cAAc,IAAIA;IACpC;IAEA,QAAc;QACV,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK;IAC9B;AACJ;AAEO,MAAMC,4CAA4CC,0BAA0B,eAAe,CAAC;IAC/F,WAAWd;IACX,cAAc;QAACe;KAAuB;AAC1C"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { IdentityContext } from "@webiny/app-admin/features/security/IdentityContext/abstractions.js";
|
|
2
|
+
import { AcquireLockUseCase } from "../../features/acquireLock/abstractions.js";
|
|
3
|
+
import { CheckLockStatusUseCase } from "../../features/checkLockStatus/abstractions.js";
|
|
4
|
+
import { ReleaseLockUseCase } from "../../features/releaseLock/abstractions.js";
|
|
5
|
+
import { ForceUnlockUseCase } from "../../features/forceUnlock/abstractions.js";
|
|
6
|
+
import { type IRecordLockingPresenter, type IRecordLockingViewModel, type IKickOutData } from "./abstractions.js";
|
|
7
|
+
declare class RecordLockingPresenterImpl implements IRecordLockingPresenter {
|
|
8
|
+
private acquireLockUseCase;
|
|
9
|
+
private checkLockStatusUseCase;
|
|
10
|
+
private releaseLockUseCase;
|
|
11
|
+
private forceUnlockUseCase;
|
|
12
|
+
private identityContext;
|
|
13
|
+
private _status;
|
|
14
|
+
private _lockRecord;
|
|
15
|
+
private _entryId;
|
|
16
|
+
private _type;
|
|
17
|
+
private _heartbeatTimer;
|
|
18
|
+
constructor(acquireLockUseCase: AcquireLockUseCase.Interface, checkLockStatusUseCase: CheckLockStatusUseCase.Interface, releaseLockUseCase: ReleaseLockUseCase.Interface, forceUnlockUseCase: ForceUnlockUseCase.Interface, identityContext: IdentityContext.Interface);
|
|
19
|
+
get vm(): IRecordLockingViewModel;
|
|
20
|
+
private checkCanForceUnlock;
|
|
21
|
+
init(entryId: string, type: string): Promise<void>;
|
|
22
|
+
refreshLock(): Promise<void>;
|
|
23
|
+
forceUnlock(): Promise<boolean>;
|
|
24
|
+
handleKickOut(data: IKickOutData): void;
|
|
25
|
+
dispose(): void;
|
|
26
|
+
private startHeartbeat;
|
|
27
|
+
private stopHeartbeat;
|
|
28
|
+
}
|
|
29
|
+
export declare const RecordLockingPresenter: typeof RecordLockingPresenterImpl & {
|
|
30
|
+
__abstraction: import("@webiny/di").Abstraction<IRecordLockingPresenter>;
|
|
31
|
+
};
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { computed, makeAutoObservable, runInAction } from "mobx";
|
|
2
|
+
import { parseIdentifier } from "@webiny/utils";
|
|
3
|
+
import { IdentityContext } from "@webiny/app-admin/features/security/IdentityContext/abstractions.js";
|
|
4
|
+
import { AcquireLockUseCase } from "../../features/acquireLock/abstractions.js";
|
|
5
|
+
import { CheckLockStatusUseCase } from "../../features/checkLockStatus/abstractions.js";
|
|
6
|
+
import { ReleaseLockUseCase } from "../../features/releaseLock/abstractions.js";
|
|
7
|
+
import { ForceUnlockUseCase } from "../../features/forceUnlock/abstractions.js";
|
|
8
|
+
import { RecordLockingPresenter } from "./abstractions.js";
|
|
9
|
+
const HEARTBEAT_INTERVAL_MS = 20000;
|
|
10
|
+
class RecordLockingPresenterImpl {
|
|
11
|
+
constructor(acquireLockUseCase, checkLockStatusUseCase, releaseLockUseCase, forceUnlockUseCase, identityContext){
|
|
12
|
+
this.acquireLockUseCase = acquireLockUseCase;
|
|
13
|
+
this.checkLockStatusUseCase = checkLockStatusUseCase;
|
|
14
|
+
this.releaseLockUseCase = releaseLockUseCase;
|
|
15
|
+
this.forceUnlockUseCase = forceUnlockUseCase;
|
|
16
|
+
this.identityContext = identityContext;
|
|
17
|
+
this._status = null;
|
|
18
|
+
this._lockRecord = null;
|
|
19
|
+
this._entryId = null;
|
|
20
|
+
this._type = null;
|
|
21
|
+
this._heartbeatTimer = null;
|
|
22
|
+
makeAutoObservable(this, {
|
|
23
|
+
acquireLockUseCase: false,
|
|
24
|
+
checkLockStatusUseCase: false,
|
|
25
|
+
releaseLockUseCase: false,
|
|
26
|
+
forceUnlockUseCase: false,
|
|
27
|
+
identityContext: false,
|
|
28
|
+
vm: computed
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
get vm() {
|
|
32
|
+
return {
|
|
33
|
+
status: this._status,
|
|
34
|
+
lockRecord: this._lockRecord,
|
|
35
|
+
lockedByUserName: this._lockRecord?.lockedBy?.displayName ?? null,
|
|
36
|
+
canForceUnlock: this.checkCanForceUnlock(),
|
|
37
|
+
canEdit: "acquired" === this._status
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
checkCanForceUnlock() {
|
|
41
|
+
const identity = this.identityContext.getIdentity();
|
|
42
|
+
if (identity.getPermission("recordLocking.*")) return true;
|
|
43
|
+
const permission = identity.getPermission("recordLocking");
|
|
44
|
+
return permission?.canForceUnlock === true;
|
|
45
|
+
}
|
|
46
|
+
async init(entryId, type) {
|
|
47
|
+
const { id } = parseIdentifier(entryId);
|
|
48
|
+
this._entryId = id;
|
|
49
|
+
this._type = type;
|
|
50
|
+
this._status = "checking";
|
|
51
|
+
try {
|
|
52
|
+
const lockRecord = await this.checkLockStatusUseCase.execute({
|
|
53
|
+
id,
|
|
54
|
+
type
|
|
55
|
+
});
|
|
56
|
+
if (lockRecord) return void runInAction(()=>{
|
|
57
|
+
this._status = "locked";
|
|
58
|
+
this._lockRecord = lockRecord;
|
|
59
|
+
});
|
|
60
|
+
const acquired = await this.acquireLockUseCase.execute({
|
|
61
|
+
id,
|
|
62
|
+
type
|
|
63
|
+
});
|
|
64
|
+
runInAction(()=>{
|
|
65
|
+
this._status = "acquired";
|
|
66
|
+
this._lockRecord = acquired;
|
|
67
|
+
});
|
|
68
|
+
this.startHeartbeat(id, type);
|
|
69
|
+
} catch {
|
|
70
|
+
runInAction(()=>{
|
|
71
|
+
this._status = "error";
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async refreshLock() {
|
|
76
|
+
if ("acquired" !== this._status || !this._entryId || !this._type) return;
|
|
77
|
+
try {
|
|
78
|
+
const record = await this.acquireLockUseCase.execute({
|
|
79
|
+
id: this._entryId,
|
|
80
|
+
type: this._type
|
|
81
|
+
});
|
|
82
|
+
runInAction(()=>{
|
|
83
|
+
this._lockRecord = record;
|
|
84
|
+
});
|
|
85
|
+
} catch {}
|
|
86
|
+
}
|
|
87
|
+
async forceUnlock() {
|
|
88
|
+
if (!this._lockRecord || !this._type) return false;
|
|
89
|
+
try {
|
|
90
|
+
await this.forceUnlockUseCase.execute({
|
|
91
|
+
id: this._lockRecord.targetId,
|
|
92
|
+
type: this._type
|
|
93
|
+
});
|
|
94
|
+
const acquired = await this.acquireLockUseCase.execute({
|
|
95
|
+
id: this._lockRecord.targetId,
|
|
96
|
+
type: this._type
|
|
97
|
+
});
|
|
98
|
+
runInAction(()=>{
|
|
99
|
+
this._status = "acquired";
|
|
100
|
+
this._lockRecord = acquired;
|
|
101
|
+
});
|
|
102
|
+
this.startHeartbeat(this._lockRecord.targetId, this._type);
|
|
103
|
+
return true;
|
|
104
|
+
} catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
handleKickOut(data) {
|
|
109
|
+
this.stopHeartbeat();
|
|
110
|
+
runInAction(()=>{
|
|
111
|
+
this._status = "kicked-out";
|
|
112
|
+
this._lockRecord = data.record;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
dispose() {
|
|
116
|
+
this.stopHeartbeat();
|
|
117
|
+
if ("acquired" === this._status && this._entryId && this._type) this.releaseLockUseCase.execute({
|
|
118
|
+
id: this._entryId,
|
|
119
|
+
type: this._type
|
|
120
|
+
}).catch(()=>{});
|
|
121
|
+
this._status = null;
|
|
122
|
+
this._lockRecord = null;
|
|
123
|
+
this._entryId = null;
|
|
124
|
+
this._type = null;
|
|
125
|
+
}
|
|
126
|
+
startHeartbeat(entryId, type) {
|
|
127
|
+
this.stopHeartbeat();
|
|
128
|
+
this._heartbeatTimer = setInterval(async ()=>{
|
|
129
|
+
try {
|
|
130
|
+
const record = await this.acquireLockUseCase.execute({
|
|
131
|
+
id: entryId,
|
|
132
|
+
type
|
|
133
|
+
});
|
|
134
|
+
runInAction(()=>{
|
|
135
|
+
this._lockRecord = record;
|
|
136
|
+
});
|
|
137
|
+
} catch {
|
|
138
|
+
this.stopHeartbeat();
|
|
139
|
+
runInAction(()=>{
|
|
140
|
+
this._status = "error";
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}, HEARTBEAT_INTERVAL_MS);
|
|
144
|
+
}
|
|
145
|
+
stopHeartbeat() {
|
|
146
|
+
if (null !== this._heartbeatTimer) {
|
|
147
|
+
clearInterval(this._heartbeatTimer);
|
|
148
|
+
this._heartbeatTimer = null;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const RecordLockingPresenter_RecordLockingPresenter = RecordLockingPresenter.createImplementation({
|
|
153
|
+
implementation: RecordLockingPresenterImpl,
|
|
154
|
+
dependencies: [
|
|
155
|
+
AcquireLockUseCase,
|
|
156
|
+
CheckLockStatusUseCase,
|
|
157
|
+
ReleaseLockUseCase,
|
|
158
|
+
ForceUnlockUseCase,
|
|
159
|
+
IdentityContext
|
|
160
|
+
]
|
|
161
|
+
});
|
|
162
|
+
export { RecordLockingPresenter_RecordLockingPresenter as RecordLockingPresenter };
|
|
163
|
+
|
|
164
|
+
//# sourceMappingURL=RecordLockingPresenter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation/entryLocking/RecordLockingPresenter.js","sources":["../../../src/presentation/entryLocking/RecordLockingPresenter.ts"],"sourcesContent":["import { computed, makeAutoObservable, runInAction } from \"mobx\";\nimport { parseIdentifier } from \"@webiny/utils\";\nimport { IdentityContext } from \"@webiny/app-admin/features/security/IdentityContext/abstractions.js\";\nimport { AcquireLockUseCase } from \"~/features/acquireLock/abstractions.js\";\nimport { CheckLockStatusUseCase } from \"~/features/checkLockStatus/abstractions.js\";\nimport { ReleaseLockUseCase } from \"~/features/releaseLock/abstractions.js\";\nimport { ForceUnlockUseCase } from \"~/features/forceUnlock/abstractions.js\";\nimport {\n RecordLockingPresenter as Abstraction,\n type IRecordLockingPresenter,\n type IRecordLockingViewModel,\n type IKickOutData,\n type RecordLockingStatus\n} from \"./abstractions.js\";\nimport type { IRecordLockingLockRecord } from \"~/types.js\";\n\nconst HEARTBEAT_INTERVAL_MS = 20_000;\n\nclass RecordLockingPresenterImpl implements IRecordLockingPresenter {\n private _status: RecordLockingStatus = null;\n private _lockRecord: IRecordLockingLockRecord | null = null;\n private _entryId: string | null = null;\n private _type: string | null = null;\n private _heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(\n private acquireLockUseCase: AcquireLockUseCase.Interface,\n private checkLockStatusUseCase: CheckLockStatusUseCase.Interface,\n private releaseLockUseCase: ReleaseLockUseCase.Interface,\n private forceUnlockUseCase: ForceUnlockUseCase.Interface,\n private identityContext: IdentityContext.Interface\n ) {\n makeAutoObservable<\n RecordLockingPresenterImpl,\n | \"acquireLockUseCase\"\n | \"checkLockStatusUseCase\"\n | \"releaseLockUseCase\"\n | \"forceUnlockUseCase\"\n | \"identityContext\"\n >(this, {\n acquireLockUseCase: false,\n checkLockStatusUseCase: false,\n releaseLockUseCase: false,\n forceUnlockUseCase: false,\n identityContext: false,\n vm: computed\n });\n }\n\n get vm(): IRecordLockingViewModel {\n return {\n status: this._status,\n lockRecord: this._lockRecord,\n lockedByUserName: this._lockRecord?.lockedBy?.displayName ?? null,\n canForceUnlock: this.checkCanForceUnlock(),\n canEdit: this._status === \"acquired\"\n };\n }\n\n private checkCanForceUnlock(): boolean {\n const identity = this.identityContext.getIdentity();\n\n if (identity.getPermission(\"recordLocking.*\")) {\n return true;\n }\n\n const permission = identity.getPermission(\"recordLocking\");\n return permission?.canForceUnlock === true;\n }\n\n async init(entryId: string, type: string): Promise<void> {\n const { id } = parseIdentifier(entryId);\n\n this._entryId = id;\n this._type = type;\n this._status = \"checking\";\n\n try {\n const lockRecord = await this.checkLockStatusUseCase.execute({\n id,\n type\n });\n\n if (lockRecord) {\n runInAction(() => {\n this._status = \"locked\";\n this._lockRecord = lockRecord;\n });\n return;\n }\n\n const acquired = await this.acquireLockUseCase.execute({\n id,\n type\n });\n\n runInAction(() => {\n this._status = \"acquired\";\n this._lockRecord = acquired;\n });\n\n this.startHeartbeat(id, type);\n } catch {\n runInAction(() => {\n this._status = \"error\";\n });\n }\n }\n\n async refreshLock(): Promise<void> {\n if (this._status !== \"acquired\" || !this._entryId || !this._type) {\n return;\n }\n\n try {\n const record = await this.acquireLockUseCase.execute({\n id: this._entryId,\n type: this._type\n });\n\n runInAction(() => {\n this._lockRecord = record;\n });\n } catch {\n // Heartbeat will retry\n }\n }\n\n async forceUnlock(): Promise<boolean> {\n if (!this._lockRecord || !this._type) {\n return false;\n }\n\n try {\n await this.forceUnlockUseCase.execute({\n id: this._lockRecord.targetId,\n type: this._type\n });\n\n const acquired = await this.acquireLockUseCase.execute({\n id: this._lockRecord.targetId,\n type: this._type\n });\n\n runInAction(() => {\n this._status = \"acquired\";\n this._lockRecord = acquired;\n });\n\n this.startHeartbeat(this._lockRecord.targetId, this._type);\n\n return true;\n } catch {\n return false;\n }\n }\n\n handleKickOut(data: IKickOutData): void {\n this.stopHeartbeat();\n\n runInAction(() => {\n this._status = \"kicked-out\";\n this._lockRecord = data.record;\n });\n }\n\n dispose(): void {\n this.stopHeartbeat();\n\n if (this._status === \"acquired\" && this._entryId && this._type) {\n this.releaseLockUseCase\n .execute({ id: this._entryId, type: this._type })\n .catch(() => {});\n }\n\n this._status = null;\n this._lockRecord = null;\n this._entryId = null;\n this._type = null;\n }\n\n private startHeartbeat(entryId: string, type: string): void {\n this.stopHeartbeat();\n\n this._heartbeatTimer = setInterval(async () => {\n try {\n const record = await this.acquireLockUseCase.execute({ id: entryId, type });\n runInAction(() => {\n this._lockRecord = record;\n });\n } catch {\n this.stopHeartbeat();\n runInAction(() => {\n this._status = \"error\";\n });\n }\n }, HEARTBEAT_INTERVAL_MS);\n }\n\n private stopHeartbeat(): void {\n if (this._heartbeatTimer !== null) {\n clearInterval(this._heartbeatTimer);\n this._heartbeatTimer = null;\n }\n }\n}\n\nexport const RecordLockingPresenter = Abstraction.createImplementation({\n implementation: RecordLockingPresenterImpl,\n dependencies: [\n AcquireLockUseCase,\n CheckLockStatusUseCase,\n ReleaseLockUseCase,\n ForceUnlockUseCase,\n IdentityContext\n ]\n});\n"],"names":["HEARTBEAT_INTERVAL_MS","RecordLockingPresenterImpl","acquireLockUseCase","checkLockStatusUseCase","releaseLockUseCase","forceUnlockUseCase","identityContext","makeAutoObservable","computed","identity","permission","entryId","type","id","parseIdentifier","lockRecord","runInAction","acquired","record","data","setInterval","clearInterval","RecordLockingPresenter","Abstraction","AcquireLockUseCase","CheckLockStatusUseCase","ReleaseLockUseCase","ForceUnlockUseCase","IdentityContext"],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import react 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 { useWebsockets } from "@webiny/app-websockets";
|
|
6
|
+
import { OverlayLoader } from "@webiny/admin-ui";
|
|
7
|
+
import { parseIdentifier } from "@webiny/utils";
|
|
8
|
+
import { SingleEntryFormContent } from "@webiny/app-headless-cms/presentation/contentEntries/views/SingleEntryFormContent.js";
|
|
9
|
+
import { SingleEntryPresenter } from "@webiny/app-headless-cms/presentation/contentEntries/singleEntry/abstractions.js";
|
|
10
|
+
import { CmsModelContext } from "@webiny/app-headless-cms/features/contentEntry/abstractions.js";
|
|
11
|
+
import { RecordLockingPresenter } from "./abstractions.js";
|
|
12
|
+
import { LockedEntryOverlay } from "./components/LockedEntryOverlay.js";
|
|
13
|
+
const SingleEntryContentDecorator = SingleEntryFormContent.createDecorator((Original)=>observer(function(props) {
|
|
14
|
+
const container = useContainer();
|
|
15
|
+
const websockets = useWebsockets();
|
|
16
|
+
const router = useRouter();
|
|
17
|
+
const { showDialog } = useDialogs();
|
|
18
|
+
const singleEntryPresenter = react.useMemo(()=>container.resolve(SingleEntryPresenter), [
|
|
19
|
+
container
|
|
20
|
+
]);
|
|
21
|
+
const lockingPresenter = react.useMemo(()=>container.resolve(RecordLockingPresenter), [
|
|
22
|
+
container
|
|
23
|
+
]);
|
|
24
|
+
const modelAccessor = react.useMemo(()=>container.resolve(CmsModelContext), [
|
|
25
|
+
container
|
|
26
|
+
]);
|
|
27
|
+
const entry = singleEntryPresenter.vm.entry;
|
|
28
|
+
react.useEffect(()=>{
|
|
29
|
+
if (!entry?.id) return;
|
|
30
|
+
lockingPresenter.init(entry.id, modelAccessor.getModel().modelId);
|
|
31
|
+
return ()=>lockingPresenter.dispose();
|
|
32
|
+
}, [
|
|
33
|
+
entry?.id
|
|
34
|
+
]);
|
|
35
|
+
react.useEffect(()=>{
|
|
36
|
+
if (!entry?.id) return;
|
|
37
|
+
const { id } = parseIdentifier(entry.id);
|
|
38
|
+
const sub = websockets.onMessage(`recordLocking.entry.kickOut.${id}`, (incoming)=>lockingPresenter.handleKickOut(incoming.data));
|
|
39
|
+
return ()=>{
|
|
40
|
+
sub.off();
|
|
41
|
+
};
|
|
42
|
+
}, [
|
|
43
|
+
entry?.id
|
|
44
|
+
]);
|
|
45
|
+
react.useEffect(()=>{
|
|
46
|
+
if ("kicked-out" !== lockingPresenter.vm.status) return;
|
|
47
|
+
router.unblockTransition();
|
|
48
|
+
showDialog({
|
|
49
|
+
title: "Entry was forcefully unlocked!",
|
|
50
|
+
content: /*#__PURE__*/ react.createElement(react.Fragment, null, "The entry you were editing was forcefully unlocked by", " ", /*#__PURE__*/ react.createElement("strong", null, lockingPresenter.vm.lockRecord?.lockedBy?.displayName || "Unknown user"), ". Unfortunately, this means you lost the unsaved changes."),
|
|
51
|
+
acceptLabel: "Ok",
|
|
52
|
+
onClose: void 0,
|
|
53
|
+
cancelLabel: null
|
|
54
|
+
});
|
|
55
|
+
}, [
|
|
56
|
+
lockingPresenter.vm.status
|
|
57
|
+
]);
|
|
58
|
+
if (!entry?.id) return /*#__PURE__*/ react.createElement(Original, props);
|
|
59
|
+
if ("checking" === lockingPresenter.vm.status) return /*#__PURE__*/ react.createElement(OverlayLoader, {
|
|
60
|
+
text: "Checking record status..."
|
|
61
|
+
});
|
|
62
|
+
if ("locked" === lockingPresenter.vm.status) return /*#__PURE__*/ react.createElement(LockedEntryOverlay, {
|
|
63
|
+
vm: lockingPresenter.vm,
|
|
64
|
+
entryTitle: entry.meta?.title,
|
|
65
|
+
onForceUnlock: ()=>lockingPresenter.forceUnlock(),
|
|
66
|
+
onNavigateBack: ()=>{}
|
|
67
|
+
});
|
|
68
|
+
return /*#__PURE__*/ react.createElement(Original, props);
|
|
69
|
+
}));
|
|
70
|
+
export { SingleEntryContentDecorator };
|
|
71
|
+
|
|
72
|
+
//# sourceMappingURL=SingleEntryContentDecorator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation/entryLocking/SingleEntryContentDecorator.js","sources":["../../../src/presentation/entryLocking/SingleEntryContentDecorator.tsx"],"sourcesContent":["import React from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport { useContainer, useRouter } from \"@webiny/app\";\nimport { useDialogs } from \"@webiny/app-admin\";\nimport { useWebsockets } from \"@webiny/app-websockets\";\nimport { OverlayLoader } from \"@webiny/admin-ui\";\nimport { parseIdentifier } from \"@webiny/utils\";\nimport { SingleEntryFormContent } from \"@webiny/app-headless-cms/presentation/contentEntries/views/SingleEntryFormContent.js\";\nimport { SingleEntryPresenter } from \"@webiny/app-headless-cms/presentation/contentEntries/singleEntry/abstractions.js\";\nimport { CmsModelContext } from \"@webiny/app-headless-cms/features/contentEntry/abstractions.js\";\nimport { RecordLockingPresenter } from \"./abstractions.js\";\nimport { LockedEntryOverlay } from \"./components/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\nexport const SingleEntryContentDecorator = SingleEntryFormContent.createDecorator(Original => {\n return observer(function RecordLockingSingleEntryContent(props) {\n const container = useContainer();\n const websockets = useWebsockets();\n const router = useRouter();\n const { showDialog } = useDialogs();\n\n const singleEntryPresenter = React.useMemo(\n () => container.resolve(SingleEntryPresenter),\n [container]\n );\n\n const lockingPresenter = React.useMemo(\n () => container.resolve(RecordLockingPresenter),\n [container]\n );\n\n const modelAccessor = React.useMemo(() => container.resolve(CmsModelContext), [container]);\n\n const entry = singleEntryPresenter.vm.entry;\n\n React.useEffect(() => {\n if (!entry?.id) {\n return;\n }\n lockingPresenter.init(entry.id, modelAccessor.getModel().modelId);\n return () => lockingPresenter.dispose();\n }, [entry?.id]);\n\n React.useEffect(() => {\n if (!entry?.id) {\n return;\n }\n const { id } = parseIdentifier(entry.id);\n const sub = websockets.onMessage<KickOutMessage>(\n `recordLocking.entry.kickOut.${id}`,\n incoming => lockingPresenter.handleKickOut(incoming.data)\n );\n return () => {\n sub.off();\n };\n }, [entry?.id]);\n\n React.useEffect(() => {\n if (lockingPresenter.vm.status !== \"kicked-out\") {\n return;\n }\n\n router.unblockTransition();\n showDialog({\n title: \"Entry was forcefully unlocked!\",\n content: (\n <>\n The entry you were editing was forcefully unlocked by{\" \"}\n <strong>\n {lockingPresenter.vm.lockRecord?.lockedBy?.displayName ||\n \"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 }, [lockingPresenter.vm.status]);\n\n if (!entry?.id) {\n return <Original {...props} />;\n }\n\n if (lockingPresenter.vm.status === \"checking\") {\n return <OverlayLoader text={\"Checking record status...\"} />;\n }\n\n if (lockingPresenter.vm.status === \"locked\") {\n return (\n <LockedEntryOverlay\n vm={lockingPresenter.vm}\n entryTitle={entry.meta?.title}\n onForceUnlock={() => lockingPresenter.forceUnlock()}\n onNavigateBack={() => {\n // It's a single-entry model, so there's nowhere to go.\n }}\n />\n );\n }\n\n return <Original {...props} />;\n });\n});\n"],"names":["SingleEntryContentDecorator","SingleEntryFormContent","Original","observer","props","container","useContainer","websockets","useWebsockets","router","useRouter","showDialog","useDialogs","singleEntryPresenter","React","SingleEntryPresenter","lockingPresenter","RecordLockingPresenter","modelAccessor","CmsModelContext","entry","id","parseIdentifier","sub","incoming","undefined","OverlayLoader","LockedEntryOverlay"],"mappings":";;;;;;;;;;;;AAmBO,MAAMA,8BAA8BC,uBAAuB,eAAe,CAACC,CAAAA,WACvEC,SAAS,SAAyCC,KAAK;QAC1D,MAAMC,YAAYC;QAClB,MAAMC,aAAaC;QACnB,MAAMC,SAASC;QACf,MAAM,EAAEC,UAAU,EAAE,GAAGC;QAEvB,MAAMC,uBAAuBC,MAAAA,OAAa,CACtC,IAAMT,UAAU,OAAO,CAACU,uBACxB;YAACV;SAAU;QAGf,MAAMW,mBAAmBF,MAAAA,OAAa,CAClC,IAAMT,UAAU,OAAO,CAACY,yBACxB;YAACZ;SAAU;QAGf,MAAMa,gBAAgBJ,MAAAA,OAAa,CAAC,IAAMT,UAAU,OAAO,CAACc,kBAAkB;YAACd;SAAU;QAEzF,MAAMe,QAAQP,qBAAqB,EAAE,CAAC,KAAK;QAE3CC,MAAAA,SAAe,CAAC;YACZ,IAAI,CAACM,OAAO,IACR;YAEJJ,iBAAiB,IAAI,CAACI,MAAM,EAAE,EAAEF,cAAc,QAAQ,GAAG,OAAO;YAChE,OAAO,IAAMF,iBAAiB,OAAO;QACzC,GAAG;YAACI,OAAO;SAAG;QAEdN,MAAAA,SAAe,CAAC;YACZ,IAAI,CAACM,OAAO,IACR;YAEJ,MAAM,EAAEC,EAAE,EAAE,GAAGC,gBAAgBF,MAAM,EAAE;YACvC,MAAMG,MAAMhB,WAAW,SAAS,CAC5B,CAAC,4BAA4B,EAAEc,IAAI,EACnCG,CAAAA,WAAYR,iBAAiB,aAAa,CAACQ,SAAS,IAAI;YAE5D,OAAO;gBACHD,IAAI,GAAG;YACX;QACJ,GAAG;YAACH,OAAO;SAAG;QAEdN,MAAAA,SAAe,CAAC;YACZ,IAAIE,AAA+B,iBAA/BA,iBAAiB,EAAE,CAAC,MAAM,EAC1B;YAGJP,OAAO,iBAAiB;YACxBE,WAAW;gBACP,OAAO;gBACP,SAAS,WAAT,GACI,0CAAE,yDACwD,mBACtD,oBAAC,gBACIK,iBAAiB,EAAE,CAAC,UAAU,EAAE,UAAU,eACvC,iBACC;gBAIjB,aAAa;gBACb,SAASS;gBACT,aAAa;YACjB;QACJ,GAAG;YAACT,iBAAiB,EAAE,CAAC,MAAM;SAAC;QAE/B,IAAI,CAACI,OAAO,IACR,OAAO,WAAP,GAAO,oBAAClB,UAAaE;QAGzB,IAAIY,AAA+B,eAA/BA,iBAAiB,EAAE,CAAC,MAAM,EAC1B,OAAO,WAAP,GAAO,oBAACU,eAAaA;YAAC,MAAM;;QAGhC,IAAIV,AAA+B,aAA/BA,iBAAiB,EAAE,CAAC,MAAM,EAC1B,OAAO,WAAP,GACI,oBAACW,oBAAkBA;YACf,IAAIX,iBAAiB,EAAE;YACvB,YAAYI,MAAM,IAAI,EAAE;YACxB,eAAe,IAAMJ,iBAAiB,WAAW;YACjD,gBAAgB,KAEhB;;QAKZ,OAAO,WAAP,GAAO,oBAACd,UAAaE;IACzB"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ISingleEntryPresenter, type ISingleEntryViewModel } from "@webiny/app-headless-cms/presentation/contentEntries/singleEntry/abstractions.js";
|
|
2
|
+
import { RecordLockingPresenter } from "./abstractions.js";
|
|
3
|
+
declare class SingleEntryPresenterWithLocking implements ISingleEntryPresenter {
|
|
4
|
+
private lockingPresenter;
|
|
5
|
+
private original;
|
|
6
|
+
constructor(lockingPresenter: RecordLockingPresenter.Interface, original: ISingleEntryPresenter);
|
|
7
|
+
get vm(): ISingleEntryViewModel;
|
|
8
|
+
init(): Promise<void>;
|
|
9
|
+
save(): Promise<boolean>;
|
|
10
|
+
dispose(): void;
|
|
11
|
+
}
|
|
12
|
+
export declare const SingleEntryPresenterLockingDecorator: typeof SingleEntryPresenterWithLocking & {
|
|
13
|
+
__abstraction: import("@webiny/di").Abstraction<ISingleEntryPresenter>;
|
|
14
|
+
};
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { computed, makeAutoObservable } from "mobx";
|
|
2
|
+
import { SingleEntryPresenter } from "@webiny/app-headless-cms/presentation/contentEntries/singleEntry/abstractions.js";
|
|
3
|
+
import { RecordLockingPresenter } from "./abstractions.js";
|
|
4
|
+
class SingleEntryPresenterWithLocking {
|
|
5
|
+
constructor(lockingPresenter, original){
|
|
6
|
+
this.lockingPresenter = lockingPresenter;
|
|
7
|
+
this.original = original;
|
|
8
|
+
makeAutoObservable(this, {
|
|
9
|
+
lockingPresenter: false,
|
|
10
|
+
original: false,
|
|
11
|
+
vm: computed
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
get vm() {
|
|
15
|
+
const base = this.original.vm;
|
|
16
|
+
const canEdit = this.lockingPresenter.vm.canEdit;
|
|
17
|
+
return {
|
|
18
|
+
...base,
|
|
19
|
+
canSave: base.canSave && canEdit
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
async init() {
|
|
23
|
+
return this.original.init();
|
|
24
|
+
}
|
|
25
|
+
async save() {
|
|
26
|
+
return this.original.save();
|
|
27
|
+
}
|
|
28
|
+
dispose() {
|
|
29
|
+
return this.original.dispose();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const SingleEntryPresenterLockingDecorator = SingleEntryPresenter.createDecorator({
|
|
33
|
+
decorator: SingleEntryPresenterWithLocking,
|
|
34
|
+
dependencies: [
|
|
35
|
+
RecordLockingPresenter
|
|
36
|
+
]
|
|
37
|
+
});
|
|
38
|
+
export { SingleEntryPresenterLockingDecorator };
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=SingleEntryPresenterDecorator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation/entryLocking/SingleEntryPresenterDecorator.js","sources":["../../../src/presentation/entryLocking/SingleEntryPresenterDecorator.ts"],"sourcesContent":["import { computed, makeAutoObservable } from \"mobx\";\nimport {\n SingleEntryPresenter,\n type ISingleEntryPresenter,\n type ISingleEntryViewModel\n} from \"@webiny/app-headless-cms/presentation/contentEntries/singleEntry/abstractions.js\";\nimport { RecordLockingPresenter } from \"./abstractions.js\";\n\nclass SingleEntryPresenterWithLocking implements ISingleEntryPresenter {\n constructor(\n private lockingPresenter: RecordLockingPresenter.Interface,\n private original: ISingleEntryPresenter\n ) {\n makeAutoObservable<SingleEntryPresenterWithLocking, \"lockingPresenter\" | \"original\">(this, {\n lockingPresenter: false,\n original: false,\n vm: computed\n });\n }\n\n get vm(): ISingleEntryViewModel {\n const base = this.original.vm;\n const canEdit = this.lockingPresenter.vm.canEdit;\n\n return {\n ...base,\n canSave: base.canSave && canEdit\n };\n }\n\n async init(): Promise<void> {\n return this.original.init();\n }\n\n async save(): Promise<boolean> {\n return this.original.save();\n }\n\n dispose(): void {\n return this.original.dispose();\n }\n}\n\nexport const SingleEntryPresenterLockingDecorator = SingleEntryPresenter.createDecorator({\n decorator: SingleEntryPresenterWithLocking,\n dependencies: [RecordLockingPresenter]\n});\n"],"names":["SingleEntryPresenterWithLocking","lockingPresenter","original","makeAutoObservable","computed","base","canEdit","SingleEntryPresenterLockingDecorator","SingleEntryPresenter","RecordLockingPresenter"],"mappings":";;;AAQA,MAAMA;IACF,YACYC,gBAAkD,EAClDC,QAA+B,CACzC;aAFUD,gBAAgB,GAAhBA;aACAC,QAAQ,GAARA;QAERC,mBAAqF,IAAI,EAAE;YACvF,kBAAkB;YAClB,UAAU;YACV,IAAIC;QACR;IACJ;IAEA,IAAI,KAA4B;QAC5B,MAAMC,OAAO,IAAI,CAAC,QAAQ,CAAC,EAAE;QAC7B,MAAMC,UAAU,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC,OAAO;QAEhD,OAAO;YACH,GAAGD,IAAI;YACP,SAASA,KAAK,OAAO,IAAIC;QAC7B;IACJ;IAEA,MAAM,OAAsB;QACxB,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI;IAC7B;IAEA,MAAM,OAAyB;QAC3B,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI;IAC7B;IAEA,UAAgB;QACZ,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO;IAChC;AACJ;AAEO,MAAMC,uCAAuCC,qBAAqB,eAAe,CAAC;IACrF,WAAWR;IACX,cAAc;QAACS;KAAuB;AAC1C"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { IRecordLockingIdentity, IRecordLockingLockRecord } from "../../types.js";
|
|
2
|
+
export type RecordLockingStatus = "checking" | "locked" | "acquired" | "kicked-out" | "error" | null;
|
|
3
|
+
export interface IRecordLockingViewModel {
|
|
4
|
+
status: RecordLockingStatus;
|
|
5
|
+
lockRecord: IRecordLockingLockRecord | null;
|
|
6
|
+
lockedByUserName: string | null;
|
|
7
|
+
canForceUnlock: boolean;
|
|
8
|
+
canEdit: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface IKickOutData {
|
|
11
|
+
record: IRecordLockingLockRecord;
|
|
12
|
+
user: IRecordLockingIdentity;
|
|
13
|
+
}
|
|
14
|
+
export interface IRecordLockingPresenter {
|
|
15
|
+
readonly vm: IRecordLockingViewModel;
|
|
16
|
+
init(entryId: string, type: string): Promise<void>;
|
|
17
|
+
refreshLock(): Promise<void>;
|
|
18
|
+
forceUnlock(): Promise<boolean>;
|
|
19
|
+
handleKickOut(data: IKickOutData): void;
|
|
20
|
+
dispose(): void;
|
|
21
|
+
}
|
|
22
|
+
export declare const RecordLockingPresenter: import("@webiny/di").Abstraction<IRecordLockingPresenter>;
|
|
23
|
+
export declare namespace RecordLockingPresenter {
|
|
24
|
+
type Interface = IRecordLockingPresenter;
|
|
25
|
+
type ViewModel = IRecordLockingViewModel;
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation/entryLocking/abstractions.js","sources":["../../../src/presentation/entryLocking/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/admin\";\nimport type { IRecordLockingIdentity, IRecordLockingLockRecord } from \"~/types.js\";\n\nexport type RecordLockingStatus =\n | \"checking\"\n | \"locked\"\n | \"acquired\"\n | \"kicked-out\"\n | \"error\"\n | null;\n\nexport interface IRecordLockingViewModel {\n status: RecordLockingStatus;\n lockRecord: IRecordLockingLockRecord | null;\n lockedByUserName: string | null;\n canForceUnlock: boolean;\n canEdit: boolean;\n}\n\nexport interface IKickOutData {\n record: IRecordLockingLockRecord;\n user: IRecordLockingIdentity;\n}\n\nexport interface IRecordLockingPresenter {\n readonly vm: IRecordLockingViewModel;\n init(entryId: string, type: string): Promise<void>;\n refreshLock(): Promise<void>;\n forceUnlock(): Promise<boolean>;\n handleKickOut(data: IKickOutData): void;\n dispose(): void;\n}\n\nexport const RecordLockingPresenter =\n createAbstraction<IRecordLockingPresenter>(\"RecordLockingPresenter\");\n\nexport namespace RecordLockingPresenter {\n export type Interface = IRecordLockingPresenter;\n export type ViewModel = IRecordLockingViewModel;\n}\n"],"names":["RecordLockingPresenter","createAbstraction"],"mappings":";AAiCO,MAAMA,yBACTC,kBAA2C"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { IRecordLockingIdentity } from "../../../types.js";
|
|
3
|
+
interface ForceUnlockButtonProps {
|
|
4
|
+
canForceUnlock: boolean;
|
|
5
|
+
lockedBy: IRecordLockingIdentity | null;
|
|
6
|
+
entryTitle?: string;
|
|
7
|
+
onForceUnlock: () => Promise<boolean>;
|
|
8
|
+
onNavigateBack: () => void;
|
|
9
|
+
}
|
|
10
|
+
export declare const ForceUnlockButton: ({ canForceUnlock, lockedBy, entryTitle, onForceUnlock, onNavigateBack }: ForceUnlockButtonProps) => React.JSX.Element | null;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import react, { useCallback } from "react";
|
|
2
|
+
import { Alert, Button, Text } from "@webiny/admin-ui";
|
|
3
|
+
import { useConfirmationDialog, useSnackbar } from "@webiny/app-admin";
|
|
4
|
+
const ForceUnlockButton = ({ canForceUnlock, lockedBy, entryTitle, onForceUnlock, onNavigateBack })=>{
|
|
5
|
+
const { showSnackbar } = useSnackbar();
|
|
6
|
+
const { showConfirmation } = useConfirmationDialog({
|
|
7
|
+
title: "Force unlock the entry",
|
|
8
|
+
message: /*#__PURE__*/ react.createElement("div", null, /*#__PURE__*/ react.createElement(Alert, {
|
|
9
|
+
type: "warning",
|
|
10
|
+
title: "Warning",
|
|
11
|
+
className: "mb-md"
|
|
12
|
+
}, /*#__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, entryTitle || "entry"), ". Are you sure you want to continue?"))
|
|
13
|
+
});
|
|
14
|
+
const onClick = useCallback(()=>{
|
|
15
|
+
showConfirmation(async ()=>{
|
|
16
|
+
const success = await onForceUnlock();
|
|
17
|
+
if (!success) return void showSnackbar("Failed to force unlock the entry.");
|
|
18
|
+
onNavigateBack();
|
|
19
|
+
});
|
|
20
|
+
}, [
|
|
21
|
+
onForceUnlock,
|
|
22
|
+
onNavigateBack
|
|
23
|
+
]);
|
|
24
|
+
if (!canForceUnlock) return null;
|
|
25
|
+
return /*#__PURE__*/ react.createElement("div", {
|
|
26
|
+
className: "mt-md"
|
|
27
|
+
}, /*#__PURE__*/ react.createElement(Text, {
|
|
28
|
+
as: "div",
|
|
29
|
+
className: "mb-md"
|
|
30
|
+
}, "Because you have full access to the system, you can force unlock the record."), /*#__PURE__*/ react.createElement(Button, {
|
|
31
|
+
onClick: onClick,
|
|
32
|
+
text: "Unlock and go back"
|
|
33
|
+
}));
|
|
34
|
+
};
|
|
35
|
+
export { ForceUnlockButton };
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=ForceUnlockButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation/entryLocking/components/ForceUnlockButton.js","sources":["../../../../src/presentation/entryLocking/components/ForceUnlockButton.tsx"],"sourcesContent":["import React, { useCallback } from \"react\";\nimport { Alert, Button, Text } from \"@webiny/admin-ui\";\nimport { useConfirmationDialog, useSnackbar } from \"@webiny/app-admin\";\nimport type { IRecordLockingIdentity } from \"~/types.js\";\n\ninterface ForceUnlockButtonProps {\n canForceUnlock: boolean;\n lockedBy: IRecordLockingIdentity | null;\n entryTitle?: string;\n onForceUnlock: () => Promise<boolean>;\n onNavigateBack: () => void;\n}\n\nexport const ForceUnlockButton = ({\n canForceUnlock,\n lockedBy,\n entryTitle,\n onForceUnlock,\n onNavigateBack\n}: ForceUnlockButtonProps) => {\n const { showSnackbar } = useSnackbar();\n const { showConfirmation } = useConfirmationDialog({\n title: \"Force unlock the entry\",\n message: (\n <div>\n <Alert type=\"warning\" title=\"Warning\" className={\"mb-md\"}>\n <strong>{lockedBy?.displayName || \"Unknown user\"}</strong> is currently editing\n this record.\n <br />\n If you force unlock it, they could potentially lose their changes.\n </Alert>\n <Text>\n You are about to forcefully unlock the <strong>{entryTitle || \"entry\"}</strong>.\n Are you sure you want to continue?\n </Text>\n </div>\n )\n });\n\n const onClick = useCallback(() => {\n showConfirmation(async () => {\n const success = await onForceUnlock();\n if (!success) {\n showSnackbar(\"Failed to force unlock the entry.\");\n return;\n }\n onNavigateBack();\n });\n }, [onForceUnlock, onNavigateBack]);\n\n if (!canForceUnlock) {\n return null;\n }\n\n return (\n <div className=\"mt-md\">\n <Text as={\"div\"} className={\"mb-md\"}>\n Because you have 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"],"names":["ForceUnlockButton","canForceUnlock","lockedBy","entryTitle","onForceUnlock","onNavigateBack","showSnackbar","useSnackbar","showConfirmation","useConfirmationDialog","Alert","Text","onClick","useCallback","success","Button"],"mappings":";;;AAaO,MAAMA,oBAAoB,CAAC,EAC9BC,cAAc,EACdC,QAAQ,EACRC,UAAU,EACVC,aAAa,EACbC,cAAc,EACO;IACrB,MAAM,EAAEC,YAAY,EAAE,GAAGC;IACzB,MAAM,EAAEC,gBAAgB,EAAE,GAAGC,sBAAsB;QAC/C,OAAO;QACP,SAAS,WAAT,GACI,oBAAC,2BACG,oBAACC,OAAKA;YAAC,MAAK;YAAU,OAAM;YAAU,WAAW;yBAC7C,oBAAC,gBAAQR,UAAU,eAAe,iBAAwB,oDAE1D,oBAAC,aAAK,qFAGV,oBAACS,MAAIA,MAAC,yDACqC,oBAAC,gBAAQR,cAAc,UAAiB;IAK/F;IAEA,MAAMS,UAAUC,YAAY;QACxBL,iBAAiB;YACb,MAAMM,UAAU,MAAMV;YACtB,IAAI,CAACU,SAAS,YACVR,aAAa;YAGjBD;QACJ;IACJ,GAAG;QAACD;QAAeC;KAAe;IAElC,IAAI,CAACJ,gBACD,OAAO;IAGX,OAAO,WAAP,GACI,oBAAC;QAAI,WAAU;qBACX,oBAACU,MAAIA;QAAC,IAAI;QAAO,WAAW;OAAS,+FAGrC,oBAACI,QAAMA;QAAC,SAASH;QAAS,MAAM;;AAG5C"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { IRecordLockingViewModel } from "../abstractions.js";
|
|
3
|
+
interface LockedEntryOverlayProps {
|
|
4
|
+
vm: IRecordLockingViewModel;
|
|
5
|
+
entryTitle?: string;
|
|
6
|
+
onForceUnlock: () => Promise<boolean>;
|
|
7
|
+
onNavigateBack: () => void;
|
|
8
|
+
}
|
|
9
|
+
export declare const LockedEntryOverlay: ({ vm, entryTitle, onForceUnlock, onNavigateBack }: LockedEntryOverlayProps) => React.JSX.Element;
|
|
10
|
+
export {};
|