Skip to content

Commit e7bd049

Browse files
fix: Optimistically update the UI when a workspace action is triggered (#4929)
1 parent bf4a6fb commit e7bd049

File tree

6 files changed

+45
-24
lines changed

6 files changed

+45
-24
lines changed

site/src/components/DropdownButton/ActionCtas.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,6 @@ const useStyles = makeStyles((theme) => ({
160160
// this is all custom to work with our button wrapper
161161
loadingButton: {
162162
border: "none",
163-
borderLeft: "1px solid #333740", // MUI disabled button
164-
borderRadius: "3px 0px 0px 3px",
163+
borderRadius: `${theme.shape.borderRadius} 0px 0px ${theme.shape.borderRadius}`,
165164
},
166165
}))

site/src/components/DropdownButton/DropdownButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ const useStyles = makeStyles((theme) => ({
9595
borderLeft: `1px solid ${theme.palette.divider}`,
9696
borderRadius: `0px ${theme.shape.borderRadius}px ${theme.shape.borderRadius}px 0px`,
9797
minWidth: "unset",
98-
width: "63px", // matching cancel button so button grouping doesn't grow in size
98+
width: "64px", // matching cancel button so button grouping doesn't grow in size
9999
"& .MuiButton-label": {
100100
marginRight: "8px",
101101
},

site/src/components/LoadingButton/LoadingButton.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export const LoadingButton: FC<React.PropsWithChildren<LoadingButtonProps>> = ({
3131
<span style={hidden}>{children}</span>
3232
{loading && (
3333
<div className={styles.loader}>
34-
<CircularProgress size={18} className={styles.spinner} />
34+
<CircularProgress size={16} className={styles.spinner} />
3535
</div>
3636
)}
3737
{Boolean(loadingLabel) && loadingLabel}
@@ -63,7 +63,7 @@ const useStyles = makeStyles<Theme, StyleProps>((theme) => ({
6363
top: "50%",
6464
left: "50%",
6565
height: 22, // centering loading icon
66-
width: 18,
66+
width: 16,
6767
},
6868
spinner: {
6969
color: theme.palette.text.disabled,

site/src/components/WorkspaceActions/WorkspaceActions.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export const WorkspaceActions: FC<WorkspaceActionsProps> = ({
6464
<DisabledButton label={t("disabledButton.deleted")} />
6565
),
6666
[ButtonTypesEnum.pending]: (
67-
<DisabledButton label={t("disabledButton.pending")} />
67+
<ActionLoadingButton label={t("disabledButton.pending")} />
6868
),
6969
}
7070

site/src/pages/WorkspacePage/WorkspacePage.test.tsx

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -92,16 +92,6 @@ afterAll(() => {
9292
})
9393

9494
describe("WorkspacePage", () => {
95-
it("requests a stop job when the user presses Stop", async () => {
96-
const stopWorkspaceMock = jest
97-
.spyOn(api, "stopWorkspace")
98-
.mockResolvedValueOnce(MockWorkspaceBuild)
99-
testButton(
100-
t("actionButton.stop", { ns: "workspacePage" }),
101-
stopWorkspaceMock,
102-
)
103-
})
104-
10595
it("requests a delete job when the user presses Delete and confirms", async () => {
10696
const user = userEvent.setup()
10797
const deleteWorkspaceMock = jest
@@ -140,11 +130,23 @@ describe("WorkspacePage", () => {
140130
const startWorkspaceMock = jest
141131
.spyOn(api, "startWorkspace")
142132
.mockImplementation(() => Promise.resolve(MockWorkspaceBuild))
143-
testButton(
133+
await testButton(
144134
t("actionButton.start", { ns: "workspacePage" }),
145135
startWorkspaceMock,
146136
)
147137
})
138+
139+
it("requests a stop job when the user presses Stop", async () => {
140+
const stopWorkspaceMock = jest
141+
.spyOn(api, "stopWorkspace")
142+
.mockResolvedValueOnce(MockWorkspaceBuild)
143+
144+
await testButton(
145+
t("actionButton.stop", { ns: "workspacePage" }),
146+
stopWorkspaceMock,
147+
)
148+
})
149+
148150
it("requests cancellation when the user presses Cancel", async () => {
149151
server.use(
150152
rest.get(

site/src/xServices/workspace/workspaceXService.ts

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ export const workspaceMachine = createMachine(
325325
},
326326
},
327327
requestingStart: {
328-
entry: "clearBuildError",
328+
entry: ["clearBuildError", "updateStatusToPending"],
329329
invoke: {
330330
src: "startWorkspace",
331331
id: "startWorkspace",
@@ -344,7 +344,7 @@ export const workspaceMachine = createMachine(
344344
},
345345
},
346346
requestingStop: {
347-
entry: "clearBuildError",
347+
entry: ["clearBuildError", "updateStatusToPending"],
348348
invoke: {
349349
src: "stopWorkspace",
350350
id: "stopWorkspace",
@@ -363,7 +363,7 @@ export const workspaceMachine = createMachine(
363363
},
364364
},
365365
requestingDelete: {
366-
entry: "clearBuildError",
366+
entry: ["clearBuildError", "updateStatusToPending"],
367367
invoke: {
368368
src: "deleteWorkspace",
369369
id: "deleteWorkspace",
@@ -382,7 +382,11 @@ export const workspaceMachine = createMachine(
382382
},
383383
},
384384
requestingCancel: {
385-
entry: ["clearCancellationMessage", "clearCancellationError"],
385+
entry: [
386+
"clearCancellationMessage",
387+
"clearCancellationError",
388+
"updateStatusToPending",
389+
],
386390
invoke: {
387391
src: "cancelWorkspace",
388392
id: "cancelWorkspace",
@@ -430,9 +434,7 @@ export const workspaceMachine = createMachine(
430434
on: {
431435
REFRESH_TIMELINE: {
432436
target: "#workspaceState.ready.timeline.gettingBuilds",
433-
cond: {
434-
type: "moreBuildsAvailable",
435-
},
437+
cond: "moreBuildsAvailable",
436438
},
437439
},
438440
},
@@ -599,6 +601,24 @@ export const workspaceMachine = createMachine(
599601
}),
600602
{ to: "scheduleBannerMachine" },
601603
),
604+
// Optimistically update. So when the user clicks on stop, we can show
605+
// the "pending" state right away without having to wait 0.5s ~ 2s to
606+
// display the visual feedback to the user.
607+
updateStatusToPending: assign({
608+
workspace: ({ workspace }) => {
609+
if (!workspace) {
610+
throw new Error("Workspace not defined")
611+
}
612+
613+
return {
614+
...workspace,
615+
latest_build: {
616+
...workspace.latest_build,
617+
status: "pending" as TypesGen.WorkspaceStatus,
618+
},
619+
}
620+
},
621+
}),
602622
},
603623
guards: {
604624
moreBuildsAvailable,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy