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.
Files changed (85) hide show
  1. package/components/ChangesEmptyState/OfflinePage.tsx +1 -1
  2. package/components/ChangesItems/ChangesItems.tsx +2 -6
  3. package/components/CustomTypeTable/changesPage.tsx +7 -12
  4. package/components/DeleteSliceModal/index.tsx +0 -3
  5. package/components/Forms/CreateCustomTypeModal/CreateCustomTypeModal.tsx +0 -4
  6. package/components/Forms/CreateSliceModal/CreateSliceModal.tsx +1 -3
  7. package/components/Forms/RenameCustomTypeModal/RenameCustomTypeModal.tsx +1 -6
  8. package/components/Forms/RenameSliceModal/RenameSliceModal.tsx +0 -3
  9. package/components/LoginModal/index.tsx +2 -40
  10. package/components/Navigation/ChangesListItem.tsx +74 -0
  11. package/components/Navigation/ChangesRightElement.tsx +33 -0
  12. package/components/Navigation/Environment.tsx +4 -51
  13. package/components/Navigation/index.css.ts +2 -1
  14. package/components/Navigation/index.test.tsx +85 -5
  15. package/components/Navigation/index.tsx +8 -3
  16. package/lib/builders/SliceBuilder/index.tsx +3 -3
  17. package/lib/models/common/ModelStatus/index.ts +0 -25
  18. package/out/404.html +1 -1
  19. package/out/_next/static/{N-r0ln97LTMP4f0cUkcwx → Dz7wwTNVdXvoAjySXz7mE}/_buildManifest.js +1 -1
  20. package/out/_next/static/chunks/25-01f422cea8066349.js +1 -0
  21. package/out/_next/static/chunks/{318-19dbcae10cdb6408.js → 318-99b5914b60bf1d17.js} +1 -1
  22. package/out/_next/static/chunks/727-f8fefdd3e50cab2a.js +1 -0
  23. package/out/_next/static/chunks/{799-0af1215eb2b3c17e.js → 799-b616401fe0dafdeb.js} +1 -1
  24. package/out/_next/static/chunks/904-4653b7a849f4db94.js +1 -0
  25. package/out/_next/static/chunks/pages/{_app-09b68248c117daf0.js → _app-5ec41fc53c1f6405.js} +24 -24
  26. package/out/_next/static/chunks/pages/{changelog-cb1df3a70b20f1b8.js → changelog-02cd80dad6f40878.js} +1 -1
  27. package/out/_next/static/chunks/pages/changes-8e9da8fbb809aad3.js +1 -0
  28. package/out/_next/static/chunks/pages/{labs-2adfb5127f9c4081.js → labs-39b50541a4c370bf.js} +1 -1
  29. package/out/_next/static/chunks/pages/{settings-94bcd7422e8b237d.js → settings-879686e3a8759dc3.js} +1 -1
  30. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]/{simulator-aaf65fbec8f5839c.js → simulator-c498e9c80cae8cce.js} +1 -1
  31. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-9b893c62b2f2fe4a.js +1 -0
  32. package/out/_next/static/chunks/pages/slices-4522d604117d8c66.js +1 -0
  33. package/out/_next/static/css/{39e089aad1742a3a.css → 3c26dbbbd8f4be12.css} +1 -1
  34. package/out/changelog.html +1 -1
  35. package/out/changes.html +1 -1
  36. package/out/custom-types/[customTypeId].html +1 -1
  37. package/out/custom-types.html +1 -1
  38. package/out/index.html +1 -1
  39. package/out/labs.html +1 -1
  40. package/out/page-types/[pageTypeId].html +1 -1
  41. package/out/settings.html +1 -1
  42. package/out/slices/[lib]/[sliceName]/[variation]/simulator.html +1 -1
  43. package/out/slices/[lib]/[sliceName]/[variation].html +1 -1
  44. package/out/slices.html +1 -1
  45. package/package.json +3 -3
  46. package/pages/_app.tsx +32 -34
  47. package/pages/changes.tsx +41 -27
  48. package/src/components/SideNav/SideNav.css.ts +1 -1
  49. package/src/components/SideNav/SideNav.tsx +5 -9
  50. package/src/components/SideNav/SideNavEnvironmentSelector.tsx +11 -31
  51. package/src/features/autoSave/AutoSaveStatusIndicator.tsx +4 -4
  52. package/src/features/autoSave/useAutoSave.tsx +177 -0
  53. package/src/features/customTypes/customTypesBuilder/CustomTypeProvider.tsx +10 -11
  54. package/src/features/customTypes/customTypesBuilder/CustomTypesBuilderPage.tsx +2 -2
  55. package/src/features/environments/actions/setEnvironment.ts +3 -3
  56. package/src/features/legacyState/actions/getLegacySliceMachineState.ts +53 -0
  57. package/src/features/legacyState/useLegacySliceMachineState.ts +7 -0
  58. package/src/features/slices/sliceBuilder/SliceBuilderProvider.tsx +10 -12
  59. package/src/hooks/useModelStatus.ts +78 -0
  60. package/src/hooks/useUnSyncChanges.ts +88 -0
  61. package/test/__testutils__/index.tsx +5 -8
  62. package/test/components/LoginModal.test.tsx +67 -0
  63. package/test/src/hooks/useModelStatus.test.ts +115 -0
  64. package/components/Navigation/ChangesItem.tsx +0 -117
  65. package/out/_next/static/chunks/25-3aa4d4b24167d340.js +0 -1
  66. package/out/_next/static/chunks/727-de03055e40815a3a.js +0 -1
  67. package/out/_next/static/chunks/904-40686b0426716111.js +0 -1
  68. package/out/_next/static/chunks/pages/changes-ccfd715c2bdc4f83.js +0 -1
  69. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-90bb2ea76d2abf42.js +0 -1
  70. package/out/_next/static/chunks/pages/slices-e0ce615c38e8338d.js +0 -1
  71. package/src/features/sync/AutoSyncProvider.tsx +0 -252
  72. package/src/features/sync/components/AutoSyncStatusIndicator.module.css +0 -17
  73. package/src/features/sync/components/AutoSyncStatusIndicator.module.css.d.ts +0 -5
  74. package/src/features/sync/components/AutoSyncStatusIndicator.tsx +0 -58
  75. package/src/features/sync/fetchUnSyncChanges.ts +0 -40
  76. package/src/features/sync/getUnSyncChanges.ts +0 -144
  77. package/src/features/sync/useUnSyncChanges.ts +0 -32
  78. package/src/hooks/useActionQueue.tsx +0 -177
  79. package/src/hooks/useAuthStatus.ts +0 -12
  80. package/src/icons/SyncFailed.tsx +0 -30
  81. package/src/icons/Synced.tsx +0 -19
  82. package/src/icons/Syncing.tsx +0 -23
  83. /package/out/_next/static/{N-r0ln97LTMP4f0cUkcwx → Dz7wwTNVdXvoAjySXz7mE}/_ssgManifest.js +0 -0
  84. /package/src/features/{sync → changes}/actions/pushChanges.ts +0 -0
  85. /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
- Connect to the internet to sync your changes.
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
- status: ModelStatus;
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: (renamedCustomType) => {
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, getState, startAuth } from "@src/apiClient";
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
- const { syncChanges } = useAutoSync();
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 { getState, telemetry } from "@src/apiClient";
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 serverState = await getState();
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
- setIsSwitchingEnv(false);
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
  }
@@ -1,5 +1,6 @@
1
1
  import { sprinkles } from "@prismicio/editor-ui";
2
2
 
3
3
  export const environmentDivider = sprinkles({
4
- marginBlock: 16,
4
+ marginTop: 16,
5
+ marginBottom: 32,
5
6
  });
@@ -131,8 +131,10 @@ describe("Side Navigation", () => {
131
131
  test.skip.each([
132
132
  ["Page types", "/"],
133
133
  ["Custom types", "/custom-types"],
134
- ["Slices", "/slices"],
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 'Review changes' item", async () => {
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("Review changes")).toBeVisible();
162
+ expect(await screen.findByText("Changes")).toBeVisible();
161
163
  const changesItem = screen
162
- .getByText("Review changes")
163
- .closest("button") as HTMLButtonElement;
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 { ChangesItem } from "./ChangesItem";
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, actionQueueStatus } = useSliceState();
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={actionQueueStatus} />
36
+ <AutoSaveStatusIndicator status={autoSaveStatus} />
37
37
  <SimulatorButton
38
38
  isSimulatorAvailableForFramework={isSimulatorAvailableForFramework}
39
- disabled={actionQueueStatus !== "done"}
39
+ disabled={autoSaveStatus !== "saved"}
40
40
  />
41
41
  </AppLayoutActions>
42
42
  </AppLayoutHeader>