Skip to content

Commit

Permalink
feat(auto-sync): Add an AutoSyncProvider and trigger a sync changes w…
Browse files Browse the repository at this point in the history
…hen needed
  • Loading branch information
xrutayisire committed Feb 5, 2024
1 parent cc80883 commit 70d9a10
Show file tree
Hide file tree
Showing 49 changed files with 1,227 additions and 932 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,23 @@ import { ChangesSectionHeader } from "@components/ChangesSectionHeader";
import { CustomTypeTable } from "@components/CustomTypeTable/changesPage";
import Grid from "@components/Grid";
import { ComponentUI } from "@lib/models/common/ComponentUI";
import { ModelStatusInformation } from "@src/hooks/useModelStatus";
import ScreenshotChangesModal from "@components/ScreenshotChangesModal";
import { countMissingScreenshots } from "@src/domain/slice";
import { useScreenshotChangesModal } from "@src/hooks/useScreenshotChangesModal";
import { ModelStatus } from "@lib/models/common/ModelStatus";
import { LocalOrRemoteCustomType } from "@lib/models/common/ModelData";
import { SharedSliceCard } from "@src/features/slices/sliceCards/SharedSliceCard";
import { AuthStatus } from "@src/modules/userContext/types";
import { ModelsStatuses } from "@src/features/sync/getUnSyncChanges";

import { DevCollaborationExperiment } from "./DevCollaborationExperiment";

interface ChangesItemsProps extends ModelStatusInformation {
interface ChangesItemsProps {
unSyncedCustomTypes: LocalOrRemoteCustomType[];
unSyncedSlices: ComponentUI[];
modelsStatuses: ModelsStatuses;
authStatus: AuthStatus;
isOnline: boolean;
}

export const ChangesItems: React.FC<ChangesItemsProps> = ({
Expand Down
19 changes: 12 additions & 7 deletions packages/slice-machine/components/CustomTypeTable/changesPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Link from "next/link";
import React from "react";
import { Box, Text } from "theme-ui";
import { ModelStatusInformation } from "@src/hooks/useModelStatus";
import { CustomTypeSM } from "@lib/models/common/CustomType";
import { ModelStatus } from "@lib/models/common/ModelStatus";
import {
Expand All @@ -10,18 +9,26 @@ import {
} from "@lib/models/common/ModelData";
import { StatusBadge } from "@src/features/changes/StatusBadge";
import { CUSTOM_TYPES_CONFIG } from "@src/features/customTypes/customTypesConfig";
import { AuthStatus } from "@src/modules/userContext/types";
import { ModelsStatuses } from "@src/features/sync/getUnSyncChanges";

interface CustomTypeTableProps extends ModelStatusInformation {
interface CustomTypeTableProps {
customTypes: LocalOrRemoteCustomType[];
modelsStatuses: ModelsStatuses;
authStatus: AuthStatus;
isOnline: boolean;
}

const firstColumnWidth = "40%";
const secondColumnWidth = "40%";
const thirdColumnWidth = "20%";

const CustomTypeChangeRow: React.FC<
{ ct: CustomTypeSM; status: ModelStatus } & ModelStatusInformation
> = ({ ct, status, authStatus, isOnline }) => {
const CustomTypeChangeRow: React.FC<{
ct: CustomTypeSM;
status: ModelStatus;
authStatus: AuthStatus;
isOnline: boolean;
}> = ({ ct, status, authStatus, isOnline }) => {
return (
<>
<Box as={"td"} style={{ width: firstColumnWidth }}>
Expand Down Expand Up @@ -80,7 +87,6 @@ export const CustomTypeTable: React.FC<CustomTypeTableProps> = ({
status={modelsStatuses.customTypes[customType.local.id]}
authStatus={authStatus}
isOnline={isOnline}
modelsStatuses={modelsStatuses}
key={customType.local.id}
/>
</tr>
Expand All @@ -97,7 +103,6 @@ export const CustomTypeTable: React.FC<CustomTypeTableProps> = ({
status={modelsStatuses.customTypes[customType.remote.id]}
authStatus={authStatus}
isOnline={isOnline}
modelsStatuses={modelsStatuses}
key={customType.remote.id}
/>
</tr>
Expand Down
3 changes: 3 additions & 0 deletions packages/slice-machine/components/DeleteSliceModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import useSliceMachineActions from "@src/modules/useSliceMachineActions";
import Card from "@components/Card";
import { Button } from "@components/Button";
import { deleteSlice } from "@src/features/slices/actions/deleteSlice";
import { useAutoSync } from "@src/features/sync/AutoSyncProvider";

type DeleteSliceModalProps = {
isOpen: boolean;
Expand All @@ -21,6 +22,7 @@ export const DeleteSliceModal: React.FunctionComponent<
> = ({ sliceId, sliceName, libName, isOpen, onClose }) => {
const [isDeleting, setIsDeleting] = useState(false);
const { deleteSliceSuccess } = useSliceMachineActions();
const { syncChanges } = useAutoSync();
const { theme } = useThemeUI();

const onDelete = async () => {
Expand All @@ -32,6 +34,7 @@ export const DeleteSliceModal: React.FunctionComponent<
libraryID: libName,
onSuccess: () => {
deleteSliceSuccess(sliceId, libName);
syncChanges();
},
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
} from "@src/features/customTypes/actions/createCustomType";
import { CUSTOM_TYPES_CONFIG } from "@src/features/customTypes/customTypesConfig";
import { getFormat } from "@src/domain/customType";
import { useAutoSync } from "@src/features/sync/AutoSyncProvider";

import { InputBox } from "../components/InputBox";
import { SelectRepeatable } from "../components/SelectRepeatable";
Expand Down Expand Up @@ -58,6 +59,7 @@ export const CreateCustomTypeModal: React.FC<CreateCustomTypeModalProps> = ({
);
const customTypesMessages = CUSTOM_TYPES_MESSAGES[format];
const [isIdFieldPristine, setIsIdFieldPristine] = useState(true);
const { syncChanges } = useAutoSync();
const router = useRouter();

const onSubmit = async ({ id, label, repeatable }: FormValues) => {
Expand Down Expand Up @@ -86,6 +88,8 @@ export const CreateCustomTypeModal: React.FC<CreateCustomTypeModalProps> = ({
}
: undefined,
});

syncChanges();
},
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import ModalFormCard from "@components/ModalFormCard";
import { createSlice } from "@src/features/slices/actions/createSlice";
import useSliceMachineActions from "@src/modules/useSliceMachineActions";
import { getState } from "@src/apiClient";
import { useAutoSync } from "@src/features/sync/AutoSyncProvider";

import { validateSliceModalValues } from "../formsValidator";
import { InputBox } from "../components/InputBox";
Expand All @@ -30,6 +31,7 @@ export const CreateSliceModal: FC<CreateSliceModalProps> = ({
}) => {
const { createSliceSuccess } = useSliceMachineActions();
const [isCreatingSlice, setIsCreatingSlice] = useState(false);
const { syncChanges } = useAutoSync();

const onSubmit = async (values: FormValues) => {
const sliceName = values.sliceName;
Expand All @@ -45,8 +47,8 @@ export const CreateSliceModal: FC<CreateSliceModalProps> = ({
const serverState = await getState();
// Update Redux store
createSliceSuccess(serverState.libraries);

onSuccess(newSlice, libraryName);
syncChanges();
},
});
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { CustomTypeFormat } from "@slicemachine/manager";
import { CUSTOM_TYPES_MESSAGES } from "@src/features/customTypes/customTypesMessages";

import { renameCustomType } from "@src/features/customTypes/actions/renameCustomType";
import { useAutoSync } from "@src/features/sync/AutoSyncProvider";

interface RenameCustomTypeModalProps {
isChangesLocal: boolean;
Expand All @@ -32,6 +33,7 @@ export const RenameCustomTypeModal: React.FC<RenameCustomTypeModalProps> = ({
const customTypeName = customType?.label ?? "";
const customTypeId = customType?.id ?? "";
const { renameCustomTypeSuccess } = useSliceMachineActions();
const { syncChanges } = useAutoSync();

const [isRenaming, setIsRenaming] = useState(false);

Expand All @@ -46,7 +48,10 @@ export const RenameCustomTypeModal: React.FC<RenameCustomTypeModalProps> = ({
await renameCustomType({
model: customType,
newLabel: values.customTypeName,
onSuccess: renameCustomTypeSuccess,
onSuccess: (renamedCustomType) => {
renameCustomTypeSuccess(renamedCustomType);
syncChanges();
},
});
}
setIsRenaming(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { SliceMachineStoreType } from "@src/redux/type";
import { getLibraries, getRemoteSlices } from "@src/modules/slices";
import { ComponentUI } from "@lib/models/common/ComponentUI";
import { renameSlice } from "@src/features/slices/actions/renameSlice";
import { useAutoSync } from "@src/features/sync/AutoSyncProvider";

import { InputBox } from "../components/InputBox";
import ModalFormCard from "../../ModalFormCard";
Expand All @@ -24,6 +25,7 @@ export const RenameSliceModal: React.FC<RenameSliceModalProps> = ({
onClose,
}) => {
const { renameSliceSuccess } = useSliceMachineActions();
const { syncChanges } = useAutoSync();
const { localLibs, remoteLibs } = useSelector(
(store: SliceMachineStoreType) => ({
localLibs: getLibraries(store),
Expand All @@ -39,6 +41,7 @@ export const RenameSliceModal: React.FC<RenameSliceModalProps> = ({
newSliceName: values.sliceName,
onSuccess: (renamedSlice) => {
renameSliceSuccess(renamedSlice.from, renamedSlice.model);
syncChanges();
},
});

Expand Down
42 changes: 40 additions & 2 deletions packages/slice-machine/components/LoginModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
Text,
} from "theme-ui";
import SliceMachineModal from "@components/SliceMachineModal";
import { checkAuthStatus, startAuth } from "@src/apiClient";
import { checkAuthStatus, getState, startAuth } from "@src/apiClient";
import { buildEndpoints } from "@lib/prismic/endpoints";
import { startPolling } from "@lib/utils/poll";
import { CheckAuthStatusResponse } from "@models/common/Auth";
Expand All @@ -25,6 +25,12 @@ import { getEnvironment } from "@src/modules/environment";
import useSliceMachineActions from "@src/modules/useSliceMachineActions";
import preferWroomBase from "@lib/utils/preferWroomBase";
import { ToasterType } from "@src/modules/toaster";
import { getUnSyncedChanges } from "@src/features/sync/getUnSyncChanges";
import { normalizeFrontendCustomTypes } from "@lib/models/common/normalizers/customType";
import { normalizeFrontendSlices } from "@lib/models/common/normalizers/slices";
import { AuthStatus } from "@src/modules/userContext/types";
import { useAutoSync } from "@src/features/sync/AutoSyncProvider";
import { getActiveEnvironment } from "@src/features/environments/actions/getActiveEnvironment";

interface ValidAuthStatus extends CheckAuthStatusResponse {
status: "ok";
Expand All @@ -42,7 +48,7 @@ const LoginModal: React.FunctionComponent = () => {
env: getEnvironment(store),
}),
);

const { syncChanges } = useAutoSync();
const { closeModals, startLoadingLogin, stopLoadingLogin, openToaster } =
useSliceMachineActions();

Expand Down Expand Up @@ -72,6 +78,38 @@ const LoginModal: React.FunctionComponent = () => {
openToaster("Logged in", ToasterType.SUCCESS);
stopLoadingLogin();
closeModals();

const serverState = await getState();
const slices = normalizeFrontendSlices(
serverState.libraries,
serverState.remoteSlices,
);
const customTypes = Object.values(
normalizeFrontendCustomTypes(
serverState.customTypes,
serverState.remoteCustomTypes,
),
);
const { changedCustomTypes, changedSlices } = getUnSyncedChanges({
authStatus: AuthStatus.AUTHORIZED,
customTypes,
isOnline: true,
libraries: serverState.libraries,
slices,
});
const { activeEnvironment } = await getActiveEnvironment();

if (
activeEnvironment?.kind === "dev" &&
(changedCustomTypes.length > 0 || changedSlices.length > 0)
) {
syncChanges({
environment: activeEnvironment,
loggedIn: true,
changedCustomTypes,
changedSlices,
});
}
} catch (e) {
stopLoadingLogin();
openToaster("Login failed", ToasterType.ERROR);
Expand Down
117 changes: 117 additions & 0 deletions packages/slice-machine/components/Navigation/ChangesItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { type FC } from "react";
import { useRouter } from "next/router";
import { Box, Button, Text } from "@prismicio/editor-ui";

import {
HoverCard,
HoverCardCloseButton,
HoverCardDescription,
HoverCardMedia,
HoverCardTitle,
} from "@src/components/HoverCard";
import useSliceMachineActions from "@src/modules/useSliceMachineActions";
import { useNetwork } from "@src/hooks/useNetwork";
import { useAuthStatus } from "@src/hooks/useAuthStatus";
import { useUnSyncChanges } from "@src/features/sync/useUnSyncChanges";
import { AuthStatus } from "@src/modules/userContext/types";
import { useAutoSync } from "@src/features/sync/AutoSyncProvider";
import { AutoSyncStatusIndicator } from "@src/features/sync/components/AutoSyncStatusIndicator";

export const ChangesItem: FC = () => {
const { setSeenChangesToolTip } = useSliceMachineActions();
const open = useOpenChangesHoverCard();
const router = useRouter();
const { autoSyncStatus } = useAutoSync();

const onClose = () => {
setSeenChangesToolTip();
};

return (
<HoverCard
open={open}
openDelay={3000}
onClose={onClose}
side="right"
sideOffset={24}
collisionPadding={280}
trigger={
<Box padding={{ bottom: 24 }}>
{autoSyncStatus === "failed" ||
autoSyncStatus === "synced" ||
autoSyncStatus === "syncing" ? (
<AutoSyncStatusIndicator autoSyncStatus={autoSyncStatus} />
) : (
// TODO(DT-1942): This should be a Button with a link component for
// accessibility
<Button
color="grey"
onClick={() => {
void router.push("/changes");
}}
sx={{ width: "100%" }}
>
<Box alignItems="center" gap={4}>
<Text variant="bold">Review changes</Text> <ChangesCount />
</Box>
</Button>
)}
</Box>
}
>
<HoverCardTitle>Push your changes</HoverCardTitle>
<HoverCardMedia component="image" src="/push.png" />
<HoverCardDescription>
When you click Save, your changes are saved locally. Then, you can push
your models to Prismic from the Changes page.
</HoverCardDescription>
<HoverCardCloseButton>Got it</HoverCardCloseButton>
</HoverCard>
);
};

export const ChangesCount: FC = () => {
const isOnline = useNetwork();
const authStatus = useAuthStatus();
const { unSyncedSlices, unSyncedCustomTypes } = useUnSyncChanges();
const numberOfChanges = unSyncedSlices.length + unSyncedCustomTypes.length;

if (
!isOnline ||
authStatus === AuthStatus.UNAUTHORIZED ||
authStatus === AuthStatus.FORBIDDEN
) {
return null;
}

if (numberOfChanges === 0) {
return null;
}

const formattedNumberOfChanges = numberOfChanges > 9 ? "+9" : numberOfChanges;

return (
<Box padding={{ inline: 6 }} borderRadius={10} backgroundColor="grey5">
<Text color="grey11" variant="small">
{formattedNumberOfChanges}
</Text>
</Box>
);
};

// TODO(DT-1925): Reactivate this feature
const useOpenChangesHoverCard = () => {
// const { hasSeenChangesToolTip, hasSeenSimulatorToolTip } = useSelector(
// (store: SliceMachineStoreType) => ({
// hasSeenChangesToolTip: userHasSeenChangesToolTip(store),
// hasSeenSimulatorToolTip: userHasSeenSimulatorToolTip(store),
// }),
// );

// return (
// !hasSeenChangesToolTip &&
// hasSeenSimulatorToolTip
// );

return false;
};
Loading

0 comments on commit 70d9a10

Please sign in to comment.