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 2, 2024
1 parent 278aa67 commit b448f3c
Show file tree
Hide file tree
Showing 44 changed files with 1,098 additions and 925 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 @@ -42,6 +44,7 @@ export const RenameSliceModal: React.FC<RenameSliceModalProps> = ({
renamedSlice.model.id,
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, Icon, 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 { useActiveEnvironment } from "@src/features/environments/useActiveEnvironment";

export const ChangesItem: FC = () => {
const { setSeenChangesToolTip } = useSliceMachineActions();
const open = useOpenChangesHoverCard();
const router = useRouter();
const { activeEnvironment } = useActiveEnvironment();
const isDevEnvironment = activeEnvironment?.kind === "dev";

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

return (
<HoverCard
open={open}
openDelay={3000}
onClose={onClose}
side="right"
sideOffset={24}
collisionPadding={280}
trigger={
<Button
color="grey"
disabled={isDevEnvironment}
sx={{ marginBottom: 16 }}
onClick={() => {
void router.push("/changes");
}}
>
<Box alignItems="center" gap={4}>
{isDevEnvironment ? (
<>
<Icon name="refresh" size="medium" color="grey11" />
<Text>Auto-sync activated</Text>
</>
) : (
<>
<Text>Review changes</Text> <ChangesCount />
</>
)}
</Box>
</Button>
}
>
<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 b448f3c

Please sign in to comment.