Skip to content

Commit

Permalink
Allowed for a confirm to pop up if a schematic is already open
Browse files Browse the repository at this point in the history
  • Loading branch information
pjdotson committed Jul 15, 2024
1 parent 19173f1 commit 4e0761b
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 120 deletions.
9 changes: 9 additions & 0 deletions console/src/confirm/Confirm.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
// Copyright 2024 Synnax Labs, Inc.
//
// Use of this software is governed by the Business Source License included in
// the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with the Business
// Source License, use of this software will be governed by the Apache License,
// Version 2.0, included in the file licenses/APL.txt.

import { Align, Button, Nav, Status, Text, Triggers } from "@synnaxlabs/pluto";
import { useDispatch, useStore } from "react-redux";

Expand Down
9 changes: 9 additions & 0 deletions console/src/confirm/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
// Copyright 2024 Synnax Labs, Inc.
//
// Use of this software is governed by the Business Source License included in
// the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with the Business
// Source License, use of this software will be governed by the Apache License,
// Version 2.0, included in the file licenses/APL.txt.

export * as Confirm from "@/confirm/external";
4 changes: 4 additions & 0 deletions console/src/layout/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
createSlice,
type Dispatch,
type PayloadAction,
type Store,
type UnknownAction,
} from "@reduxjs/toolkit";
import { type Synnax } from "@synnaxlabs/client";
Expand All @@ -19,6 +20,7 @@ import { Haul, Mosaic } from "@synnaxlabs/pluto";
import { type deep, id, type location } from "@synnaxlabs/x";
import { ComponentType } from "react";

import { CreateConfirmModal } from "@/confirm/Confirm";
import { type Placer } from "@/layout/hooks";
import * as latest from "@/layout/migrations";

Expand Down Expand Up @@ -103,6 +105,8 @@ export interface FileHandlerProps {
loc: location.Location;
name: string;
placer: Placer;
store: Store;
confirm: CreateConfirmModal;
client: Synnax | null;
workspaceKey: string | null;
dispatch: Dispatch<UnknownAction>;
Expand Down
4 changes: 4 additions & 0 deletions console/src/layouts/Mosaic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { memo, type ReactElement, useCallback, useLayoutEffect } from "react";
import { useDispatch, useStore } from "react-redux";

import { NAV_DRAWERS, NavDrawer, NavMenu } from "@/components/nav/Nav";
import { Confirm } from "@/confirm";
import { Layout } from "@/layout";
import { Content } from "@/layout/Content";
import { usePlacer } from "@/layout/hooks";
Expand Down Expand Up @@ -157,6 +158,7 @@ export const Mosaic = memo((): ReactElement => {
);

const workspaceKey = Workspace.useSelectActiveKey();
const confirm = Confirm.useModal();

const handleFileDrop = useCallback(
(nodeKey: number, loc: location.Location, event: React.DragEvent) => {
Expand All @@ -176,6 +178,8 @@ export const Mosaic = memo((): ReactElement => {
file: fileAsJSON,
placer,
name,
store,
confirm,
client,
workspaceKey,
loc,
Expand Down
77 changes: 0 additions & 77 deletions console/src/schematic/Schematic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
// included in the file licenses/APL.txt.

import { Dispatch, type PayloadAction } from "@reduxjs/toolkit";
import { NotFoundError } from "@synnaxlabs/client";
import { useSelectWindowKey } from "@synnaxlabs/drift/react";
import { Icon } from "@synnaxlabs/media";
import {
Expand All @@ -18,8 +17,6 @@ import {
Haul,
Legend,
Schematic as Core,
Status,
Synnax,
Text,
Theming,
Triggers,
Expand All @@ -28,9 +25,6 @@ import {
Viewport,
} from "@synnaxlabs/pluto";
import { box, deep, id, type UnknownRecord } from "@synnaxlabs/x";
import { useMutation } from "@tanstack/react-query";
import { open } from "@tauri-apps/plugin-dialog";
import { readFile } from "@tauri-apps/plugin-fs";
import {
type ReactElement,
useCallback,
Expand All @@ -43,7 +37,6 @@ import { v4 as uuidv4 } from "uuid";

import { useLoadRemote } from "@/hooks/useLoadRemote";
import { Layout } from "@/layout";
import { migrateState, STATES_Z } from "@/schematic/migrations";
import {
select,
useSelect,
Expand Down Expand Up @@ -418,73 +411,3 @@ export const create =
dispatch(internalCreate({ ...deep.copy(ZERO_STATE), key, ...rest }));
return { key, location, name, type: LAYOUT_TYPE, window, tab };
};

export interface ImportProps {
filePath?: string;
}

export const useImport = (): ((props: ImportProps) => void) => {
const addStatus = Status.useAggregator();
const placeLayout = Layout.usePlacer();
const client = Synnax.use();
const workspace = Workspace.useSelectActiveKey();
return useMutation<void, Error, ImportProps, void>({
mutationFn: async ({ filePath }) => {
let path = filePath;
if (path == null) {
const fileResponse = await open({
directory: false,
multiple: false,
title: "Import schematic into Synnax",
});
if (fileResponse == null) return;
path = fileResponse.path;
}
const file = await readFile(path);
const fileName = path.split("/").pop();
const name = fileName?.split(".")[0] ?? "New Schematic";
const importedStr = new TextDecoder().decode(file);
const json = JSON.parse(importedStr);
const z = STATES_Z.find((stateZ) => {
return stateZ.safeParse(json).success;
});
if (z == null)
throw new Error(
(fileName != null ? `${fileName} is not` : `${filePath} is not a path to`) +
" a valid schematic.",
);
const newState = migrateState(z.parse(json));
const creator = create({
...newState,
name,
});
if (client == null) {
placeLayout(creator);
return;
}
try {
const schematic = await client.workspaces.schematic.retrieve(newState.key);
await client.workspaces.schematic.setData(
schematic.key,
newState as unknown as UnknownRecord,
);
} catch (e) {
if (!NotFoundError.matches(e)) throw e;
if (workspace == null) return;
await client.workspaces.schematic.create(workspace, {
name,
data: deep.copy(newState) as unknown as UnknownRecord,
...newState,
});
}
placeLayout(creator);
},
onError: (e) =>
addStatus({
key: id.id(),
variant: "error",
message: "Failed to import schematic.",
description: e.message,
}),
}).mutate;
};
5 changes: 5 additions & 0 deletions console/src/schematic/migrations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,8 @@ export const migrateSlice = migrate.migrator<AnySliceState, SliceState>({
});

export const STATES_Z = [v0.stateZ, v1.stateZ, v2.stateZ];

export const parser: (potentialState: any) => State | null = (potentialState) => {
const z = STATES_Z.find((stateZ) => stateZ.safeParse(potentialState).success);
return z == null ? null : migrateState(z.parse(potentialState));
};
81 changes: 41 additions & 40 deletions console/src/schematic/services/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
// Version 2.0, included in the file licenses/APL.txt.

import { NotFoundError } from "@synnaxlabs/client";
import { deep, type UnknownRecord } from "@synnaxlabs/x";
import { deep, errors, type UnknownRecord } from "@synnaxlabs/x";

import { Layout } from "@/layout";
import { moveMosaicTab } from "@/layout/slice";
import { migrateState, STATES_Z } from "@/schematic/migrations";
import { parser } from "@/schematic/migrations";
import { create } from "@/schematic/Schematic";
import { select } from "@/schematic/selectors";
import { remove } from "@/schematic/slice";

export const FileHandler: Layout.FileHandler = async ({
mosaicKey,
Expand All @@ -23,56 +25,55 @@ export const FileHandler: Layout.FileHandler = async ({
name,
client,
workspaceKey,
confirm,
dispatch,
store,
}): Promise<boolean> => {
const z = STATES_Z.find((stateZ) => {
return stateZ.safeParse(file).success;
});
if (z == null) return false;
const state = migrateState(z.parse(file));
const newState = parser(file);
if (newState == null) return false;
const creator = create({
...state,
...newState,
name,
});
if (client == null) {
placer(creator);
return true;
const key = newState.key;
const existingState = select(store.getState(), key);
if (existingState != null) {
if (deep.equal(existingState, newState)) throw Error(`${name} already exists.`);
if (
!(await confirm({
message: `${name} already exists`,
description: "Would you like to replace the existing schematic?",
cancel: { label: "Cancel" },
confirm: { label: "Replace", variant: "error" },
}))
)
throw errors.CANCELED;
Layout.remove({ keys: [key] });
remove({ keys: [key] });
}
try {
const schematic = await client.workspaces.schematic.retrieve(state.key);
await client.workspaces.schematic.setData(
schematic.key,
state as unknown as UnknownRecord,
);
} catch (e) {
if (!NotFoundError.matches(e)) throw e;
if (workspaceKey == null) {
console.log("creator", creator);
const foo = placer(creator);
console.log("foo", foo);
dispatch(
moveMosaicTab({
key: mosaicKey,
windowKey: foo.windowKey,
tabKey: foo.key,
loc,
}),
if (client != null) {
try {
await client.workspaces.schematic.retrieve(key);
await client.workspaces.schematic.setData(
key,
newState as unknown as UnknownRecord,
);
return true;
} catch (e) {
if (!NotFoundError.matches(e)) throw e;
if (workspaceKey == null) throw Error("Workspace key is required.");
await client.workspaces.schematic.create(workspaceKey, {
name,
data: newState as unknown as UnknownRecord,
...newState,
});
}
await client.workspaces.schematic.create(workspaceKey, {
name,
data: deep.copy(state) as unknown as UnknownRecord,
...state,
});
}
const foo = placer(creator);
console.log("foo", foo);
const windowKey = placer(creator).windowKey;
dispatch(
moveMosaicTab({
key: mosaicKey,
windowKey: foo.windowKey,
tabKey: state.key,
windowKey,
tabKey: newState.key,
loc,
}),
);
Expand Down
Loading

0 comments on commit 4e0761b

Please sign in to comment.