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.
Files changed (132) hide show
  1. package/.depcheckrc +3 -0
  2. package/components/ChangesEmptyState/OfflinePage.tsx +1 -1
  3. package/components/ChangesItems/ChangesItems.tsx +6 -2
  4. package/components/CustomTypeTable/changesPage.tsx +12 -7
  5. package/components/DeleteSliceModal/index.tsx +3 -0
  6. package/components/DeleteVariationModal/index.tsx +1 -0
  7. package/components/Forms/CreateCustomTypeModal/CreateCustomTypeModal.tsx +4 -0
  8. package/components/Forms/CreateSliceModal/CreateSliceModal.tsx +3 -1
  9. package/components/Forms/RenameCustomTypeModal/RenameCustomTypeModal.tsx +6 -1
  10. package/components/Forms/RenameSliceModal/RenameSliceModal.tsx +3 -0
  11. package/components/Forms/RenameVariationModal/RenameVariationModal.tsx +0 -1
  12. package/components/LoginModal/index.tsx +40 -2
  13. package/components/ModalFormCard/index.tsx +0 -1
  14. package/components/Navigation/ChangesItem.tsx +117 -0
  15. package/components/Navigation/Environment.tsx +51 -4
  16. package/components/Navigation/index.css.ts +1 -2
  17. package/components/Navigation/index.test.tsx +5 -85
  18. package/components/Navigation/index.tsx +32 -20
  19. package/components/ReviewModal/ReviewForm.tsx +0 -1
  20. package/lib/builders/SliceBuilder/index.tsx +3 -3
  21. package/lib/builders/common/EditModal/Form.jsx +0 -1
  22. package/lib/builders/common/Zone/Card/components/NewField.tsx +0 -1
  23. package/lib/models/common/ModelStatus/index.ts +25 -0
  24. package/out/404.html +1 -1
  25. package/out/_next/static/chunks/18539003-5f78714a35fde4a1.js +97 -0
  26. package/out/_next/static/chunks/25-3aa4d4b24167d340.js +1 -0
  27. package/out/_next/static/chunks/318-19dbcae10cdb6408.js +1 -0
  28. package/out/_next/static/chunks/727-de03055e40815a3a.js +1 -0
  29. package/out/_next/static/chunks/777-8c5507e3b119b5dc.js +8 -0
  30. package/out/_next/static/chunks/799-0af1215eb2b3c17e.js +1 -0
  31. package/out/_next/static/chunks/904-40686b0426716111.js +1 -0
  32. package/out/_next/static/chunks/pages/_app-bca191df4ac7078e.js +434 -0
  33. package/out/_next/static/chunks/pages/{changelog-afd18276fe018b24.js → changelog-cb1df3a70b20f1b8.js} +1 -1
  34. package/out/_next/static/chunks/pages/changes-ccfd715c2bdc4f83.js +1 -0
  35. package/out/_next/static/chunks/pages/{labs-ee206005948f8e87.js → labs-2adfb5127f9c4081.js} +1 -1
  36. package/out/_next/static/chunks/pages/settings-94bcd7422e8b237d.js +1 -0
  37. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]/{simulator-52c3e1f9d14160b8.js → simulator-aaf65fbec8f5839c.js} +1 -1
  38. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-90bb2ea76d2abf42.js +1 -0
  39. package/out/_next/static/chunks/pages/slices-e0ce615c38e8338d.js +1 -0
  40. package/out/_next/static/css/39e089aad1742a3a.css +1 -0
  41. package/out/_next/static/css/{37153180c3b0dd66.css → 838fd8bb5a7c32b0.css} +1 -1
  42. package/out/_next/static/css/ca32882a5d2d8d88.css +1 -0
  43. package/out/_next/static/eNujlD43QIdmra7ggcNtK/_buildManifest.js +1 -0
  44. package/out/changelog.html +1 -1
  45. package/out/changes.html +1 -1
  46. package/out/custom-types/[customTypeId].html +1 -1
  47. package/out/custom-types.html +1 -1
  48. package/out/index.html +1 -1
  49. package/out/labs.html +1 -1
  50. package/out/page-types/[pageTypeId].html +1 -1
  51. package/out/settings.html +1 -0
  52. package/out/slices/[lib]/[sliceName]/[variation]/simulator.html +1 -1
  53. package/out/slices/[lib]/[sliceName]/[variation].html +1 -1
  54. package/out/slices.html +1 -1
  55. package/package.json +10 -8
  56. package/pages/_app.tsx +34 -32
  57. package/pages/changes.tsx +27 -41
  58. package/pages/settings.tsx +1 -0
  59. package/postcss.config.js +10 -0
  60. package/src/components/FieldSet/FieldSet.stories.tsx +20 -11
  61. package/src/components/RelativeTime/RelativeTime.stories.tsx +25 -0
  62. package/src/components/RelativeTime/RelativeTime.tsx +40 -0
  63. package/src/components/RelativeTime/index.ts +1 -0
  64. package/src/components/SideNav/SideNav.css.ts +1 -1
  65. package/src/components/SideNav/SideNav.tsx +9 -5
  66. package/src/components/SideNav/SideNavEnvironmentSelector.tsx +31 -11
  67. package/src/domain/__tests__/customType.test.ts +26 -19
  68. package/src/domain/customType.ts +22 -5
  69. package/src/features/autoSave/AutoSaveStatusIndicator.tsx +4 -4
  70. package/src/features/customTypes/customTypesBuilder/CustomTypeProvider.tsx +11 -10
  71. package/src/features/customTypes/customTypesBuilder/CustomTypesBuilderPage.tsx +3 -3
  72. package/src/features/environments/actions/setEnvironment.ts +4 -4
  73. package/src/features/settings/SettingsPage.tsx +47 -0
  74. package/src/features/settings/git/ConnectGitRepository.tsx +126 -0
  75. package/src/features/settings/git/ConnectGitRepositoryBlankSlate.tsx +33 -0
  76. package/src/features/settings/git/GitOwnerSelect.tsx +71 -0
  77. package/src/features/settings/git/GitProvider.ts +40 -0
  78. package/src/features/settings/git/GitProviderConnectButtons.tsx +63 -0
  79. package/src/features/settings/git/GitRepositoriesList.tsx +76 -0
  80. package/src/features/settings/git/GitRepositoriesSearch.tsx +69 -0
  81. package/src/features/settings/git/GitRepositoryConnectDialog.tsx +85 -0
  82. package/src/features/settings/git/GitRepositoryDisconnectDialog.tsx +60 -0
  83. package/src/features/settings/git/useGitIntegrationExperiment.ts +8 -0
  84. package/src/features/settings/git/useGitOwners.ts +12 -0
  85. package/src/features/settings/git/useGitRepos.ts +24 -0
  86. package/src/features/settings/git/useLinkedGitRepos.ts +41 -0
  87. package/src/features/settings/git/useWriteAPIToken.ts +23 -0
  88. package/src/features/slices/convertLegacySlice/ConvertLegacySliceAsNewSliceDialog.tsx +0 -1
  89. package/src/features/slices/convertLegacySlice/ConvertLegacySliceAsNewVariationDialog.tsx +0 -1
  90. package/src/features/slices/convertLegacySlice/ConvertLegacySliceMergeWithIdenticalDialog.tsx +0 -1
  91. package/src/features/slices/sliceBuilder/SliceBuilderProvider.tsx +12 -10
  92. package/src/features/sync/AutoSyncProvider.tsx +252 -0
  93. package/src/features/sync/components/AutoSyncStatusIndicator.module.css +17 -0
  94. package/src/features/sync/components/AutoSyncStatusIndicator.module.css.d.ts +5 -0
  95. package/src/features/sync/components/AutoSyncStatusIndicator.tsx +58 -0
  96. package/src/features/sync/fetchUnSyncChanges.ts +40 -0
  97. package/src/features/sync/getUnSyncChanges.ts +144 -0
  98. package/src/features/sync/useUnSyncChanges.ts +32 -0
  99. package/src/hooks/useActionQueue.tsx +177 -0
  100. package/src/hooks/useAuthStatus.ts +12 -0
  101. package/src/hooks/useUser.ts +14 -0
  102. package/src/icons/SettingsIcon.tsx +19 -0
  103. package/src/icons/SyncFailed.tsx +30 -0
  104. package/src/icons/Synced.tsx +19 -0
  105. package/src/icons/Syncing.tsx +23 -0
  106. package/test/__testutils__/index.tsx +8 -5
  107. package/test/src/modules/__fixtures__/serverState.ts +1 -2
  108. package/components/Navigation/ChangesListItem.tsx +0 -74
  109. package/components/Navigation/ChangesRightElement.tsx +0 -33
  110. package/out/_next/static/chunks/18539003-5cc559566b738915.js +0 -97
  111. package/out/_next/static/chunks/25-01f422cea8066349.js +0 -1
  112. package/out/_next/static/chunks/318-387e3f23d717c53d.js +0 -1
  113. package/out/_next/static/chunks/376-7e784d6069e01654.js +0 -8
  114. package/out/_next/static/chunks/727-358d7fae68a880b3.js +0 -1
  115. package/out/_next/static/chunks/799-d95a93e965557aeb.js +0 -1
  116. package/out/_next/static/chunks/904-1ff7519adece2b55.js +0 -1
  117. package/out/_next/static/chunks/pages/_app-e31adcf75e9176d3.js +0 -432
  118. package/out/_next/static/chunks/pages/changes-92c84f6cac0285fd.js +0 -1
  119. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-608d206d10a719e3.js +0 -1
  120. package/out/_next/static/chunks/pages/slices-b0b58192a87df822.js +0 -1
  121. package/out/_next/static/css/200244e3e0463c39.css +0 -1
  122. package/out/_next/static/vxOhh_oqUIQ5PTSKUKgMR/_buildManifest.js +0 -1
  123. package/src/features/autoSave/useAutoSave.tsx +0 -177
  124. package/src/features/legacyState/actions/getLegacySliceMachineState.ts +0 -53
  125. package/src/features/legacyState/useLegacySliceMachineState.ts +0 -7
  126. package/src/hooks/useModelStatus.ts +0 -78
  127. package/src/hooks/useUnSyncChanges.ts +0 -88
  128. package/test/components/LoginModal.test.tsx +0 -67
  129. package/test/src/hooks/useModelStatus.test.ts +0 -115
  130. /package/out/_next/static/{vxOhh_oqUIQ5PTSKUKgMR → eNujlD43QIdmra7ggcNtK}/_ssgManifest.js +0 -0
  131. /package/src/features/{changes → sync}/actions/pushChanges.ts +0 -0
  132. /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,7 +0,0 @@
1
- import { useRequest } from "@prismicio/editor-support/Suspense";
2
-
3
- import { getLegacySliceMachineState } from "./actions/getLegacySliceMachineState";
4
-
5
- export function useLegacySliceMachineState() {
6
- return useRequest(getLegacySliceMachineState, []);
7
- }
@@ -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
- });