Skip to content

Commit

Permalink
refactor: replace doRemote... functions with useMutation (#449)
Browse files Browse the repository at this point in the history
  • Loading branch information
lihebi committed Aug 14, 2023
1 parent ced3c7e commit 6df5151
Show file tree
Hide file tree
Showing 11 changed files with 267 additions and 542 deletions.
1 change: 0 additions & 1 deletion api/src/resolver_repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,6 @@ export default {
Query: {
repo,
getDashboardRepos,
getVisibility,
},
Mutation: {
createRepo,
Expand Down
6 changes: 0 additions & 6 deletions api/src/typedefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ export const typeDefs = gql`
codeiumAPIKey: String!
}
type Visibility {
collaborators: [User]
isPublic: Boolean
}
type Repo {
id: ID!
name: String
Expand Down Expand Up @@ -108,7 +103,6 @@ export const typeDefs = gql`
pod(id: ID!): Pod
getDashboardRepos: [Repo]
activeSessions: [String]
getVisibility(repoId: String!): Visibility
listAllRuntimes: [RuntimeInfo]
infoRuntime(sessionId: String!): RuntimeInfo
}
Expand Down
16 changes: 9 additions & 7 deletions ui/src/components/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,6 @@ function useJump() {
const setFocusedEditor = useStore(store, (state) => state.setFocusedEditor);

const nodesMap = useStore(store, (state) => state.getNodesMap());
const pods = useStore(store, (state) => state.pods);

const reactflow = useReactFlow();

Expand Down Expand Up @@ -205,9 +204,12 @@ function useJump() {
}

// get the sibling nodes
const nodes = Array.from(nodesMap.values()).filter(
const siblings = Array.from(nodesMap.values()).filter(
(node) => node.parentNode === pod.parentNode
);
const children = Array.from(nodesMap.values()).filter(
(node) => node.parentNode === id
);

let to: null | Node = null;

Expand All @@ -220,7 +222,7 @@ function useJump() {
to = pod;
}
} else {
to = getBestNode(nodes, pod, "up");
to = getBestNode(siblings, pod, "up");
}
break;
case "ArrowDown":
Expand All @@ -231,7 +233,7 @@ function useJump() {
(pod.height || 1) ** 2 + (pod.width || 1) ** 2
);
let childDist = 0;
for (const child of pods[id].children) {
for (const child of children) {
childDist = Math.sqrt(
nodesMap.get(child.id)!.position.x ** 2 +
nodesMap.get(child.id)!.position.y ** 2
Expand All @@ -245,14 +247,14 @@ function useJump() {
to = pod;
}
} else {
to = getBestNode(nodes, pod, "down");
to = getBestNode(siblings, pod, "down");
}
break;
case "ArrowLeft":
to = getBestNode(nodes, pod, "left");
to = getBestNode(siblings, pod, "left");
break;
case "ArrowRight":
to = getBestNode(nodes, pod, "right");
to = getBestNode(siblings, pod, "right");
break;
case "Enter":
if (pod.type == "CODE") {
Expand Down
1 change: 0 additions & 1 deletion ui/src/components/MyMonaco.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,6 @@ export const MyMonaco = memo<MyMonacoProps>(function MyMonaco({
if (!store) throw new Error("Missing BearContext.Provider in the tree");
const readOnly = useStore(store, (state) => state.role === "GUEST");
const showLineNumbers = useStore(store, (state) => state.showLineNumbers);
const initPodContent = useStore(store, (state) => state.initPodContent);
const clearResults = useStore(store, (s) => s.clearResults);
const wsRun = useStore(store, (state) => state.wsRun);
const setPodFocus = useStore(store, (state) => state.setPodFocus);
Expand Down
41 changes: 31 additions & 10 deletions ui/src/components/SettingDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { useStore } from "zustand";
import { RepoContext } from "../lib/store";
import { Link, Stack } from "@mui/material";
import LaunchIcon from "@mui/icons-material/Launch";
import { useApolloClient } from "@apollo/client";
import { gql, useApolloClient, useMutation } from "@apollo/client";
import Button from "@mui/material/Button";
import DoneIcon from "@mui/icons-material/Done";
import CloseIcon from "@mui/icons-material/Close";
Expand All @@ -34,7 +34,6 @@ export function SettingDialog({ open = false }: SettingDiagProps) {
const user = useStore(store, (state) => state.user);
const isCustomToken = useStore(store, (state) => state.isCustomToken);
const setIsCustomToken = useStore(store, (state) => state.setIsCustomToken);
const updateAPIKey = useStore(store, (state) => state.updateAPIKey);
const client = useApolloClient();
const inputRef = useRef<HTMLInputElement>(null);
const [infoShow, setInfoShow] = useState(false);
Expand All @@ -53,6 +52,25 @@ export function SettingDialog({ open = false }: SettingDiagProps) {
setInfoShow(false);
};

const [updateCodeiumAPIKey, { data, error }] = useMutation(gql`
mutation updateCodeiumAPIKey($apiKey: String!) {
updateCodeiumAPIKey(apiKey: $apiKey)
}
`);

useEffect(() => {
if (data?.updateCodeiumAPIKey) {
setStatus("success");
setMessage(`${user.name}, welcome. Token updated`);
setInfoShow(true);
}
if (error) {
setStatus("error");
setMessage(error.message || "Unknown error");
setInfoShow(true);
}
}, [data, error]);

const updateToken = async () => {
const token = inputRef.current?.value.trim();
if (!token) {
Expand All @@ -70,13 +88,10 @@ export function SettingDialog({ open = false }: SettingDiagProps) {
: "Unknown error"
);
}
if (await updateAPIKey(client, api_key)) {
setStatus("success");
setMessage(`${name}, welcome. Token updated`);
setInfoShow(true);
} else {
throw new Error("Update failed");
}
updateCodeiumAPIKey({
variables: { apiKey: api_key },
refetchQueries: ["Me"],
});
} catch (e) {
setStatus("error");
setMessage((e as Error).message || "Unknown error");
Expand Down Expand Up @@ -131,7 +146,13 @@ export function SettingDialog({ open = false }: SettingDiagProps) {
{apiKey && (
<Button
endIcon={<CloseIcon />}
onClick={() => updateAPIKey(client, "")}
// FIXME use clearToken mutation instead.
onClick={() =>
updateCodeiumAPIKey({
variables: { apiKey: "" },
refetchQueries: ["Me"],
})
}
>
Clear Stored Token
</Button>
Expand Down
133 changes: 86 additions & 47 deletions ui/src/components/ShareProjDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Button from "@mui/material/Button";
import Alert from "@mui/material/Alert";
import { AlertColor } from "@mui/material/Alert";
import Snackbar from "@mui/material/Snackbar";
import { useState } from "react";
import { useEffect, useState } from "react";
import ListSubheader from "@mui/material/ListSubheader";
import List from "@mui/material/List";
import ListItem from "@mui/material/ListItem";
Expand All @@ -26,7 +26,7 @@ import React, { useContext, useReducer } from "react";
import { CopyToClipboard } from "react-copy-to-clipboard";
import { useStore } from "zustand";
import { RepoContext } from "../lib/store";
import { useApolloClient } from "@apollo/client";
import { gql, useApolloClient, useMutation } from "@apollo/client";

const initialState = { showInfo: false, status: "info", message: "wait..." };

Expand Down Expand Up @@ -102,15 +102,27 @@ function reducer(state, action) {
}
}

function CollaboratorList({ collaborators, dispatch, isOwner }) {
function CollaboratorList({ repoId, collaborators, dispatch, isOwner }) {
const store = useContext(RepoContext);
if (!store) throw new Error("Missing BearContext.Provider in the tree");
const apolloClient = useApolloClient();
const deleteCollaborator = useStore(
store,
(state) => state.deleteCollaborator
const [deleteCollaborator, { data, loading, error }] = useMutation(
gql`
mutation deleteCollaborator($repoId: String!, $collaboratorId: String!) {
deleteCollaborator(repoId: $repoId, collaboratorId: $collaboratorId)
}
`
);

useEffect(() => {
if (error) {
dispatch({ type: "delete error", message: error.message });
}
if (data) {
dispatch({ type: "delete success", name: data.deleteCollaborator });
}
}, [error]);

if (!collaborators || collaborators?.length === 0) {
return (
<List dense={true}>
Expand All @@ -129,15 +141,6 @@ function CollaboratorList({ collaborators, dispatch, isOwner }) {
);
}

async function handleDeleteCollaborator(userId, name) {
const { success, error } = await deleteCollaborator(apolloClient, userId);
if (success) {
dispatch({ type: "delete success", name });
} else {
dispatch({ type: "delete error", message: error.message });
}
}

return (
<List
sx={{
Expand All @@ -155,10 +158,12 @@ function CollaboratorList({ collaborators, dispatch, isOwner }) {
edge="end"
aria-label="delete"
onClick={() =>
handleDeleteCollaborator(
collab.id,
collab.firstname + " " + collab.lastname
)
deleteCollaborator({
variables: {
repoId: repoId,
collaboratorId: collab.id,
},
})
}
>
<CloseIcon />
Expand All @@ -182,8 +187,46 @@ function CollaboratorList({ collaborators, dispatch, isOwner }) {
);
}

const aboutVisibility =
"A private project is only visible to you and collaborators, while a public project is visible to everyone. For both of them, only the owner can invite collaborators by their email addresses, and only collaborators can edit the project. The owner can change the visibility of a project at any time.";
const useUpdateVisibility = ({ dispatch }) => {
const [updateVisibility, { data, error }] = useMutation(gql`
mutation updateVisibility($repoId: String!, $isPublic: Boolean!) {
updateVisibility(repoId: $repoId, isPublic: $isPublic)
}
`);

useEffect(() => {
if (error) {
dispatch({ type: "change visibility error", message: error.message });
}
if (data) {
dispatch({ type: "change visibility success" });
}
}, [data, error]);
return updateVisibility;
};

const useAddCollaborator = ({ dispatch }) => {
const [addCollaborator, { data, error }] = useMutation(gql`
mutation addCollaborator($repoId: String!, $email: String!) {
addCollaborator(repoId: $repoId, email: $email)
}
`);

useEffect(() => {
if (error) {
dispatch({ type: "inivite error", message: error.message });
}
if (data) {
dispatch({ type: "inivite success", email: data.addCollaborator });
}
}, [data, error]);
return addCollaborator;
};

const aboutVisibility = `A private project is only visible to you and collaborators, while a public
project is visible to everyone. For both of them, only the owner can invite
collaborators by their email addresses, and only collaborators can edit the
project. The owner can change the visibility of a project at any time.`;

export function ShareProjDialog({
open = false,
Expand All @@ -197,34 +240,12 @@ export function ShareProjDialog({
const isPublic = useStore(store, (state) => state.isPublic);
const collaborators = useStore(store, (state) => state.collaborators);
const setShareOpen = useStore(store, (state) => state.setShareOpen);
const updateVisibility = useStore(store, (state) => state.updateVisibility);
const addCollaborator = useStore(store, (state) => state.addCollaborator);
const isOwner = useStore(store, (state) => state.role === "OWNER");
const title = useStore(store, (state) => state.repoName || "Untitled");
const url = `${window.location.protocol}//${window.location.host}/repo/${id}`;
const inputRef = React.useRef<HTMLInputElement>(null);

async function flipVisibility() {
if (await updateVisibility(apolloClient, !isPublic)) {
dispatch({ type: "change visibility success" });
} else {
dispatch({ type: "change visibility error", message: "Unknown error" });
}
}

async function onShare() {
const email = inputRef?.current?.value;
if (!email) {
dispatch({ type: "error", message: "Email cannot be empty" });
return;
}
const { success, error } = await addCollaborator(apolloClient, email);
if (success) {
dispatch({ type: "inivite success", email });
} else {
dispatch({ type: "inivite error", message: error.message });
}
}
const updateVisibility = useUpdateVisibility({ dispatch });
const addCollaborator = useAddCollaborator({ dispatch });

function onCloseAlert(event: React.SyntheticEvent | Event, reason?: string) {
if (reason === "clickaway") {
Expand Down Expand Up @@ -289,7 +310,14 @@ export function ShareProjDialog({
</IconButton>
</Tooltip>
{isOwner && (
<Button sx={{ float: "right" }} onClick={flipVisibility}>
<Button
sx={{ float: "right" }}
onClick={() => {
updateVisibility({
variables: { repoId: id, isPublic: !isPublic },
});
}}
>
Make it {isPublic ? "private" : "public"}
</Button>
)}
Expand All @@ -307,6 +335,7 @@ export function ShareProjDialog({
)}

<CollaboratorList
repoId={id}
collaborators={collaborators}
isOwner={isOwner}
dispatch={dispatch}
Expand Down Expand Up @@ -336,7 +365,17 @@ export function ShareProjDialog({
>
Cancel
</Button>
<Button onClick={onShare} disabled={!isOwner}>
<Button
onClick={() => {
const email = inputRef?.current?.value;
if (!email) {
dispatch({ type: "error", message: "Email cannot be empty" });
return;
}
addCollaborator({ variables: { repoId: id, email } });
}}
disabled={!isOwner}
>
Share
</Button>
</DialogActions>
Expand Down
Loading

0 comments on commit 6df5151

Please sign in to comment.