slice-machine-ui 1.23.2-alpha.xru-auto-sync-provider.10 → 1.23.2-beta.10
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/components/ChangesEmptyState/OfflinePage.tsx +1 -1
- package/components/ChangesItems/ChangesItems.tsx +2 -6
- package/components/CustomTypeTable/changesPage.tsx +7 -12
- package/components/DeleteSliceModal/index.tsx +0 -3
- package/components/Forms/CreateCustomTypeModal/CreateCustomTypeModal.tsx +0 -4
- package/components/Forms/CreateSliceModal/CreateSliceModal.tsx +1 -3
- package/components/Forms/RenameCustomTypeModal/RenameCustomTypeModal.tsx +1 -6
- package/components/Forms/RenameSliceModal/RenameSliceModal.tsx +0 -3
- package/components/LoginModal/index.tsx +2 -40
- package/components/Navigation/ChangesListItem.tsx +74 -0
- package/components/Navigation/ChangesRightElement.tsx +33 -0
- package/components/Navigation/Environment.tsx +4 -51
- package/components/Navigation/index.css.ts +2 -1
- package/components/Navigation/index.test.tsx +85 -5
- package/components/Navigation/index.tsx +8 -3
- package/lib/builders/SliceBuilder/index.tsx +3 -3
- package/lib/models/common/ModelStatus/index.ts +0 -25
- package/out/404.html +1 -1
- package/out/_next/static/{N-r0ln97LTMP4f0cUkcwx → Dz7wwTNVdXvoAjySXz7mE}/_buildManifest.js +1 -1
- package/out/_next/static/chunks/25-01f422cea8066349.js +1 -0
- package/out/_next/static/chunks/{318-19dbcae10cdb6408.js → 318-99b5914b60bf1d17.js} +1 -1
- package/out/_next/static/chunks/727-f8fefdd3e50cab2a.js +1 -0
- package/out/_next/static/chunks/{799-0af1215eb2b3c17e.js → 799-b616401fe0dafdeb.js} +1 -1
- package/out/_next/static/chunks/904-4653b7a849f4db94.js +1 -0
- package/out/_next/static/chunks/pages/{_app-09b68248c117daf0.js → _app-5ec41fc53c1f6405.js} +24 -24
- package/out/_next/static/chunks/pages/{changelog-cb1df3a70b20f1b8.js → changelog-02cd80dad6f40878.js} +1 -1
- package/out/_next/static/chunks/pages/changes-8e9da8fbb809aad3.js +1 -0
- package/out/_next/static/chunks/pages/{labs-2adfb5127f9c4081.js → labs-39b50541a4c370bf.js} +1 -1
- package/out/_next/static/chunks/pages/{settings-94bcd7422e8b237d.js → settings-879686e3a8759dc3.js} +1 -1
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]/{simulator-aaf65fbec8f5839c.js → simulator-c498e9c80cae8cce.js} +1 -1
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-9b893c62b2f2fe4a.js +1 -0
- package/out/_next/static/chunks/pages/slices-4522d604117d8c66.js +1 -0
- package/out/_next/static/css/{39e089aad1742a3a.css → 3c26dbbbd8f4be12.css} +1 -1
- 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 -1
- 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 +3 -3
- package/pages/_app.tsx +32 -34
- package/pages/changes.tsx +41 -27
- package/src/components/SideNav/SideNav.css.ts +1 -1
- package/src/components/SideNav/SideNav.tsx +5 -9
- package/src/components/SideNav/SideNavEnvironmentSelector.tsx +11 -31
- package/src/features/autoSave/AutoSaveStatusIndicator.tsx +4 -4
- package/src/features/autoSave/useAutoSave.tsx +177 -0
- package/src/features/customTypes/customTypesBuilder/CustomTypeProvider.tsx +10 -11
- package/src/features/customTypes/customTypesBuilder/CustomTypesBuilderPage.tsx +2 -2
- package/src/features/environments/actions/setEnvironment.ts +3 -3
- package/src/features/legacyState/actions/getLegacySliceMachineState.ts +53 -0
- package/src/features/legacyState/useLegacySliceMachineState.ts +7 -0
- package/src/features/slices/sliceBuilder/SliceBuilderProvider.tsx +10 -12
- package/src/hooks/useModelStatus.ts +78 -0
- package/src/hooks/useUnSyncChanges.ts +88 -0
- package/test/__testutils__/index.tsx +5 -8
- package/test/components/LoginModal.test.tsx +67 -0
- package/test/src/hooks/useModelStatus.test.ts +115 -0
- package/components/Navigation/ChangesItem.tsx +0 -117
- package/out/_next/static/chunks/25-3aa4d4b24167d340.js +0 -1
- package/out/_next/static/chunks/727-de03055e40815a3a.js +0 -1
- package/out/_next/static/chunks/904-40686b0426716111.js +0 -1
- package/out/_next/static/chunks/pages/changes-ccfd715c2bdc4f83.js +0 -1
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-90bb2ea76d2abf42.js +0 -1
- package/out/_next/static/chunks/pages/slices-e0ce615c38e8338d.js +0 -1
- package/src/features/sync/AutoSyncProvider.tsx +0 -252
- package/src/features/sync/components/AutoSyncStatusIndicator.module.css +0 -17
- package/src/features/sync/components/AutoSyncStatusIndicator.module.css.d.ts +0 -5
- package/src/features/sync/components/AutoSyncStatusIndicator.tsx +0 -58
- package/src/features/sync/fetchUnSyncChanges.ts +0 -40
- package/src/features/sync/getUnSyncChanges.ts +0 -144
- package/src/features/sync/useUnSyncChanges.ts +0 -32
- package/src/hooks/useActionQueue.tsx +0 -177
- package/src/hooks/useAuthStatus.ts +0 -12
- package/src/icons/SyncFailed.tsx +0 -30
- package/src/icons/Synced.tsx +0 -19
- package/src/icons/Syncing.tsx +0 -23
- /package/out/_next/static/{N-r0ln97LTMP4f0cUkcwx → Dz7wwTNVdXvoAjySXz7mE}/_ssgManifest.js +0 -0
- /package/src/features/{sync → changes}/actions/pushChanges.ts +0 -0
- /package/src/features/{sync → changes}/actions/trackPushChangesSuccess.ts +0 -0
|
@@ -28,7 +28,7 @@ export const OfflinePage = () => {
|
|
|
28
28
|
<Text
|
|
29
29
|
sx={{ fontSize: "13px", lineHeight: "24px", textAlign: "center" }}
|
|
30
30
|
>
|
|
31
|
-
|
|
31
|
+
You need a stable internet connection to review and push changes
|
|
32
32
|
</Text>
|
|
33
33
|
</Flex>
|
|
34
34
|
</Flex>
|
|
@@ -6,23 +6,19 @@ import { ChangesSectionHeader } from "@components/ChangesSectionHeader";
|
|
|
6
6
|
import { CustomTypeTable } from "@components/CustomTypeTable/changesPage";
|
|
7
7
|
import Grid from "@components/Grid";
|
|
8
8
|
import { ComponentUI } from "@lib/models/common/ComponentUI";
|
|
9
|
+
import { ModelStatusInformation } from "@src/hooks/useModelStatus";
|
|
9
10
|
import ScreenshotChangesModal from "@components/ScreenshotChangesModal";
|
|
10
11
|
import { countMissingScreenshots } from "@src/domain/slice";
|
|
11
12
|
import { useScreenshotChangesModal } from "@src/hooks/useScreenshotChangesModal";
|
|
12
13
|
import { ModelStatus } from "@lib/models/common/ModelStatus";
|
|
13
14
|
import { LocalOrRemoteCustomType } from "@lib/models/common/ModelData";
|
|
14
15
|
import { SharedSliceCard } from "@src/features/slices/sliceCards/SharedSliceCard";
|
|
15
|
-
import { AuthStatus } from "@src/modules/userContext/types";
|
|
16
|
-
import { ModelsStatuses } from "@src/features/sync/getUnSyncChanges";
|
|
17
16
|
|
|
18
17
|
import { DevCollaborationExperiment } from "./DevCollaborationExperiment";
|
|
19
18
|
|
|
20
|
-
interface ChangesItemsProps {
|
|
19
|
+
interface ChangesItemsProps extends ModelStatusInformation {
|
|
21
20
|
unSyncedCustomTypes: LocalOrRemoteCustomType[];
|
|
22
21
|
unSyncedSlices: ComponentUI[];
|
|
23
|
-
modelsStatuses: ModelsStatuses;
|
|
24
|
-
authStatus: AuthStatus;
|
|
25
|
-
isOnline: boolean;
|
|
26
22
|
}
|
|
27
23
|
|
|
28
24
|
export const ChangesItems: React.FC<ChangesItemsProps> = ({
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Link from "next/link";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { Box, Text } from "theme-ui";
|
|
4
|
+
import { ModelStatusInformation } from "@src/hooks/useModelStatus";
|
|
4
5
|
import { CustomTypeSM } from "@lib/models/common/CustomType";
|
|
5
6
|
import { ModelStatus } from "@lib/models/common/ModelStatus";
|
|
6
7
|
import {
|
|
@@ -9,26 +10,18 @@ import {
|
|
|
9
10
|
} from "@lib/models/common/ModelData";
|
|
10
11
|
import { StatusBadge } from "@src/features/changes/StatusBadge";
|
|
11
12
|
import { CUSTOM_TYPES_CONFIG } from "@src/features/customTypes/customTypesConfig";
|
|
12
|
-
import { AuthStatus } from "@src/modules/userContext/types";
|
|
13
|
-
import { ModelsStatuses } from "@src/features/sync/getUnSyncChanges";
|
|
14
13
|
|
|
15
|
-
interface CustomTypeTableProps {
|
|
14
|
+
interface CustomTypeTableProps extends ModelStatusInformation {
|
|
16
15
|
customTypes: LocalOrRemoteCustomType[];
|
|
17
|
-
modelsStatuses: ModelsStatuses;
|
|
18
|
-
authStatus: AuthStatus;
|
|
19
|
-
isOnline: boolean;
|
|
20
16
|
}
|
|
21
17
|
|
|
22
18
|
const firstColumnWidth = "40%";
|
|
23
19
|
const secondColumnWidth = "40%";
|
|
24
20
|
const thirdColumnWidth = "20%";
|
|
25
21
|
|
|
26
|
-
const CustomTypeChangeRow: React.FC<
|
|
27
|
-
ct: CustomTypeSM;
|
|
28
|
-
|
|
29
|
-
authStatus: AuthStatus;
|
|
30
|
-
isOnline: boolean;
|
|
31
|
-
}> = ({ ct, status, authStatus, isOnline }) => {
|
|
22
|
+
const CustomTypeChangeRow: React.FC<
|
|
23
|
+
{ ct: CustomTypeSM; status: ModelStatus } & ModelStatusInformation
|
|
24
|
+
> = ({ ct, status, authStatus, isOnline }) => {
|
|
32
25
|
return (
|
|
33
26
|
<>
|
|
34
27
|
<Box as={"td"} style={{ width: firstColumnWidth }}>
|
|
@@ -87,6 +80,7 @@ export const CustomTypeTable: React.FC<CustomTypeTableProps> = ({
|
|
|
87
80
|
status={modelsStatuses.customTypes[customType.local.id]}
|
|
88
81
|
authStatus={authStatus}
|
|
89
82
|
isOnline={isOnline}
|
|
83
|
+
modelsStatuses={modelsStatuses}
|
|
90
84
|
key={customType.local.id}
|
|
91
85
|
/>
|
|
92
86
|
</tr>
|
|
@@ -103,6 +97,7 @@ export const CustomTypeTable: React.FC<CustomTypeTableProps> = ({
|
|
|
103
97
|
status={modelsStatuses.customTypes[customType.remote.id]}
|
|
104
98
|
authStatus={authStatus}
|
|
105
99
|
isOnline={isOnline}
|
|
100
|
+
modelsStatuses={modelsStatuses}
|
|
106
101
|
key={customType.remote.id}
|
|
107
102
|
/>
|
|
108
103
|
</tr>
|
|
@@ -7,7 +7,6 @@ import useSliceMachineActions from "@src/modules/useSliceMachineActions";
|
|
|
7
7
|
import Card from "@components/Card";
|
|
8
8
|
import { Button } from "@components/Button";
|
|
9
9
|
import { deleteSlice } from "@src/features/slices/actions/deleteSlice";
|
|
10
|
-
import { useAutoSync } from "@src/features/sync/AutoSyncProvider";
|
|
11
10
|
|
|
12
11
|
type DeleteSliceModalProps = {
|
|
13
12
|
isOpen: boolean;
|
|
@@ -22,7 +21,6 @@ export const DeleteSliceModal: React.FunctionComponent<
|
|
|
22
21
|
> = ({ sliceId, sliceName, libName, isOpen, onClose }) => {
|
|
23
22
|
const [isDeleting, setIsDeleting] = useState(false);
|
|
24
23
|
const { deleteSliceSuccess } = useSliceMachineActions();
|
|
25
|
-
const { syncChanges } = useAutoSync();
|
|
26
24
|
const { theme } = useThemeUI();
|
|
27
25
|
|
|
28
26
|
const onDelete = async () => {
|
|
@@ -34,7 +32,6 @@ export const DeleteSliceModal: React.FunctionComponent<
|
|
|
34
32
|
libraryID: libName,
|
|
35
33
|
onSuccess: () => {
|
|
36
34
|
deleteSliceSuccess(sliceId, libName);
|
|
37
|
-
syncChanges();
|
|
38
35
|
},
|
|
39
36
|
});
|
|
40
37
|
|
|
@@ -21,7 +21,6 @@ import {
|
|
|
21
21
|
} from "@src/features/customTypes/actions/createCustomType";
|
|
22
22
|
import { CUSTOM_TYPES_CONFIG } from "@src/features/customTypes/customTypesConfig";
|
|
23
23
|
import { getFormat } from "@src/domain/customType";
|
|
24
|
-
import { useAutoSync } from "@src/features/sync/AutoSyncProvider";
|
|
25
24
|
|
|
26
25
|
import { InputBox } from "../components/InputBox";
|
|
27
26
|
import { SelectRepeatable } from "../components/SelectRepeatable";
|
|
@@ -59,7 +58,6 @@ export const CreateCustomTypeModal: React.FC<CreateCustomTypeModalProps> = ({
|
|
|
59
58
|
);
|
|
60
59
|
const customTypesMessages = CUSTOM_TYPES_MESSAGES[format];
|
|
61
60
|
const [isIdFieldPristine, setIsIdFieldPristine] = useState(true);
|
|
62
|
-
const { syncChanges } = useAutoSync();
|
|
63
61
|
const router = useRouter();
|
|
64
62
|
|
|
65
63
|
const onSubmit = async ({ id, label, repeatable }: FormValues) => {
|
|
@@ -88,8 +86,6 @@ export const CreateCustomTypeModal: React.FC<CreateCustomTypeModalProps> = ({
|
|
|
88
86
|
}
|
|
89
87
|
: undefined,
|
|
90
88
|
});
|
|
91
|
-
|
|
92
|
-
syncChanges();
|
|
93
89
|
},
|
|
94
90
|
});
|
|
95
91
|
|
|
@@ -9,7 +9,6 @@ import ModalFormCard from "@components/ModalFormCard";
|
|
|
9
9
|
import { createSlice } from "@src/features/slices/actions/createSlice";
|
|
10
10
|
import useSliceMachineActions from "@src/modules/useSliceMachineActions";
|
|
11
11
|
import { getState } from "@src/apiClient";
|
|
12
|
-
import { useAutoSync } from "@src/features/sync/AutoSyncProvider";
|
|
13
12
|
|
|
14
13
|
import { validateSliceModalValues } from "../formsValidator";
|
|
15
14
|
import { InputBox } from "../components/InputBox";
|
|
@@ -31,7 +30,6 @@ export const CreateSliceModal: FC<CreateSliceModalProps> = ({
|
|
|
31
30
|
}) => {
|
|
32
31
|
const { createSliceSuccess } = useSliceMachineActions();
|
|
33
32
|
const [isCreatingSlice, setIsCreatingSlice] = useState(false);
|
|
34
|
-
const { syncChanges } = useAutoSync();
|
|
35
33
|
|
|
36
34
|
const onSubmit = async (values: FormValues) => {
|
|
37
35
|
const sliceName = values.sliceName;
|
|
@@ -47,8 +45,8 @@ export const CreateSliceModal: FC<CreateSliceModalProps> = ({
|
|
|
47
45
|
const serverState = await getState();
|
|
48
46
|
// Update Redux store
|
|
49
47
|
createSliceSuccess(serverState.libraries);
|
|
48
|
+
|
|
50
49
|
onSuccess(newSlice, libraryName);
|
|
51
|
-
syncChanges();
|
|
52
50
|
},
|
|
53
51
|
});
|
|
54
52
|
};
|
|
@@ -13,7 +13,6 @@ import { CustomTypeFormat } from "@slicemachine/manager";
|
|
|
13
13
|
import { CUSTOM_TYPES_MESSAGES } from "@src/features/customTypes/customTypesMessages";
|
|
14
14
|
|
|
15
15
|
import { renameCustomType } from "@src/features/customTypes/actions/renameCustomType";
|
|
16
|
-
import { useAutoSync } from "@src/features/sync/AutoSyncProvider";
|
|
17
16
|
|
|
18
17
|
interface RenameCustomTypeModalProps {
|
|
19
18
|
isChangesLocal: boolean;
|
|
@@ -33,7 +32,6 @@ export const RenameCustomTypeModal: React.FC<RenameCustomTypeModalProps> = ({
|
|
|
33
32
|
const customTypeName = customType?.label ?? "";
|
|
34
33
|
const customTypeId = customType?.id ?? "";
|
|
35
34
|
const { renameCustomTypeSuccess } = useSliceMachineActions();
|
|
36
|
-
const { syncChanges } = useAutoSync();
|
|
37
35
|
|
|
38
36
|
const [isRenaming, setIsRenaming] = useState(false);
|
|
39
37
|
|
|
@@ -48,10 +46,7 @@ export const RenameCustomTypeModal: React.FC<RenameCustomTypeModalProps> = ({
|
|
|
48
46
|
await renameCustomType({
|
|
49
47
|
model: customType,
|
|
50
48
|
newLabel: values.customTypeName,
|
|
51
|
-
onSuccess:
|
|
52
|
-
renameCustomTypeSuccess(renamedCustomType);
|
|
53
|
-
syncChanges();
|
|
54
|
-
},
|
|
49
|
+
onSuccess: renameCustomTypeSuccess,
|
|
55
50
|
});
|
|
56
51
|
}
|
|
57
52
|
setIsRenaming(false);
|
|
@@ -6,7 +6,6 @@ import { SliceMachineStoreType } from "@src/redux/type";
|
|
|
6
6
|
import { getLibraries, getRemoteSlices } from "@src/modules/slices";
|
|
7
7
|
import { ComponentUI } from "@lib/models/common/ComponentUI";
|
|
8
8
|
import { renameSlice } from "@src/features/slices/actions/renameSlice";
|
|
9
|
-
import { useAutoSync } from "@src/features/sync/AutoSyncProvider";
|
|
10
9
|
|
|
11
10
|
import { InputBox } from "../components/InputBox";
|
|
12
11
|
import ModalFormCard from "../../ModalFormCard";
|
|
@@ -25,7 +24,6 @@ export const RenameSliceModal: React.FC<RenameSliceModalProps> = ({
|
|
|
25
24
|
onClose,
|
|
26
25
|
}) => {
|
|
27
26
|
const { renameSliceSuccess } = useSliceMachineActions();
|
|
28
|
-
const { syncChanges } = useAutoSync();
|
|
29
27
|
const { localLibs, remoteLibs } = useSelector(
|
|
30
28
|
(store: SliceMachineStoreType) => ({
|
|
31
29
|
localLibs: getLibraries(store),
|
|
@@ -41,7 +39,6 @@ export const RenameSliceModal: React.FC<RenameSliceModalProps> = ({
|
|
|
41
39
|
newSliceName: values.sliceName,
|
|
42
40
|
onSuccess: (renamedSlice) => {
|
|
43
41
|
renameSliceSuccess(renamedSlice.from, renamedSlice.model);
|
|
44
|
-
syncChanges();
|
|
45
42
|
},
|
|
46
43
|
});
|
|
47
44
|
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
Text,
|
|
12
12
|
} from "theme-ui";
|
|
13
13
|
import SliceMachineModal from "@components/SliceMachineModal";
|
|
14
|
-
import { checkAuthStatus,
|
|
14
|
+
import { checkAuthStatus, startAuth } from "@src/apiClient";
|
|
15
15
|
import { buildEndpoints } from "@lib/prismic/endpoints";
|
|
16
16
|
import { startPolling } from "@lib/utils/poll";
|
|
17
17
|
import { CheckAuthStatusResponse } from "@models/common/Auth";
|
|
@@ -25,12 +25,6 @@ import { getEnvironment } from "@src/modules/environment";
|
|
|
25
25
|
import useSliceMachineActions from "@src/modules/useSliceMachineActions";
|
|
26
26
|
import preferWroomBase from "@lib/utils/preferWroomBase";
|
|
27
27
|
import { ToasterType } from "@src/modules/toaster";
|
|
28
|
-
import { getUnSyncedChanges } from "@src/features/sync/getUnSyncChanges";
|
|
29
|
-
import { normalizeFrontendCustomTypes } from "@lib/models/common/normalizers/customType";
|
|
30
|
-
import { normalizeFrontendSlices } from "@lib/models/common/normalizers/slices";
|
|
31
|
-
import { AuthStatus } from "@src/modules/userContext/types";
|
|
32
|
-
import { useAutoSync } from "@src/features/sync/AutoSyncProvider";
|
|
33
|
-
import { getActiveEnvironment } from "@src/features/environments/actions/getActiveEnvironment";
|
|
34
28
|
|
|
35
29
|
interface ValidAuthStatus extends CheckAuthStatusResponse {
|
|
36
30
|
status: "ok";
|
|
@@ -48,7 +42,7 @@ const LoginModal: React.FunctionComponent = () => {
|
|
|
48
42
|
env: getEnvironment(store),
|
|
49
43
|
}),
|
|
50
44
|
);
|
|
51
|
-
|
|
45
|
+
|
|
52
46
|
const { closeModals, startLoadingLogin, stopLoadingLogin, openToaster } =
|
|
53
47
|
useSliceMachineActions();
|
|
54
48
|
|
|
@@ -78,38 +72,6 @@ const LoginModal: React.FunctionComponent = () => {
|
|
|
78
72
|
openToaster("Logged in", ToasterType.SUCCESS);
|
|
79
73
|
stopLoadingLogin();
|
|
80
74
|
closeModals();
|
|
81
|
-
|
|
82
|
-
const serverState = await getState();
|
|
83
|
-
const slices = normalizeFrontendSlices(
|
|
84
|
-
serverState.libraries,
|
|
85
|
-
serverState.remoteSlices,
|
|
86
|
-
);
|
|
87
|
-
const customTypes = Object.values(
|
|
88
|
-
normalizeFrontendCustomTypes(
|
|
89
|
-
serverState.customTypes,
|
|
90
|
-
serverState.remoteCustomTypes,
|
|
91
|
-
),
|
|
92
|
-
);
|
|
93
|
-
const { changedCustomTypes, changedSlices } = getUnSyncedChanges({
|
|
94
|
-
authStatus: AuthStatus.AUTHORIZED,
|
|
95
|
-
customTypes,
|
|
96
|
-
isOnline: true,
|
|
97
|
-
libraries: serverState.libraries,
|
|
98
|
-
slices,
|
|
99
|
-
});
|
|
100
|
-
const { activeEnvironment } = await getActiveEnvironment();
|
|
101
|
-
|
|
102
|
-
if (
|
|
103
|
-
activeEnvironment?.kind === "dev" &&
|
|
104
|
-
(changedCustomTypes.length > 0 || changedSlices.length > 0)
|
|
105
|
-
) {
|
|
106
|
-
syncChanges({
|
|
107
|
-
environment: activeEnvironment,
|
|
108
|
-
loggedIn: true,
|
|
109
|
-
changedCustomTypes,
|
|
110
|
-
changedSlices,
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
75
|
} catch (e) {
|
|
114
76
|
stopLoadingLogin();
|
|
115
77
|
openToaster("Login failed", ToasterType.ERROR);
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type FC } from "react";
|
|
2
|
+
import Link from "next/link";
|
|
3
|
+
import { useRouter } from "next/router";
|
|
4
|
+
|
|
5
|
+
import { SideNavLink, SideNavListItem } from "@src/components/SideNav";
|
|
6
|
+
import { RadarIcon } from "@src/icons/RadarIcon";
|
|
7
|
+
import {
|
|
8
|
+
HoverCard,
|
|
9
|
+
HoverCardCloseButton,
|
|
10
|
+
HoverCardDescription,
|
|
11
|
+
HoverCardMedia,
|
|
12
|
+
HoverCardTitle,
|
|
13
|
+
} from "@src/components/HoverCard";
|
|
14
|
+
import useSliceMachineActions from "@src/modules/useSliceMachineActions";
|
|
15
|
+
|
|
16
|
+
import { ChangesRightElement } from "./ChangesRightElement";
|
|
17
|
+
|
|
18
|
+
export const ChangesListItem: FC = () => {
|
|
19
|
+
const { setSeenChangesToolTip } = useSliceMachineActions();
|
|
20
|
+
const open = useOpenChangesHoverCard();
|
|
21
|
+
const router = useRouter();
|
|
22
|
+
|
|
23
|
+
const onClose = () => {
|
|
24
|
+
setSeenChangesToolTip();
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<HoverCard
|
|
29
|
+
open={open}
|
|
30
|
+
openDelay={3000}
|
|
31
|
+
onClose={onClose}
|
|
32
|
+
side="right"
|
|
33
|
+
sideOffset={24}
|
|
34
|
+
collisionPadding={280}
|
|
35
|
+
trigger={
|
|
36
|
+
<SideNavListItem>
|
|
37
|
+
<SideNavLink
|
|
38
|
+
title="Changes"
|
|
39
|
+
href="/changes"
|
|
40
|
+
active={router.asPath.startsWith("/changes")}
|
|
41
|
+
component={Link}
|
|
42
|
+
Icon={RadarIcon}
|
|
43
|
+
RightElement={<ChangesRightElement />}
|
|
44
|
+
/>
|
|
45
|
+
</SideNavListItem>
|
|
46
|
+
}
|
|
47
|
+
>
|
|
48
|
+
<HoverCardTitle>Push your changes</HoverCardTitle>
|
|
49
|
+
<HoverCardMedia component="image" src="/push.png" />
|
|
50
|
+
<HoverCardDescription>
|
|
51
|
+
When you click Save, your changes are saved locally. Then, you can push
|
|
52
|
+
your models to Prismic from the Changes page.
|
|
53
|
+
</HoverCardDescription>
|
|
54
|
+
<HoverCardCloseButton>Got it</HoverCardCloseButton>
|
|
55
|
+
</HoverCard>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// TODO(DT-1925): Reactivate this feature
|
|
60
|
+
const useOpenChangesHoverCard = () => {
|
|
61
|
+
// const { hasSeenChangesToolTip, hasSeenSimulatorToolTip } = useSelector(
|
|
62
|
+
// (store: SliceMachineStoreType) => ({
|
|
63
|
+
// hasSeenChangesToolTip: userHasSeenChangesToolTip(store),
|
|
64
|
+
// hasSeenSimulatorToolTip: userHasSeenSimulatorToolTip(store),
|
|
65
|
+
// }),
|
|
66
|
+
// );
|
|
67
|
+
|
|
68
|
+
// return (
|
|
69
|
+
// !hasSeenChangesToolTip &&
|
|
70
|
+
// hasSeenSimulatorToolTip
|
|
71
|
+
// );
|
|
72
|
+
|
|
73
|
+
return false;
|
|
74
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { FC } from "react";
|
|
2
|
+
|
|
3
|
+
import { useUnSyncChanges } from "@src/hooks/useUnSyncChanges";
|
|
4
|
+
import { RightElement } from "@src/components/SideNav/SideNav";
|
|
5
|
+
import { AuthStatus } from "@src/modules/userContext/types";
|
|
6
|
+
import { useNetwork } from "@src/hooks/useNetwork";
|
|
7
|
+
|
|
8
|
+
export const ChangesRightElement: FC = () => {
|
|
9
|
+
const isOnline = useNetwork();
|
|
10
|
+
const { unSyncedSlices, unSyncedCustomTypes, authStatus } =
|
|
11
|
+
useUnSyncChanges();
|
|
12
|
+
const numberOfChanges = unSyncedSlices.length + unSyncedCustomTypes.length;
|
|
13
|
+
|
|
14
|
+
if (
|
|
15
|
+
!isOnline ||
|
|
16
|
+
authStatus === AuthStatus.UNAUTHORIZED ||
|
|
17
|
+
authStatus === AuthStatus.FORBIDDEN
|
|
18
|
+
) {
|
|
19
|
+
return <RightElement>Logged out</RightElement>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (numberOfChanges === 0) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const formattedNumberOfChanges = numberOfChanges > 9 ? "+9" : numberOfChanges;
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<RightElement type="pill" data-cy="changes-number">
|
|
30
|
+
{formattedNumberOfChanges}
|
|
31
|
+
</RightElement>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
@@ -1,40 +1,25 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
2
1
|
import {
|
|
3
2
|
Environment as EnvironmentType,
|
|
4
3
|
isUnauthenticatedError,
|
|
5
4
|
isUnauthorizedError,
|
|
6
5
|
} from "@slicemachine/manager/client";
|
|
7
6
|
|
|
8
|
-
import {
|
|
7
|
+
import { telemetry } from "@src/apiClient";
|
|
9
8
|
import { useEnvironments } from "@src/features/environments/useEnvironments";
|
|
10
9
|
import { setEnvironment } from "@src/features/environments/actions/setEnvironment";
|
|
11
10
|
import { useActiveEnvironment } from "@src/features/environments/useActiveEnvironment";
|
|
11
|
+
import { getLegacySliceMachineState } from "@src/features/legacyState/actions/getLegacySliceMachineState";
|
|
12
12
|
import useSliceMachineActions from "@src/modules/useSliceMachineActions";
|
|
13
13
|
import { SideNavEnvironmentSelector } from "@src/components/SideNav";
|
|
14
14
|
import { useNetwork } from "@src/hooks/useNetwork";
|
|
15
|
-
import { useAutoSync } from "@src/features/sync/AutoSyncProvider";
|
|
16
|
-
import { normalizeFrontendSlices } from "@lib/models/common/normalizers/slices";
|
|
17
|
-
import { normalizeFrontendCustomTypes } from "@lib/models/common/normalizers/customType";
|
|
18
|
-
import { getUnSyncedChanges } from "@src/features/sync/getUnSyncChanges";
|
|
19
|
-
import { useAuthStatus } from "@src/hooks/useAuthStatus";
|
|
20
15
|
|
|
21
16
|
export function Environment() {
|
|
22
17
|
const { environments, error: useEnvironmentsError } = useEnvironments();
|
|
23
18
|
const { activeEnvironment } = useActiveEnvironment();
|
|
24
19
|
const { refreshState, openLoginModal } = useSliceMachineActions();
|
|
25
|
-
const { syncChanges } = useAutoSync();
|
|
26
20
|
const isOnline = useNetwork();
|
|
27
|
-
const authStatus = useAuthStatus();
|
|
28
|
-
const [isSwitchingEnv, setIsSwitchingEnv] = useState(false);
|
|
29
|
-
const { autoSyncStatus } = useAutoSync();
|
|
30
21
|
|
|
31
22
|
async function onSelect(environment: EnvironmentType) {
|
|
32
|
-
if (activeEnvironment?.name === environment.name) {
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
setIsSwitchingEnv(true);
|
|
37
|
-
|
|
38
23
|
void telemetry.track({
|
|
39
24
|
event: "environment:switch",
|
|
40
25
|
domain: environment.domain,
|
|
@@ -42,39 +27,9 @@ export function Environment() {
|
|
|
42
27
|
|
|
43
28
|
await setEnvironment(environment);
|
|
44
29
|
|
|
45
|
-
const
|
|
46
|
-
refreshState(serverState);
|
|
47
|
-
|
|
48
|
-
const slices = normalizeFrontendSlices(
|
|
49
|
-
serverState.libraries,
|
|
50
|
-
serverState.remoteSlices,
|
|
51
|
-
);
|
|
52
|
-
const customTypes = Object.values(
|
|
53
|
-
normalizeFrontendCustomTypes(
|
|
54
|
-
serverState.customTypes,
|
|
55
|
-
serverState.remoteCustomTypes,
|
|
56
|
-
),
|
|
57
|
-
);
|
|
58
|
-
const { changedCustomTypes, changedSlices } = getUnSyncedChanges({
|
|
59
|
-
authStatus,
|
|
60
|
-
customTypes,
|
|
61
|
-
isOnline,
|
|
62
|
-
libraries: serverState.libraries,
|
|
63
|
-
slices,
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
if (
|
|
67
|
-
environment.kind === "dev" &&
|
|
68
|
-
(changedCustomTypes.length > 0 || changedSlices.length > 0)
|
|
69
|
-
) {
|
|
70
|
-
syncChanges({
|
|
71
|
-
environment,
|
|
72
|
-
changedCustomTypes,
|
|
73
|
-
changedSlices,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
30
|
+
const legacySliceMachineState = await getLegacySliceMachineState();
|
|
76
31
|
|
|
77
|
-
|
|
32
|
+
refreshState(legacySliceMachineState);
|
|
78
33
|
}
|
|
79
34
|
|
|
80
35
|
if (!isOnline) {
|
|
@@ -87,8 +42,6 @@ export function Environment() {
|
|
|
87
42
|
environments={environments}
|
|
88
43
|
activeEnvironment={activeEnvironment}
|
|
89
44
|
onSelect={onSelect}
|
|
90
|
-
disabled={isSwitchingEnv || autoSyncStatus === "syncing"}
|
|
91
|
-
loading={isSwitchingEnv}
|
|
92
45
|
/>
|
|
93
46
|
);
|
|
94
47
|
}
|
|
@@ -131,8 +131,10 @@ describe("Side Navigation", () => {
|
|
|
131
131
|
test.skip.each([
|
|
132
132
|
["Page types", "/"],
|
|
133
133
|
["Custom types", "/custom-types"],
|
|
134
|
-
["
|
|
134
|
+
["Changes", "/changes"],
|
|
135
|
+
|
|
135
136
|
["Changelog", "/changelog"],
|
|
137
|
+
["Slices", "/slices"],
|
|
136
138
|
// TODO: once we have a plan fo how to display individual libraries change this
|
|
137
139
|
// ["Slices/a", "/slices#slices/a"],
|
|
138
140
|
// ["Slices/b", "/slices#slices/b"]
|
|
@@ -154,17 +156,95 @@ describe("Side Navigation", () => {
|
|
|
154
156
|
},
|
|
155
157
|
);
|
|
156
158
|
|
|
157
|
-
test("should display the number of changes on the '
|
|
159
|
+
test("should display the number of changes on the 'Changes' item", async () => {
|
|
158
160
|
renderSideNavigation({ canUpdate: true, hasChanges: true });
|
|
159
161
|
|
|
160
|
-
expect(await screen.findByText("
|
|
162
|
+
expect(await screen.findByText("Changes")).toBeVisible();
|
|
161
163
|
const changesItem = screen
|
|
162
|
-
.getByText("
|
|
163
|
-
.closest("
|
|
164
|
+
.getByText("Changes")
|
|
165
|
+
.closest("a") as HTMLAnchorElement;
|
|
164
166
|
|
|
165
167
|
expect(within(changesItem).getByText("1")).toBeVisible();
|
|
166
168
|
});
|
|
167
169
|
|
|
170
|
+
test("should not display the number of changes or 'Disconnect' on the 'Changes' item", async () => {
|
|
171
|
+
renderSideNavigation({ canUpdate: true, hasChanges: false });
|
|
172
|
+
|
|
173
|
+
expect(await screen.findByText("Changes")).toBeVisible();
|
|
174
|
+
const changesItem = screen
|
|
175
|
+
.getByText("Changes")
|
|
176
|
+
.closest("a") as HTMLAnchorElement;
|
|
177
|
+
|
|
178
|
+
expect(within(changesItem).queryByText("1")).not.toBeInTheDocument();
|
|
179
|
+
expect(
|
|
180
|
+
within(changesItem).queryByText("Logged out"),
|
|
181
|
+
).not.toBeInTheDocument();
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("should display the information that user is disconnected on the 'Changes' item when unauthorized", async () => {
|
|
185
|
+
renderSideNavigation({
|
|
186
|
+
canUpdate: true,
|
|
187
|
+
hasChanges: true,
|
|
188
|
+
authStatus: AuthStatus.UNAUTHORIZED,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
expect(await screen.findByText("Changes")).toBeVisible();
|
|
192
|
+
const changesItem = screen
|
|
193
|
+
.getByText("Changes")
|
|
194
|
+
.closest("a") as HTMLAnchorElement;
|
|
195
|
+
|
|
196
|
+
expect(within(changesItem).getByText("Logged out")).toBeVisible();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("should display the information that user is disconnected on the 'Changes' item when forbidden", async () => {
|
|
200
|
+
renderSideNavigation({
|
|
201
|
+
canUpdate: true,
|
|
202
|
+
hasChanges: true,
|
|
203
|
+
authStatus: AuthStatus.FORBIDDEN,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
expect(await screen.findByText("Changes")).toBeVisible();
|
|
207
|
+
const changesItem = screen
|
|
208
|
+
.getByText("Changes")
|
|
209
|
+
.closest("a") as HTMLAnchorElement;
|
|
210
|
+
|
|
211
|
+
expect(within(changesItem).getByText("Logged out")).toBeVisible();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("should display the information that user is disconnected on the 'Changes' item when offline", async () => {
|
|
215
|
+
vi.spyOn(navigator, "onLine", "get").mockReturnValueOnce(false);
|
|
216
|
+
|
|
217
|
+
renderSideNavigation({
|
|
218
|
+
canUpdate: true,
|
|
219
|
+
hasChanges: true,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
expect(await screen.findByText("Changes")).toBeVisible();
|
|
223
|
+
const changesItem = screen
|
|
224
|
+
.getByText("Changes")
|
|
225
|
+
.closest("a") as HTMLAnchorElement;
|
|
226
|
+
|
|
227
|
+
expect(within(changesItem).getByText("Logged out")).toBeVisible();
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test("should not display the information that user is disconnected when user is online", async () => {
|
|
231
|
+
vi.spyOn(navigator, "onLine", "get").mockReturnValueOnce(true);
|
|
232
|
+
|
|
233
|
+
renderSideNavigation({
|
|
234
|
+
canUpdate: true,
|
|
235
|
+
hasChanges: true,
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
expect(await screen.findByText("Changes")).toBeVisible();
|
|
239
|
+
const changesItem = screen
|
|
240
|
+
.getByText("Changes")
|
|
241
|
+
.closest("a") as HTMLAnchorElement;
|
|
242
|
+
|
|
243
|
+
expect(
|
|
244
|
+
within(changesItem).queryByText("Logged out"),
|
|
245
|
+
).not.toBeInTheDocument();
|
|
246
|
+
});
|
|
247
|
+
|
|
168
248
|
test("Video Item with next", async (ctx) => {
|
|
169
249
|
const adapter = createTestPlugin({
|
|
170
250
|
meta: {
|
|
@@ -11,6 +11,7 @@ import { MathPlusIcon } from "@src/icons/MathPlusIcon";
|
|
|
11
11
|
import { SettingsIcon } from "@src/icons/SettingsIcon";
|
|
12
12
|
import { CUSTOM_TYPES_CONFIG } from "@src/features/customTypes/customTypesConfig";
|
|
13
13
|
import {
|
|
14
|
+
SideNavSeparator,
|
|
14
15
|
SideNavLink,
|
|
15
16
|
SideNavListItem,
|
|
16
17
|
SideNavList,
|
|
@@ -29,7 +30,7 @@ import { CUSTOM_TYPES_MESSAGES } from "@src/features/customTypes/customTypesMess
|
|
|
29
30
|
import { useGitIntegrationExperiment } from "@src/features/settings/git/useGitIntegrationExperiment";
|
|
30
31
|
import { useRepositoryInformation } from "@src/hooks/useRepositoryInformation";
|
|
31
32
|
|
|
32
|
-
import {
|
|
33
|
+
import { ChangesListItem } from "./ChangesListItem";
|
|
33
34
|
import { Environment } from "./Environment";
|
|
34
35
|
|
|
35
36
|
import * as styles from "./index.css";
|
|
@@ -75,8 +76,6 @@ const Navigation: FC = () => {
|
|
|
75
76
|
href={repositoryUrl}
|
|
76
77
|
/>
|
|
77
78
|
|
|
78
|
-
<ChangesItem />
|
|
79
|
-
|
|
80
79
|
<SideNavList>
|
|
81
80
|
<SideNavListItem>
|
|
82
81
|
<SideNavLink
|
|
@@ -108,6 +107,12 @@ const Navigation: FC = () => {
|
|
|
108
107
|
/>
|
|
109
108
|
</SideNavListItem>
|
|
110
109
|
|
|
110
|
+
<SideNavSeparator />
|
|
111
|
+
|
|
112
|
+
<ChangesListItem />
|
|
113
|
+
|
|
114
|
+
<SideNavSeparator />
|
|
115
|
+
|
|
111
116
|
<SideNavListItem>
|
|
112
117
|
<SideNavLink
|
|
113
118
|
title="Slices"
|
|
@@ -21,7 +21,7 @@ import { useSliceState } from "@src/features/slices/sliceBuilder/SliceBuilderPro
|
|
|
21
21
|
import { AutoSaveStatusIndicator } from "@src/features/autoSave/AutoSaveStatusIndicator";
|
|
22
22
|
|
|
23
23
|
export const SliceBuilder: FC = () => {
|
|
24
|
-
const { slice,
|
|
24
|
+
const { slice, autoSaveStatus } = useSliceState();
|
|
25
25
|
|
|
26
26
|
const isSimulatorAvailableForFramework = useSelector(
|
|
27
27
|
selectIsSimulatorAvailableForFramework,
|
|
@@ -33,10 +33,10 @@ export const SliceBuilder: FC = () => {
|
|
|
33
33
|
<AppLayoutBackButton url="/slices" />
|
|
34
34
|
<AppLayoutBreadcrumb folder="Slices" page={slice.model.name} />
|
|
35
35
|
<AppLayoutActions>
|
|
36
|
-
<AutoSaveStatusIndicator status={
|
|
36
|
+
<AutoSaveStatusIndicator status={autoSaveStatus} />
|
|
37
37
|
<SimulatorButton
|
|
38
38
|
isSimulatorAvailableForFramework={isSimulatorAvailableForFramework}
|
|
39
|
-
disabled={
|
|
39
|
+
disabled={autoSaveStatus !== "saved"}
|
|
40
40
|
/>
|
|
41
41
|
</AppLayoutActions>
|
|
42
42
|
</AppLayoutHeader>
|