Skip to content

Commit d167d19

Browse files
committed
WEB-109 Extract DocumentContext to its own file, refactor unsaved changes into context
1 parent 0b32b19 commit d167d19

6 files changed

Lines changed: 92 additions & 75 deletions

File tree

src/app/editor/[id]/[slug]/SaveButton.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
import { createUsePuck } from "@puckeditor/core";
44
import { useTransition } from "react";
55
import { toast } from "sonner";
6-
import { useDocumentContext } from "./client";
6+
import { useDocumentContext } from "./document-context";
7+
import { useUnsavedChangesContext } from "./unsaved-changes-context";
78
import { saveVersionAction } from "../../../../lib/documents/actions";
89
import { getEditorUrl, getPreviewUrl } from "../../../../lib/editor-url";
910
import { runAction } from "../../runAction";
@@ -13,7 +14,8 @@ const usePuck = createUsePuck();
1314

1415
export function SaveButton() {
1516
const data = usePuck((s) => s.appState.data);
16-
const { documentId, documentName, isArchived, isDirty, addVersion, clearUnsavedChangesGuard } = useDocumentContext();
17+
const { documentId, documentName, isArchived, isDirty, addVersion } = useDocumentContext();
18+
const { clearUnsavedChangesGuard } = useUnsavedChangesContext();
1719
const { alert } = useDialogs();
1820

1921
const [isSaving, startTransition] = useTransition();

src/app/editor/[id]/[slug]/VersionPluginContainer.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
import { useTransition } from "react";
44
import { useRouter } from "next/navigation";
5-
import { useDocumentContext } from "./client";
5+
import { useDocumentContext } from "./document-context";
6+
import { useUnsavedChangesContext } from "./unsaved-changes-context";
67
import { publishVersionAction } from "../../../../lib/documents/actions";
78
import { getEditorUrl, getPreviewUrl } from "../../../../lib/editor-url";
89
import { runAction } from "../../runAction";
@@ -12,7 +13,8 @@ import { toast } from "sonner";
1213

1314
export function VersionPluginContainer() {
1415
const router = useRouter();
15-
const { documentId, documentName, versionId, publishedVersionId, versions, isArchived, confirmDiscardChanges, setPublishedVersionId } = useDocumentContext();
16+
const { documentId, documentName, versionId, publishedVersionId, versions, isArchived, setPublishedVersionId } = useDocumentContext();
17+
const { confirmDiscardChanges } = useUnsavedChangesContext();
1618
const [isPublishing, startTransition] = useTransition();
1719
const { alert } = useDialogs();
1820

@@ -35,11 +37,7 @@ export function VersionPluginContainer() {
3537
return;
3638
}
3739

38-
const shouldLeave = await confirmDiscardChanges({
39-
message: "You have unsaved changes. Leave this version without saving?",
40-
actionLabel: "Leave",
41-
destructive: true,
42-
});
40+
const shouldLeave = await confirmDiscardChanges();
4341

4442
if (!shouldLeave) {
4543
return;
Lines changed: 26 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,17 @@
11
"use client";
22

33
import type { Data } from "@puckeditor/core";
4-
import { Puck } from "@puckeditor/core";
4+
import { Puck, blocksPlugin, outlinePlugin } from "@puckeditor/core";
55
import config from "../../../../puck.config";
6-
import { useState, createContext, useContext, useCallback } from "react";
6+
import { useState, useCallback } from "react";
77
import { VersionPlugin } from "./VersionPlugin";
8-
import { blocksPlugin, outlinePlugin } from "@puckeditor/core";
98
import { ActionBarOverride } from "./ActionBarOverride";
109
import { SaveButton } from "./SaveButton";
1110
import { MediaProvider, type MediaWithUrl } from "@/components/puck/media-context";
1211
import type { Version } from "../../../../lib/types";
13-
import { useUnsavedChangesGuard, type ConfirmDiscardChangesOptions } from "./useUnsavedChangesGuard";
14-
15-
type DocumentContextType = {
16-
documentId: number;
17-
documentName: string;
18-
versionId?: number;
19-
publishedVersionId?: number;
20-
versions: Version[];
21-
isArchived: boolean;
22-
isDirty: boolean;
23-
addVersion: (version: Version) => void;
24-
confirmDiscardChanges: (options?: ConfirmDiscardChangesOptions) => Promise<boolean>;
25-
clearUnsavedChangesGuard: () => Promise<void>;
26-
setPublishedVersionId: (id: number) => void;
27-
};
28-
29-
const DocumentContext = createContext<DocumentContextType>({
30-
documentId: 0,
31-
documentName: "",
32-
versions: [],
33-
isArchived: false,
34-
isDirty: false,
35-
addVersion: () => {},
36-
confirmDiscardChanges: async () => true,
37-
clearUnsavedChangesGuard: async () => {},
38-
setPublishedVersionId: () => {},
39-
});
40-
41-
export function useDocumentContext() {
42-
return useContext(DocumentContext);
43-
}
12+
import { useUnsavedChangesGuard } from "./useUnsavedChangesGuard";
13+
import { DocumentContext } from "./document-context";
14+
import { UnsavedChangesContext } from "./unsaved-changes-context";
4415

4516
export function Client({
4617
documentId,
@@ -75,25 +46,27 @@ export function Client({
7546

7647
return (
7748
<MediaProvider media={media}>
78-
<DocumentContext.Provider
79-
value={{ documentId, documentName, versionId, publishedVersionId, versions, isArchived, isDirty, addVersion, confirmDiscardChanges, clearUnsavedChangesGuard, setPublishedVersionId }}
80-
>
81-
<Puck
82-
config={config}
83-
data={data}
84-
onChange={() => setIsDirty(true)}
85-
ui={{plugin: {current: "version-plugin"}}}
86-
plugins={[VersionPlugin, blocksPlugin(), outlinePlugin()]}
87-
permissions={isArchived
88-
? { drag: false, duplicate: false, delete: false, edit: false, insert: false }
89-
: { duplicate: false } // We replace this with our own, to avoid an icon collision
90-
}
91-
overrides={{
92-
actionBar: ActionBarOverride,
93-
headerActions: SaveButton
94-
}}
95-
/>
96-
</DocumentContext.Provider>
49+
<DocumentContext.Provider
50+
value={{ documentId, documentName, versionId, publishedVersionId, versions, isArchived, isDirty, addVersion, setPublishedVersionId }}
51+
>
52+
<UnsavedChangesContext.Provider value={{ confirmDiscardChanges, clearUnsavedChangesGuard }}>
53+
<Puck
54+
config={config}
55+
data={data}
56+
onChange={() => setIsDirty(true)}
57+
ui={{ plugin: { current: "version-plugin" } }}
58+
plugins={[VersionPlugin, blocksPlugin(), outlinePlugin()]}
59+
permissions={isArchived
60+
? { drag: false, duplicate: false, delete: false, edit: false, insert: false }
61+
: { duplicate: false } // We replace this with our own, to avoid an icon collision
62+
}
63+
overrides={{
64+
actionBar: ActionBarOverride,
65+
headerActions: SaveButton
66+
}}
67+
/>
68+
</UnsavedChangesContext.Provider>
69+
</DocumentContext.Provider>
9770
</MediaProvider>
9871
);
9972
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"use client";
2+
3+
import { createContext, useContext } from "react";
4+
import type { Version } from "../../../../lib/types";
5+
6+
type DocumentContextType = {
7+
documentId: number;
8+
documentName: string;
9+
versionId?: number;
10+
publishedVersionId?: number;
11+
versions: Version[];
12+
isArchived: boolean;
13+
isDirty: boolean;
14+
addVersion: (version: Version) => void;
15+
setPublishedVersionId: (id: number) => void;
16+
};
17+
18+
const DocumentContext = createContext<DocumentContextType>({
19+
documentId: 0,
20+
documentName: "",
21+
versions: [],
22+
isArchived: false,
23+
isDirty: false,
24+
addVersion: () => {},
25+
setPublishedVersionId: () => {},
26+
});
27+
28+
export function useDocumentContext() {
29+
return useContext(DocumentContext);
30+
}
31+
32+
export { DocumentContext };
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"use client";
2+
3+
import { createContext, useContext } from "react";
4+
5+
type UnsavedChangesContextType = {
6+
confirmDiscardChanges: () => Promise<boolean>;
7+
clearUnsavedChangesGuard: () => Promise<void>;
8+
};
9+
10+
const UnsavedChangesContext = createContext<UnsavedChangesContextType>({
11+
confirmDiscardChanges: async () => true,
12+
clearUnsavedChangesGuard: async () => {},
13+
});
14+
15+
export function useUnsavedChangesContext() {
16+
return useContext(UnsavedChangesContext);
17+
}
18+
19+
export { UnsavedChangesContext };

src/app/editor/[id]/[slug]/useUnsavedChangesGuard.ts

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,7 @@
33
import { useCallback, useEffect, useRef } from "react";
44
import { useDialogs } from "@/components/ui/dialog-provider";
55

6-
export type ConfirmDiscardChangesOptions = {
7-
message?: string;
8-
actionLabel?: string;
9-
destructive?: boolean;
10-
};
11-
12-
const DEFAULT_LEAVE_MESSAGE = "You have unsaved changes. Leave this page without saving?";
13-
const DEFAULT_LEAVE_ACTION_LABEL = "Leave";
6+
const LEAVE_MESSAGE = "You have unsaved changes. Leave this page without saving?";
147

158
export function useUnsavedChangesGuard(isDirty: boolean) {
169
const { confirm } = useDialogs();
@@ -32,15 +25,15 @@ export function useUnsavedChangesGuard(isDirty: boolean) {
3225
});
3326
}, []);
3427

35-
const confirmDiscardChanges = useCallback(async (options: ConfirmDiscardChangesOptions = {}) => {
28+
const confirmDiscardChanges = useCallback(async () => {
3629
if (!isDirty) {
3730
return true;
3831
}
3932

4033
const shouldLeave = await confirm({
41-
message: options.message ?? DEFAULT_LEAVE_MESSAGE,
42-
actionLabel: options.actionLabel ?? DEFAULT_LEAVE_ACTION_LABEL,
43-
destructive: options.destructive ?? true,
34+
message: LEAVE_MESSAGE,
35+
actionLabel: "Leave",
36+
destructive: true,
4437
});
4538

4639
if (!shouldLeave) {
@@ -74,7 +67,7 @@ export function useUnsavedChangesGuard(isDirty: boolean) {
7467
return;
7568
}
7669

77-
const shouldLeave = window.confirm(DEFAULT_LEAVE_MESSAGE);
70+
const shouldLeave = window.confirm(LEAVE_MESSAGE);
7871

7972
if (!shouldLeave) {
8073
window.history.pushState(window.history.state, "", window.location.href);

0 commit comments

Comments
 (0)