Skip to content

Commit 6a78d32

Browse files
authored
chore: enhance delete repo UX (#364)
1 parent 6b99329 commit 6a78d32

File tree

1 file changed

+77
-24
lines changed

1 file changed

+77
-24
lines changed

ui/src/pages/repos.tsx

Lines changed: 77 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useQuery, useMutation, gql } from "@apollo/client";
1+
import { useQuery, useMutation, gql, useApolloClient } from "@apollo/client";
22
import React, { useState, useEffect, useCallback } from "react";
33

44
import Link from "@mui/material/Link";
@@ -32,13 +32,35 @@ import {
3232
DialogActions,
3333
DialogContent,
3434
DialogTitle,
35+
useTheme,
3536
} from "@mui/material";
3637
import { useAuth } from "../lib/auth";
3738
import { GoogleSignin } from "./login";
3839
import { timeDifference } from "../lib/utils";
40+
import { useSnackbar } from "notistack";
3941

40-
function RepoLine({ repo, deletable, sharable, runtimeInfo, onDeleteRepo }) {
42+
const GET_REPOS = gql`
43+
query GetRepos {
44+
myRepos {
45+
name
46+
id
47+
public
48+
updatedAt
49+
createdAt
50+
}
51+
}
52+
`;
53+
54+
function RepoLine({
55+
repo,
56+
deletable,
57+
sharable,
58+
runtimeInfo,
59+
onDeleteRepo,
60+
deleting,
61+
}) {
4162
const { me } = useMe();
63+
const theme = useTheme();
4264
const [killRuntime] = useMutation(
4365
gql`
4466
mutation killRuntime($sessionId: String!) {
@@ -50,14 +72,25 @@ function RepoLine({ repo, deletable, sharable, runtimeInfo, onDeleteRepo }) {
5072
}
5173
);
5274

75+
// haochen: any reason not using Loading state from useMutation?
5376
const [killing, setKilling] = useState(false);
5477
return (
5578
<TableRow
5679
key={repo.id}
5780
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
5881
>
5982
<TableCell align="center">
60-
<Link component={ReactLink} to={`/repo/${repo.id}`}>
83+
<Link
84+
component={ReactLink}
85+
to={`/repo/${repo.id}`}
86+
sx={
87+
deleting && {
88+
color: theme.palette.action.disabled,
89+
textDecorationColor: theme.palette.action.disabled,
90+
pointerEvents: "none",
91+
}
92+
}
93+
>
6194
<Box
6295
sx={{
6396
display: "flex",
@@ -94,20 +127,25 @@ function RepoLine({ repo, deletable, sharable, runtimeInfo, onDeleteRepo }) {
94127
{deletable && (
95128
<Tooltip title="Delete Repo">
96129
<IconButton
130+
disabled={deleting}
97131
size="small"
98132
onClick={() => {
99133
// FIXME ensure the runtime is killed
100134
onDeleteRepo(repo);
101135
}}
102136
>
103-
<DeleteIcon fontSize="inherit" />
137+
{deleting ? (
138+
<CircularProgress size="14px" />
139+
) : (
140+
<DeleteIcon fontSize="inherit" />
141+
)}
104142
</IconButton>
105143
</Tooltip>
106144
)}
107145
{runtimeInfo ? (
108146
<Tooltip title="Kill runtime">
109147
<IconButton
110-
disabled={killing}
148+
disabled={killing || deleting}
111149
size="small"
112150
onClick={async () => {
113151
// FIXME when to set killing=false?
@@ -202,14 +240,29 @@ function RepoList({ repos }) {
202240
const [clickedRepo, setClickedRepo] = useState<
203241
{ id: string; name: string } | undefined
204242
>();
205-
const [deleteRepo] = useMutation(
243+
const [isConfirmDeleteDialogOpen, setConfirmDeleteDialogOpen] =
244+
useState(false);
245+
const { enqueueSnackbar } = useSnackbar();
246+
const client = useApolloClient();
247+
const [deleteRepo, deleteRepoResult] = useMutation(
206248
gql`
207249
mutation deleteRepo($id: ID!) {
208250
deleteRepo(id: $id)
209251
}
210252
`,
211253
{
212-
refetchQueries: ["GetRepos"],
254+
onCompleted() {
255+
client.writeQuery({
256+
query: GET_REPOS,
257+
data: {
258+
myRepos: repos.filter((repo) => repo.id !== clickedRepo?.id),
259+
},
260+
});
261+
enqueueSnackbar("Successfully deleted repo", { variant: "success" });
262+
},
263+
onError() {
264+
enqueueSnackbar("Failed to delete repo", { variant: "error" });
265+
},
213266
}
214267
);
215268
// FIXME once ttl is reached, the runtime is killed, but this query is not
@@ -224,13 +277,14 @@ function RepoList({ repos }) {
224277
`);
225278

226279
const onConfirmDeleteRepo = useCallback(() => {
280+
setConfirmDeleteDialogOpen(false);
227281
deleteRepo({
228282
variables: {
229283
id: clickedRepo?.id,
230284
},
231-
});
232-
setClickedRepo(undefined);
233-
}, [clickedRepo, deleteRepo]);
285+
}).then(() => setClickedRepo(undefined));
286+
}, [clickedRepo?.id, deleteRepo]);
287+
234288
return (
235289
<>
236290
<TableContainer component={Paper}>
@@ -249,6 +303,9 @@ function RepoList({ repos }) {
249303
<RepoLine
250304
repo={repo}
251305
deletable={true}
306+
deleting={
307+
repo.id === clickedRepo?.id && deleteRepoResult.loading
308+
}
252309
sharable={true}
253310
runtimeInfo={
254311
loading
@@ -258,34 +315,30 @@ function RepoList({ repos }) {
258315
)
259316
}
260317
key={repo.id}
261-
onDeleteRepo={setClickedRepo}
318+
onDeleteRepo={(repo) => {
319+
setClickedRepo(repo);
320+
setConfirmDeleteDialogOpen(true);
321+
}}
262322
/>
263323
))}
264324
</TableBody>
265325
</Table>
266326
</TableContainer>
267327
<ConfirmDeleteDialog
268328
repoName={clickedRepo?.name}
269-
open={Boolean(clickedRepo)}
270-
handleCancel={() => setClickedRepo(undefined)}
329+
open={isConfirmDeleteDialogOpen}
330+
handleCancel={() => {
331+
setClickedRepo(undefined);
332+
setConfirmDeleteDialogOpen(false);
333+
}}
271334
handleConfirm={onConfirmDeleteRepo}
272335
/>
273336
</>
274337
);
275338
}
276339

277340
function MyRepos() {
278-
const { loading, error, data } = useQuery(gql`
279-
query GetRepos {
280-
myRepos {
281-
name
282-
id
283-
public
284-
updatedAt
285-
createdAt
286-
}
287-
}
288-
`);
341+
const { loading, error, data } = useQuery(GET_REPOS);
289342

290343
if (loading) {
291344
return <CircularProgress />;

0 commit comments

Comments
 (0)