slice-machine-ui 1.23.2-beta.8 → 1.24.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/.depcheckrc +3 -0
- package/components/ChangesEmptyState/OfflinePage.tsx +1 -1
- package/components/ChangesItems/ChangesItems.tsx +6 -2
- package/components/CustomTypeTable/changesPage.tsx +12 -7
- package/components/DeleteSliceModal/index.tsx +3 -0
- package/components/DeleteVariationModal/index.tsx +1 -0
- package/components/Forms/CreateCustomTypeModal/CreateCustomTypeModal.tsx +4 -0
- package/components/Forms/CreateSliceModal/CreateSliceModal.tsx +3 -1
- package/components/Forms/RenameCustomTypeModal/RenameCustomTypeModal.tsx +6 -1
- package/components/Forms/RenameSliceModal/RenameSliceModal.tsx +3 -0
- package/components/Forms/RenameVariationModal/RenameVariationModal.tsx +0 -1
- package/components/LoginModal/index.tsx +40 -2
- package/components/ModalFormCard/index.tsx +0 -1
- package/components/Navigation/ChangesItem.tsx +117 -0
- package/components/Navigation/Environment.tsx +51 -4
- package/components/Navigation/index.css.ts +1 -2
- package/components/Navigation/index.test.tsx +5 -85
- package/components/Navigation/index.tsx +32 -20
- package/components/ReviewModal/ReviewForm.tsx +0 -1
- package/lib/builders/SliceBuilder/index.tsx +3 -3
- package/lib/builders/common/EditModal/Form.jsx +0 -1
- package/lib/builders/common/Zone/Card/components/NewField.tsx +0 -1
- package/lib/models/common/ModelStatus/index.ts +25 -0
- package/out/404.html +1 -1
- package/out/_next/static/chunks/18539003-5f78714a35fde4a1.js +97 -0
- package/out/_next/static/chunks/25-3aa4d4b24167d340.js +1 -0
- package/out/_next/static/chunks/318-19dbcae10cdb6408.js +1 -0
- package/out/_next/static/chunks/727-de03055e40815a3a.js +1 -0
- package/out/_next/static/chunks/777-8c5507e3b119b5dc.js +8 -0
- package/out/_next/static/chunks/799-0af1215eb2b3c17e.js +1 -0
- package/out/_next/static/chunks/904-40686b0426716111.js +1 -0
- package/out/_next/static/chunks/pages/_app-bca191df4ac7078e.js +434 -0
- package/out/_next/static/chunks/pages/{changelog-afd18276fe018b24.js → changelog-cb1df3a70b20f1b8.js} +1 -1
- package/out/_next/static/chunks/pages/changes-ccfd715c2bdc4f83.js +1 -0
- package/out/_next/static/chunks/pages/{labs-ee206005948f8e87.js → labs-2adfb5127f9c4081.js} +1 -1
- package/out/_next/static/chunks/pages/settings-94bcd7422e8b237d.js +1 -0
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]/{simulator-52c3e1f9d14160b8.js → simulator-aaf65fbec8f5839c.js} +1 -1
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-90bb2ea76d2abf42.js +1 -0
- package/out/_next/static/chunks/pages/slices-e0ce615c38e8338d.js +1 -0
- package/out/_next/static/css/39e089aad1742a3a.css +1 -0
- package/out/_next/static/css/{37153180c3b0dd66.css → 838fd8bb5a7c32b0.css} +1 -1
- package/out/_next/static/css/ca32882a5d2d8d88.css +1 -0
- package/out/_next/static/eNujlD43QIdmra7ggcNtK/_buildManifest.js +1 -0
- package/out/changelog.html +1 -1
- package/out/changes.html +1 -1
- package/out/custom-types/[customTypeId].html +1 -1
- package/out/custom-types.html +1 -1
- package/out/index.html +1 -1
- package/out/labs.html +1 -1
- package/out/page-types/[pageTypeId].html +1 -1
- package/out/settings.html +1 -0
- package/out/slices/[lib]/[sliceName]/[variation]/simulator.html +1 -1
- package/out/slices/[lib]/[sliceName]/[variation].html +1 -1
- package/out/slices.html +1 -1
- package/package.json +10 -8
- package/pages/_app.tsx +34 -32
- package/pages/changes.tsx +27 -41
- package/pages/settings.tsx +1 -0
- package/postcss.config.js +10 -0
- package/src/components/FieldSet/FieldSet.stories.tsx +20 -11
- package/src/components/RelativeTime/RelativeTime.stories.tsx +25 -0
- package/src/components/RelativeTime/RelativeTime.tsx +40 -0
- package/src/components/RelativeTime/index.ts +1 -0
- package/src/components/SideNav/SideNav.css.ts +1 -1
- package/src/components/SideNav/SideNav.tsx +9 -5
- package/src/components/SideNav/SideNavEnvironmentSelector.tsx +31 -11
- package/src/domain/__tests__/customType.test.ts +26 -19
- package/src/domain/customType.ts +22 -5
- package/src/features/autoSave/AutoSaveStatusIndicator.tsx +4 -4
- package/src/features/customTypes/customTypesBuilder/CustomTypeProvider.tsx +11 -10
- package/src/features/customTypes/customTypesBuilder/CustomTypesBuilderPage.tsx +3 -3
- package/src/features/environments/actions/setEnvironment.ts +4 -4
- package/src/features/settings/SettingsPage.tsx +47 -0
- package/src/features/settings/git/ConnectGitRepository.tsx +126 -0
- package/src/features/settings/git/ConnectGitRepositoryBlankSlate.tsx +33 -0
- package/src/features/settings/git/GitOwnerSelect.tsx +71 -0
- package/src/features/settings/git/GitProvider.ts +40 -0
- package/src/features/settings/git/GitProviderConnectButtons.tsx +63 -0
- package/src/features/settings/git/GitRepositoriesList.tsx +76 -0
- package/src/features/settings/git/GitRepositoriesSearch.tsx +69 -0
- package/src/features/settings/git/GitRepositoryConnectDialog.tsx +85 -0
- package/src/features/settings/git/GitRepositoryDisconnectDialog.tsx +60 -0
- package/src/features/settings/git/useGitIntegrationExperiment.ts +8 -0
- package/src/features/settings/git/useGitOwners.ts +12 -0
- package/src/features/settings/git/useGitRepos.ts +24 -0
- package/src/features/settings/git/useLinkedGitRepos.ts +41 -0
- package/src/features/settings/git/useWriteAPIToken.ts +23 -0
- package/src/features/slices/convertLegacySlice/ConvertLegacySliceAsNewSliceDialog.tsx +0 -1
- package/src/features/slices/convertLegacySlice/ConvertLegacySliceAsNewVariationDialog.tsx +0 -1
- package/src/features/slices/convertLegacySlice/ConvertLegacySliceMergeWithIdenticalDialog.tsx +0 -1
- package/src/features/slices/sliceBuilder/SliceBuilderProvider.tsx +12 -10
- package/src/features/sync/AutoSyncProvider.tsx +252 -0
- package/src/features/sync/components/AutoSyncStatusIndicator.module.css +17 -0
- package/src/features/sync/components/AutoSyncStatusIndicator.module.css.d.ts +5 -0
- package/src/features/sync/components/AutoSyncStatusIndicator.tsx +58 -0
- package/src/features/sync/fetchUnSyncChanges.ts +40 -0
- package/src/features/sync/getUnSyncChanges.ts +144 -0
- package/src/features/sync/useUnSyncChanges.ts +32 -0
- package/src/hooks/useActionQueue.tsx +177 -0
- package/src/hooks/useAuthStatus.ts +12 -0
- package/src/hooks/useUser.ts +14 -0
- package/src/icons/SettingsIcon.tsx +19 -0
- package/src/icons/SyncFailed.tsx +30 -0
- package/src/icons/Synced.tsx +19 -0
- package/src/icons/Syncing.tsx +23 -0
- package/test/__testutils__/index.tsx +8 -5
- package/test/src/modules/__fixtures__/serverState.ts +1 -2
- package/components/Navigation/ChangesListItem.tsx +0 -74
- package/components/Navigation/ChangesRightElement.tsx +0 -33
- package/out/_next/static/chunks/18539003-5cc559566b738915.js +0 -97
- package/out/_next/static/chunks/25-01f422cea8066349.js +0 -1
- package/out/_next/static/chunks/318-387e3f23d717c53d.js +0 -1
- package/out/_next/static/chunks/376-7e784d6069e01654.js +0 -8
- package/out/_next/static/chunks/727-358d7fae68a880b3.js +0 -1
- package/out/_next/static/chunks/799-d95a93e965557aeb.js +0 -1
- package/out/_next/static/chunks/904-1ff7519adece2b55.js +0 -1
- package/out/_next/static/chunks/pages/_app-e31adcf75e9176d3.js +0 -432
- package/out/_next/static/chunks/pages/changes-92c84f6cac0285fd.js +0 -1
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-608d206d10a719e3.js +0 -1
- package/out/_next/static/chunks/pages/slices-b0b58192a87df822.js +0 -1
- package/out/_next/static/css/200244e3e0463c39.css +0 -1
- package/out/_next/static/vxOhh_oqUIQ5PTSKUKgMR/_buildManifest.js +0 -1
- package/src/features/autoSave/useAutoSave.tsx +0 -177
- package/src/features/legacyState/actions/getLegacySliceMachineState.ts +0 -53
- package/src/features/legacyState/useLegacySliceMachineState.ts +0 -7
- package/src/hooks/useModelStatus.ts +0 -78
- package/src/hooks/useUnSyncChanges.ts +0 -88
- package/test/components/LoginModal.test.tsx +0 -67
- package/test/src/hooks/useModelStatus.test.ts +0 -115
- /package/out/_next/static/{vxOhh_oqUIQ5PTSKUKgMR → eNujlD43QIdmra7ggcNtK}/_ssgManifest.js +0 -0
- /package/src/features/{changes → sync}/actions/pushChanges.ts +0 -0
- /package/src/features/{changes → sync}/actions/trackPushChangesSuccess.ts +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
self.__BUILD_MANIFEST=function(s,c,a,e,t,i,b,f,n,u,d,h,l,k,j,p,g,o,r){return{__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":[a,j,p,"static/chunks/pages/index-cf93d16e9c63b6db.js"],"/_error":["static/chunks/pages/_error-69b4f498bafdd188.js"],"/changelog":[s,e,"static/chunks/870-0c7a8a9622de78b7.js","static/chunks/pages/changelog-afd18276fe018b24.js"],"/changes":[s,t,"static/chunks/8eec4907-b712959d9f984b68.js",i,"static/chunks/918-5bb55fb464e34768.js",c,f,"static/css/37153180c3b0dd66.css","static/chunks/pages/changes-92c84f6cac0285fd.js"],"/custom-types":[a,j,p,"static/chunks/pages/custom-types-3c8e721ec6802127.js"],"/custom-types/[customTypeId]":[s,n,u,d,h,e,b,i,l,g,c,a,k,o,r,"static/chunks/pages/custom-types/[customTypeId]-97ab3b90b85d645d.js"],"/labs":["static/chunks/pages/labs-ee206005948f8e87.js"],"/page-types/[pageTypeId]":[s,n,u,d,h,e,b,i,l,g,c,a,k,o,r,"static/chunks/pages/page-types/[pageTypeId]-84a22c5e480d3fa9.js"],"/slices":[s,t,b,c,f,"static/css/f8eb526e10bc2441.css","static/chunks/pages/slices-b0b58192a87df822.js"],"/slices/[lib]/[sliceName]/[variation]":[s,t,n,u,d,h,e,b,i,l,c,k,f,"static/css/b22c0750c14cc089.css","static/chunks/pages/slices/[lib]/[sliceName]/[variation]-608d206d10a719e3.js"],"/slices/[lib]/[sliceName]/[variation]/simulator":[t,"static/chunks/72585f70-28b4d7d5384b3703.js","static/chunks/18539003-5cc559566b738915.js","static/chunks/376-7e784d6069e01654.js","static/chunks/pages/slices/[lib]/[sliceName]/[variation]/simulator-52c3e1f9d14160b8.js"],sortedPages:["/","/_app","/_error","/changelog","/changes","/custom-types","/custom-types/[customTypeId]","/labs","/page-types/[pageTypeId]","/slices","/slices/[lib]/[sliceName]/[variation]","/slices/[lib]/[sliceName]/[variation]/simulator"]}}("static/chunks/59b4e022-7a3b5554b806349f.js","static/chunks/25-01f422cea8066349.js","static/chunks/799-d95a93e965557aeb.js","static/chunks/183-4feb6bb3eddbc768.js","static/chunks/c8eae200-966ce352f7b5d2b9.js","static/chunks/543-f8766685f7f451e2.js","static/chunks/50-cfc2514205b86001.js","static/chunks/286-ed90373e9a469944.js","static/chunks/f36c6662-03bb5e9d9aa0b0aa.js","static/chunks/4c744e84-e3161d754618373f.js","static/chunks/065a3ddb-9a38ca0d60f0bf2f.js","static/chunks/1cc2734a-2c5b6c0367fcde86.js","static/chunks/696-b12edad30a3617e5.js","static/chunks/318-387e3f23d717c53d.js","static/chunks/727-358d7fae68a880b3.js","static/css/be1f49e49a5aa7eb.css","static/chunks/474-5a072e6fac61f703.js","static/chunks/904-1ff7519adece2b55.js","static/css/72465cc1225f28a2.css"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
useCallback,
|
|
3
|
-
useState,
|
|
4
|
-
useEffect,
|
|
5
|
-
SetStateAction,
|
|
6
|
-
Dispatch,
|
|
7
|
-
useMemo,
|
|
8
|
-
} from "react";
|
|
9
|
-
import { uniqueId } from "lodash";
|
|
10
|
-
import { toast } from "react-toastify";
|
|
11
|
-
import { Box, Button, Text } from "@prismicio/editor-ui";
|
|
12
|
-
|
|
13
|
-
export type AutoSaveStatus = "saving" | "saved" | "failed";
|
|
14
|
-
|
|
15
|
-
type UseAutoSaveArgs = {
|
|
16
|
-
autoSaveStatusDelay?: number;
|
|
17
|
-
errorMessage?: string;
|
|
18
|
-
retryDelay?: number;
|
|
19
|
-
retryMessage?: string;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
type UseAutoSaveReturnType = {
|
|
23
|
-
autoSaveStatus: AutoSaveStatus;
|
|
24
|
-
setNextSave: (nextSave: NextSave) => void;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
type AutoSaveStack = {
|
|
28
|
-
pendingSave: NextSave | undefined;
|
|
29
|
-
nextSave: NextSave | undefined;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
type NextSave = () => Promise<unknown>;
|
|
33
|
-
|
|
34
|
-
export const useAutoSave = (
|
|
35
|
-
args: UseAutoSaveArgs = {},
|
|
36
|
-
): UseAutoSaveReturnType => {
|
|
37
|
-
const {
|
|
38
|
-
autoSaveStatusDelay = 300,
|
|
39
|
-
errorMessage = "An error happened while saving",
|
|
40
|
-
retryDelay = 1000,
|
|
41
|
-
retryMessage = "Retry",
|
|
42
|
-
} = args;
|
|
43
|
-
|
|
44
|
-
const [autoSaveStack, setAutoSaveStack] = useState<AutoSaveStack>({
|
|
45
|
-
pendingSave: undefined,
|
|
46
|
-
nextSave: undefined,
|
|
47
|
-
});
|
|
48
|
-
const [autoSaveStatusActual, setAutoSaveStatusActual] =
|
|
49
|
-
useState<AutoSaveStatus>("saved");
|
|
50
|
-
const [autoSaveStatusDelayed, setAutoSaveStatusDelayed] =
|
|
51
|
-
useState<AutoSaveStatus>(autoSaveStatusActual);
|
|
52
|
-
|
|
53
|
-
const setNextSave = useCallback((nextSave: NextSave) => {
|
|
54
|
-
setAutoSaveStack((prevState) => ({
|
|
55
|
-
...prevState,
|
|
56
|
-
nextSave,
|
|
57
|
-
}));
|
|
58
|
-
}, []);
|
|
59
|
-
|
|
60
|
-
const executeSave = useCallback(
|
|
61
|
-
async (nextSave?: NextSave) => {
|
|
62
|
-
if (nextSave) {
|
|
63
|
-
setAutoSaveStatusActual("saving");
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
await nextSave();
|
|
67
|
-
|
|
68
|
-
setAutoSaveStatusActual("saved");
|
|
69
|
-
} catch (error) {
|
|
70
|
-
setAutoSaveStatusActual("failed");
|
|
71
|
-
console.error(errorMessage, error);
|
|
72
|
-
|
|
73
|
-
toastError({
|
|
74
|
-
errorMessage,
|
|
75
|
-
retryDelay,
|
|
76
|
-
retryMessage,
|
|
77
|
-
setAutoSaveStatusActual,
|
|
78
|
-
setAutoSaveStack,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
[errorMessage, retryDelay, retryMessage],
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
useEffect(() => {
|
|
87
|
-
if (autoSaveStatusActual === "saved" && autoSaveStack.nextSave) {
|
|
88
|
-
void executeSave(autoSaveStack.nextSave);
|
|
89
|
-
|
|
90
|
-
setAutoSaveStack({
|
|
91
|
-
pendingSave: autoSaveStack.nextSave,
|
|
92
|
-
nextSave: undefined,
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
}, [autoSaveStatusActual, autoSaveStack, executeSave]);
|
|
96
|
-
|
|
97
|
-
useEffect(() => {
|
|
98
|
-
if (autoSaveStatusActual === "saving") {
|
|
99
|
-
setAutoSaveStatusDelayed("saving");
|
|
100
|
-
} else {
|
|
101
|
-
const delayedTimeout = setTimeout(() => {
|
|
102
|
-
setAutoSaveStatusDelayed(autoSaveStatusActual);
|
|
103
|
-
}, autoSaveStatusDelay);
|
|
104
|
-
|
|
105
|
-
return () => {
|
|
106
|
-
clearTimeout(delayedTimeout);
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return;
|
|
111
|
-
}, [autoSaveStatusActual, autoSaveStatusDelay]);
|
|
112
|
-
|
|
113
|
-
return useMemo(
|
|
114
|
-
() => ({
|
|
115
|
-
autoSaveStatus: autoSaveStatusDelayed,
|
|
116
|
-
setNextSave,
|
|
117
|
-
}),
|
|
118
|
-
[autoSaveStatusDelayed, setNextSave],
|
|
119
|
-
);
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
type ToastErrorArgs = {
|
|
123
|
-
errorMessage: string;
|
|
124
|
-
retryDelay: number;
|
|
125
|
-
retryMessage: string;
|
|
126
|
-
setAutoSaveStatusActual: Dispatch<SetStateAction<AutoSaveStatus>>;
|
|
127
|
-
setAutoSaveStack: Dispatch<SetStateAction<AutoSaveStack>>;
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
function toastError(args: ToastErrorArgs) {
|
|
131
|
-
const {
|
|
132
|
-
errorMessage,
|
|
133
|
-
retryDelay,
|
|
134
|
-
retryMessage,
|
|
135
|
-
setAutoSaveStatusActual,
|
|
136
|
-
setAutoSaveStack,
|
|
137
|
-
} = args;
|
|
138
|
-
const toastId = uniqueId();
|
|
139
|
-
|
|
140
|
-
toast.error(
|
|
141
|
-
() => (
|
|
142
|
-
<Box gap={8} alignItems="center">
|
|
143
|
-
<Text color="grey1" variant="small">
|
|
144
|
-
{errorMessage}
|
|
145
|
-
</Text>
|
|
146
|
-
<Button
|
|
147
|
-
size="small"
|
|
148
|
-
color="grey"
|
|
149
|
-
onClick={() => {
|
|
150
|
-
toast.dismiss(toastId);
|
|
151
|
-
|
|
152
|
-
// Ensure saving is directly propagated before waiting for the retry
|
|
153
|
-
setAutoSaveStatusActual("saving");
|
|
154
|
-
|
|
155
|
-
setTimeout(() => {
|
|
156
|
-
// By setting the status to saved, the save effect will be triggered
|
|
157
|
-
// with the latest data to save.
|
|
158
|
-
setAutoSaveStatusActual("saved");
|
|
159
|
-
setAutoSaveStack((prevState) => ({
|
|
160
|
-
nextSave: prevState.nextSave ?? prevState.pendingSave,
|
|
161
|
-
pendingSave: undefined,
|
|
162
|
-
}));
|
|
163
|
-
}, retryDelay);
|
|
164
|
-
}}
|
|
165
|
-
>
|
|
166
|
-
{retryMessage}
|
|
167
|
-
</Button>
|
|
168
|
-
</Box>
|
|
169
|
-
),
|
|
170
|
-
{
|
|
171
|
-
autoClose: false,
|
|
172
|
-
closeOnClick: false,
|
|
173
|
-
draggable: false,
|
|
174
|
-
toastId,
|
|
175
|
-
},
|
|
176
|
-
);
|
|
177
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import ServerState from "@models/server/ServerState";
|
|
2
|
-
import { Slices } from "@lib/models/common/Slice";
|
|
3
|
-
import { CustomTypes } from "@lib/models/common/CustomType";
|
|
4
|
-
import { managerClient } from "@src/managerClient";
|
|
5
|
-
|
|
6
|
-
export async function getLegacySliceMachineState() {
|
|
7
|
-
const rawState = await managerClient.getState();
|
|
8
|
-
|
|
9
|
-
// `rawState` from the client contains non-SM-specific models. We need to
|
|
10
|
-
// transform the data to something SM recognizes.
|
|
11
|
-
const state: ServerState = {
|
|
12
|
-
...rawState,
|
|
13
|
-
libraries: rawState.libraries.map((library) => {
|
|
14
|
-
return {
|
|
15
|
-
...library,
|
|
16
|
-
components: library.components.map((component) => {
|
|
17
|
-
return {
|
|
18
|
-
...component,
|
|
19
|
-
model: Slices.toSM(component.model),
|
|
20
|
-
|
|
21
|
-
// Replace screnshot Blobs with URLs.
|
|
22
|
-
screenshots: Object.fromEntries(
|
|
23
|
-
Object.entries(component.screenshots).map(
|
|
24
|
-
([variationID, screenshot]) => {
|
|
25
|
-
return [
|
|
26
|
-
variationID,
|
|
27
|
-
{
|
|
28
|
-
...screenshot,
|
|
29
|
-
url: URL.createObjectURL(screenshot.data),
|
|
30
|
-
},
|
|
31
|
-
];
|
|
32
|
-
},
|
|
33
|
-
),
|
|
34
|
-
),
|
|
35
|
-
};
|
|
36
|
-
}),
|
|
37
|
-
};
|
|
38
|
-
}),
|
|
39
|
-
customTypes: rawState.customTypes.map((customTypeModel) => {
|
|
40
|
-
return CustomTypes.toSM(customTypeModel);
|
|
41
|
-
}),
|
|
42
|
-
remoteCustomTypes: rawState.remoteCustomTypes.map(
|
|
43
|
-
(remoteCustomTypeModel) => {
|
|
44
|
-
return CustomTypes.toSM(remoteCustomTypeModel);
|
|
45
|
-
},
|
|
46
|
-
),
|
|
47
|
-
remoteSlices: rawState.remoteSlices.map((remoteSliceModel) => {
|
|
48
|
-
return Slices.toSM(remoteSliceModel);
|
|
49
|
-
}),
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
return state;
|
|
53
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
computeModelStatus,
|
|
3
|
-
ModelStatus,
|
|
4
|
-
} from "@lib/models/common/ModelStatus";
|
|
5
|
-
import { getAuthStatus } from "@src/modules/environment";
|
|
6
|
-
import { AuthStatus } from "@src/modules/userContext/types";
|
|
7
|
-
import { SliceMachineStoreType } from "@src/redux/type";
|
|
8
|
-
import { useSelector } from "react-redux";
|
|
9
|
-
import { useNetwork } from "./useNetwork";
|
|
10
|
-
import {
|
|
11
|
-
hasLocal,
|
|
12
|
-
LocalOrRemoteCustomType,
|
|
13
|
-
LocalOrRemoteModel,
|
|
14
|
-
LocalOrRemoteSlice,
|
|
15
|
-
} from "@lib/models/common/ModelData";
|
|
16
|
-
|
|
17
|
-
// Slices and Custom Types needs to be separated as Ids are not unique amongst each others.
|
|
18
|
-
export interface ModelStatusInformation {
|
|
19
|
-
modelsStatuses: {
|
|
20
|
-
slices: { [sliceId: string]: ModelStatus };
|
|
21
|
-
customTypes: { [ctId: string]: ModelStatus };
|
|
22
|
-
};
|
|
23
|
-
authStatus: AuthStatus;
|
|
24
|
-
isOnline: boolean;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function computeStatuses(
|
|
28
|
-
models: LocalOrRemoteCustomType[],
|
|
29
|
-
userHasAccessToModels: boolean,
|
|
30
|
-
): { [sliceId: string]: ModelStatus };
|
|
31
|
-
function computeStatuses(
|
|
32
|
-
models: LocalOrRemoteSlice[],
|
|
33
|
-
userHasAccessToModels: boolean,
|
|
34
|
-
): { [sliceId: string]: ModelStatus };
|
|
35
|
-
function computeStatuses(
|
|
36
|
-
models: LocalOrRemoteModel[],
|
|
37
|
-
userHasAccessToModels: boolean,
|
|
38
|
-
) {
|
|
39
|
-
return models.reduce<{ [id: string]: ModelStatus }>(
|
|
40
|
-
(acc, model) => {
|
|
41
|
-
const { status } = computeModelStatus(model, userHasAccessToModels);
|
|
42
|
-
|
|
43
|
-
return {
|
|
44
|
-
...acc,
|
|
45
|
-
[hasLocal(model) ? model.local.id : model.remote.id]: status,
|
|
46
|
-
};
|
|
47
|
-
},
|
|
48
|
-
{} as { [sliceId: string]: ModelStatus },
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export const useModelStatus = ({
|
|
53
|
-
slices = [],
|
|
54
|
-
customTypes = [],
|
|
55
|
-
}: {
|
|
56
|
-
slices?: LocalOrRemoteSlice[];
|
|
57
|
-
customTypes?: LocalOrRemoteCustomType[];
|
|
58
|
-
}): ModelStatusInformation => {
|
|
59
|
-
const isOnline = useNetwork();
|
|
60
|
-
const { authStatus } = useSelector((store: SliceMachineStoreType) => ({
|
|
61
|
-
authStatus: getAuthStatus(store),
|
|
62
|
-
}));
|
|
63
|
-
const userHasAccessToModels =
|
|
64
|
-
isOnline &&
|
|
65
|
-
authStatus != AuthStatus.FORBIDDEN &&
|
|
66
|
-
authStatus != AuthStatus.UNAUTHORIZED;
|
|
67
|
-
|
|
68
|
-
const modelsStatuses = {
|
|
69
|
-
slices: computeStatuses(slices, userHasAccessToModels),
|
|
70
|
-
customTypes: computeStatuses(customTypes, userHasAccessToModels),
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
return {
|
|
74
|
-
modelsStatuses,
|
|
75
|
-
authStatus,
|
|
76
|
-
isOnline,
|
|
77
|
-
};
|
|
78
|
-
};
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { ComponentUI } from "@lib/models/common/ComponentUI";
|
|
2
|
-
import { ModelStatus } from "@lib/models/common/ModelStatus";
|
|
3
|
-
import { selectAllCustomTypes } from "@src/modules/availableCustomTypes";
|
|
4
|
-
import { getFrontendSlices, getLibraries } from "@src/modules/slices";
|
|
5
|
-
import { SliceMachineStoreType } from "@src/redux/type";
|
|
6
|
-
import { useSelector } from "react-redux";
|
|
7
|
-
import { ModelStatusInformation, useModelStatus } from "./useModelStatus";
|
|
8
|
-
import {
|
|
9
|
-
LocalOrRemoteCustomType,
|
|
10
|
-
RemoteOnlySlice,
|
|
11
|
-
getModelId,
|
|
12
|
-
isRemoteOnly,
|
|
13
|
-
} from "@lib/models/common/ModelData";
|
|
14
|
-
|
|
15
|
-
export const unSyncStatuses = [
|
|
16
|
-
ModelStatus.New,
|
|
17
|
-
ModelStatus.Modified,
|
|
18
|
-
ModelStatus.Deleted,
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
export interface UnSyncChanges extends ModelStatusInformation {
|
|
22
|
-
unSyncedSlices: ComponentUI[];
|
|
23
|
-
unSyncedCustomTypes: LocalOrRemoteCustomType[];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// ComponentUI are manipulated on all the relevant pages
|
|
27
|
-
// But the data is not available for remote only slices
|
|
28
|
-
// which have been deleted locally
|
|
29
|
-
// Should revisit this with the sync improvements
|
|
30
|
-
const wrapDeletedSlice = (s: RemoteOnlySlice): ComponentUI => ({
|
|
31
|
-
model: s.remote,
|
|
32
|
-
screenshots: {},
|
|
33
|
-
from: "",
|
|
34
|
-
href: "",
|
|
35
|
-
pathToSlice: "",
|
|
36
|
-
fileName: "",
|
|
37
|
-
extension: "",
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
export const useUnSyncChanges = (): UnSyncChanges => {
|
|
41
|
-
const { customTypes, slices, libraries } = useSelector(
|
|
42
|
-
(store: SliceMachineStoreType) => ({
|
|
43
|
-
customTypes: selectAllCustomTypes(store),
|
|
44
|
-
slices: getFrontendSlices(store),
|
|
45
|
-
libraries: getLibraries(store),
|
|
46
|
-
}),
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
const { modelsStatuses, authStatus, isOnline } = useModelStatus({
|
|
50
|
-
slices,
|
|
51
|
-
customTypes,
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
const localComponents: ComponentUI[] = libraries.flatMap(
|
|
55
|
-
(lib) => lib.components,
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
const deletedComponents: ComponentUI[] = slices
|
|
59
|
-
.filter(isRemoteOnly)
|
|
60
|
-
.map(wrapDeletedSlice);
|
|
61
|
-
|
|
62
|
-
const components: ComponentUI[] = localComponents
|
|
63
|
-
.concat(deletedComponents)
|
|
64
|
-
.sort((s1, s2) => (s1.model.name > s2.model.name ? 1 : -1));
|
|
65
|
-
|
|
66
|
-
const unSyncedSlices = components.filter(
|
|
67
|
-
(component) =>
|
|
68
|
-
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
69
|
-
modelsStatuses.slices[component.model.id] &&
|
|
70
|
-
unSyncStatuses.includes(modelsStatuses.slices[component.model.id]),
|
|
71
|
-
);
|
|
72
|
-
const unSyncedCustomTypes = customTypes.filter(
|
|
73
|
-
(customType) =>
|
|
74
|
-
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
75
|
-
modelsStatuses.customTypes[getModelId(customType)] &&
|
|
76
|
-
unSyncStatuses.includes(
|
|
77
|
-
modelsStatuses.customTypes[getModelId(customType)],
|
|
78
|
-
),
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
unSyncedSlices,
|
|
83
|
-
unSyncedCustomTypes,
|
|
84
|
-
modelsStatuses,
|
|
85
|
-
authStatus,
|
|
86
|
-
isOnline,
|
|
87
|
-
};
|
|
88
|
-
};
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
|
|
3
|
-
import { describe, test, expect, vi } from "vitest";
|
|
4
|
-
import React from "react";
|
|
5
|
-
import { render } from "@testing-library/react";
|
|
6
|
-
import LoginModal from "@components/LoginModal";
|
|
7
|
-
import { FrontEndEnvironment } from "@lib/models/common/Environment";
|
|
8
|
-
import { useSelector } from "react-redux";
|
|
9
|
-
|
|
10
|
-
const mockDispatch = vi.fn();
|
|
11
|
-
vi.mock("react-beautiful-dnd", () => {
|
|
12
|
-
return {};
|
|
13
|
-
});
|
|
14
|
-
vi.mock("react-redux", () => ({
|
|
15
|
-
useSelector: vi.fn(),
|
|
16
|
-
useDispatch: () => mockDispatch,
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
|
-
const useSelectorMock = vi.mocked(useSelector);
|
|
20
|
-
|
|
21
|
-
const App = () => <LoginModal />;
|
|
22
|
-
|
|
23
|
-
describe("LoginModal", () => {
|
|
24
|
-
window.open = vi.fn();
|
|
25
|
-
|
|
26
|
-
const div = document.createElement("div");
|
|
27
|
-
div.id = "__next";
|
|
28
|
-
document.body.appendChild(div);
|
|
29
|
-
|
|
30
|
-
test("when given a prismic url in env it should open to prismic.io/dashboard", () => {
|
|
31
|
-
useSelectorMock.mockImplementation(() => ({
|
|
32
|
-
env: {
|
|
33
|
-
manifest: {
|
|
34
|
-
apiEndpoint: "https://foo.prismic.io/api/v2",
|
|
35
|
-
},
|
|
36
|
-
} as FrontEndEnvironment,
|
|
37
|
-
isOpen: true,
|
|
38
|
-
isLoginLoading: true,
|
|
39
|
-
}));
|
|
40
|
-
const result = render(<App />);
|
|
41
|
-
|
|
42
|
-
expect(result.getByText("Click here").closest("a")).toHaveAttribute(
|
|
43
|
-
"href",
|
|
44
|
-
// Since we're not using the `start-slicemachine` server proxy, port defaults to Next/React Testing Library's default port
|
|
45
|
-
"https://prismic.io/dashboard/cli/login?source=slice-machine&port=3000&path=/api/auth",
|
|
46
|
-
);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test("when given wroom.io url it should open to wroom.io/dashboard", () => {
|
|
50
|
-
useSelectorMock.mockImplementation(() => ({
|
|
51
|
-
env: {
|
|
52
|
-
manifest: {
|
|
53
|
-
apiEndpoint: "https://foo.wroom.io/api/v2",
|
|
54
|
-
},
|
|
55
|
-
} as FrontEndEnvironment,
|
|
56
|
-
isOpen: true,
|
|
57
|
-
isLoginLoading: true,
|
|
58
|
-
}));
|
|
59
|
-
const result = render(<App />);
|
|
60
|
-
|
|
61
|
-
expect(result.getByText("Click here").closest("a")).toHaveAttribute(
|
|
62
|
-
"href",
|
|
63
|
-
// Since we're not using the `start-slicemachine` server proxy, port defaults to Next/React Testing Library's default port
|
|
64
|
-
"https://wroom.io/dashboard/cli/login?source=slice-machine&port=3000&path=/api/auth",
|
|
65
|
-
);
|
|
66
|
-
});
|
|
67
|
-
});
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { describe, test, beforeEach, expect, vi } from "vitest";
|
|
2
|
-
import { useModelStatus } from "../../../src/hooks/useModelStatus";
|
|
3
|
-
import * as networkHook from "../../../src/hooks/useNetwork";
|
|
4
|
-
import { AuthStatus } from "@src/modules/userContext/types";
|
|
5
|
-
|
|
6
|
-
import { Slices } from "@lib/models/common/Slice";
|
|
7
|
-
import SliceMock from "../../__fixtures__/sliceModel";
|
|
8
|
-
|
|
9
|
-
import { CustomTypes } from "@lib/models/common/CustomType";
|
|
10
|
-
import { customTypeMock } from "../../__fixtures__/customType";
|
|
11
|
-
import { ModelStatus } from "@lib/models/common/ModelStatus";
|
|
12
|
-
|
|
13
|
-
const mockSelector = vi.fn();
|
|
14
|
-
vi.mock("react-redux", async () => {
|
|
15
|
-
const actual: typeof import("react-redux") =
|
|
16
|
-
await vi.importActual("react-redux");
|
|
17
|
-
|
|
18
|
-
return {
|
|
19
|
-
...actual,
|
|
20
|
-
useSelector: () => mockSelector(),
|
|
21
|
-
};
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
const BaseSliceMock = {
|
|
25
|
-
...SliceMock,
|
|
26
|
-
variations: SliceMock.variations.map((variation) => {
|
|
27
|
-
// @ts-expect-error we know imageUrl is optional
|
|
28
|
-
delete variation["imageUrl"];
|
|
29
|
-
return variation;
|
|
30
|
-
}),
|
|
31
|
-
};
|
|
32
|
-
const sliceModel = Slices.toSM(BaseSliceMock);
|
|
33
|
-
const customTypeModel = CustomTypes.toSM(customTypeMock);
|
|
34
|
-
|
|
35
|
-
describe("[useModelStatus hook]", () => {
|
|
36
|
-
beforeEach(() => {
|
|
37
|
-
vi.clearAllMocks();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test("it should return the model status correctly", () => {
|
|
41
|
-
vi.spyOn(networkHook, "useNetwork").mockImplementation(() => true); // isOnline
|
|
42
|
-
mockSelector.mockReturnValue({ authStatus: AuthStatus.AUTHORIZED });
|
|
43
|
-
|
|
44
|
-
const result = useModelStatus({
|
|
45
|
-
slices: [{ local: sliceModel, remote: sliceModel, localScreenshots: {} }],
|
|
46
|
-
customTypes: [{ local: customTypeModel }],
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
expect(result).toEqual({
|
|
50
|
-
modelsStatuses: {
|
|
51
|
-
slices: { [sliceModel.id]: ModelStatus.Synced },
|
|
52
|
-
customTypes: { [customTypeModel.id]: ModelStatus.New },
|
|
53
|
-
},
|
|
54
|
-
authStatus: AuthStatus.AUTHORIZED,
|
|
55
|
-
isOnline: true,
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test("it should return Unknown status is the user doesn't have internet", () => {
|
|
60
|
-
vi.spyOn(networkHook, "useNetwork").mockImplementation(() => false); // isOnline
|
|
61
|
-
mockSelector.mockReturnValue({ authStatus: AuthStatus.AUTHORIZED });
|
|
62
|
-
|
|
63
|
-
const result = useModelStatus({
|
|
64
|
-
slices: [{ local: sliceModel, remote: sliceModel, localScreenshots: {} }],
|
|
65
|
-
customTypes: [{ local: customTypeModel }],
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
expect(result).toEqual({
|
|
69
|
-
modelsStatuses: {
|
|
70
|
-
slices: { [sliceModel.id]: ModelStatus.Unknown },
|
|
71
|
-
customTypes: { [customTypeModel.id]: ModelStatus.Unknown },
|
|
72
|
-
},
|
|
73
|
-
authStatus: AuthStatus.AUTHORIZED,
|
|
74
|
-
isOnline: false,
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
test("it should return Unknown status is the user is not connected to Prismic", () => {
|
|
79
|
-
vi.spyOn(networkHook, "useNetwork").mockImplementation(() => true); // isOnline
|
|
80
|
-
mockSelector.mockReturnValue({ authStatus: AuthStatus.UNAUTHORIZED });
|
|
81
|
-
|
|
82
|
-
const result = useModelStatus({
|
|
83
|
-
slices: [{ local: sliceModel, remote: sliceModel, localScreenshots: {} }],
|
|
84
|
-
customTypes: [{ local: customTypeModel }],
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
expect(result).toEqual({
|
|
88
|
-
modelsStatuses: {
|
|
89
|
-
slices: { [sliceModel.id]: ModelStatus.Unknown },
|
|
90
|
-
customTypes: { [customTypeModel.id]: ModelStatus.Unknown },
|
|
91
|
-
},
|
|
92
|
-
authStatus: AuthStatus.UNAUTHORIZED,
|
|
93
|
-
isOnline: true,
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
test("it should return Unknown status is the user doesn't have access to the repository", () => {
|
|
98
|
-
vi.spyOn(networkHook, "useNetwork").mockImplementation(() => true); // isOnline
|
|
99
|
-
mockSelector.mockReturnValue({ authStatus: AuthStatus.FORBIDDEN });
|
|
100
|
-
|
|
101
|
-
const result = useModelStatus({
|
|
102
|
-
slices: [{ local: sliceModel, remote: sliceModel, localScreenshots: {} }],
|
|
103
|
-
customTypes: [{ local: customTypeModel }],
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
expect(result).toEqual({
|
|
107
|
-
modelsStatuses: {
|
|
108
|
-
slices: { [sliceModel.id]: ModelStatus.Unknown },
|
|
109
|
-
customTypes: { [customTypeModel.id]: ModelStatus.Unknown },
|
|
110
|
-
},
|
|
111
|
-
authStatus: AuthStatus.FORBIDDEN,
|
|
112
|
-
isOnline: true,
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
});
|
|
File without changes
|
|
File without changes
|
|
File without changes
|