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 8, 2024
1 parent 6da2e44 commit 16300ac
Show file tree
Hide file tree
Showing 53 changed files with 1,433 additions and 933 deletions.
38 changes: 38 additions & 0 deletions packages/init/test/__testutils__/loginAndActionWithStdin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { PrismicAuthLoginResponse } from "./createPrismicAuthLoginResponse";

export async function loginAndFetchUserDataWithStdin(

Check failure on line 3 in packages/init/test/__testutils__/loginAndActionWithStdin.ts

View workflow job for this annotation

GitHub Actions / packages (@slicemachine/init)

Missing return type on function
prismicAuthLoginResponse: PrismicAuthLoginResponse,
action: () => Promise<void>,

Check failure on line 5 in packages/init/test/__testutils__/loginAndActionWithStdin.ts

View workflow job for this annotation

GitHub Actions / packages (@slicemachine/init)

'action' is defined but never used. Allowed unused args must match /^_/u
) {
const stdin = mockStdin();

// @ts-expect-error - Accessing protected method
const promise = initProcess.loginAndFetchUserData();

await new Promise((res) => setTimeout(res, 50));

stdin.send("o").restore();

await new Promise((res) => setTimeout(res, 50));

const port: number =
spiedManager.user.getLoginSessionInfo.mock.results[0].value.port;

const body = JSON.stringify(prismicAuthLoginResponse);

// We use low-level `http` because node-fetch has some issue with 127.0.0.1 on CIs
const request = http.request({
host: "127.0.0.1",
port: `${port}`,
path: "/",
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": Buffer.byteLength(body),
},
});
request.write(body);
request.end();

return promise;
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const OfflinePage = () => {
<Text
sx={{ fontSize: "13px", lineHeight: "24px", textAlign: "center" }}
>
You need a stable internet connection to review and push changes
Connect to the internet to sync your changes.
</Text>
</Flex>
</Flex>
Expand Down
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
Loading

0 comments on commit 16300ac

Please sign in to comment.