feat(gui): add cancel button to loading card
This commit is contained in:
parent
87bbce4fff
commit
f5039d800d
|
@ -503,7 +503,9 @@ export function makeClient(root: string, f = fetch): ApiClient {
|
||||||
const path = makeApiUrl(root, 'cancel');
|
const path = makeApiUrl(root, 'cancel');
|
||||||
path.searchParams.append('output', params.output.key);
|
path.searchParams.append('output', params.output.key);
|
||||||
|
|
||||||
const res = await f(path);
|
const res = await f(path, {
|
||||||
|
method: 'PUT',
|
||||||
|
});
|
||||||
return res.status === STATUS_SUCCESS;
|
return res.status === STATUS_SUCCESS;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { doesExist, mustExist } from '@apextoaster/js-utils';
|
import { doesExist, mustExist } from '@apextoaster/js-utils';
|
||||||
import { Button, Card, CardContent, CircularProgress } from '@mui/material';
|
import { Box, Button, Card, CardContent, CircularProgress, Typography } from '@mui/material';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useContext } from 'react';
|
import { useContext, useEffect } from 'react';
|
||||||
import { useMutation, useQuery } from 'react-query';
|
import { useMutation, useQuery } from 'react-query';
|
||||||
import { useStore } from 'zustand';
|
import { useStore } from 'zustand';
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@ import { ImageResponse } from '../client.js';
|
||||||
import { POLL_TIME } from '../config.js';
|
import { POLL_TIME } from '../config.js';
|
||||||
import { ClientContext, ConfigContext, StateContext } from '../state.js';
|
import { ClientContext, ConfigContext, StateContext } from '../state.js';
|
||||||
|
|
||||||
|
const LOADING_PERCENT = 100;
|
||||||
|
|
||||||
export interface LoadingCardProps {
|
export interface LoadingCardProps {
|
||||||
loading: ImageResponse;
|
loading: ImageResponse;
|
||||||
}
|
}
|
||||||
|
@ -22,50 +24,61 @@ export function LoadingCard(props: LoadingCardProps) {
|
||||||
const clearLoading = useStore(state, (s) => s.clearLoading);
|
const clearLoading = useStore(state, (s) => s.clearLoading);
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
const pushHistory = useStore(state, (s) => s.pushHistory);
|
const pushHistory = useStore(state, (s) => s.pushHistory);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
|
const setReady = useStore(state, (s) => s.setReady);
|
||||||
|
|
||||||
async function doCancel() {
|
const cancel = useMutation(() => client.cancel(props.loading));
|
||||||
const cancelled = await client.cancel(props.loading);
|
const ready = useQuery('ready', () => client.ready(props.loading), {
|
||||||
if (cancelled) {
|
|
||||||
clearLoading();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const cancel = useMutation(doCancel);
|
|
||||||
const query = useQuery('ready', () => client.ready(props.loading), {
|
|
||||||
// data will always be ready without this, even if the API says its not
|
// data will always be ready without this, even if the API says its not
|
||||||
cacheTime: 0,
|
cacheTime: 0,
|
||||||
refetchInterval: POLL_TIME,
|
refetchInterval: POLL_TIME,
|
||||||
});
|
});
|
||||||
|
|
||||||
function progress() {
|
function getProgress() {
|
||||||
if (doesExist(query.data)) {
|
if (doesExist(ready.data)) {
|
||||||
return Math.ceil(query.data.progress / props.loading.params.steps);
|
return ready.data.progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ready() {
|
function getPercent() {
|
||||||
return doesExist(query.data) && query.data.ready;
|
const pct = getProgress() / props.loading.params.steps;
|
||||||
|
return Math.ceil(pct * LOADING_PERCENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
React.useEffect(() => {
|
function getReady() {
|
||||||
if (query.status === 'success' && query.data.ready) {
|
return doesExist(ready.data) && ready.data.ready;
|
||||||
pushHistory(props.loading);
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (cancel.status === 'success') {
|
||||||
|
clearLoading();
|
||||||
}
|
}
|
||||||
}, [query.status, ready()]);
|
}, [cancel.status]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (ready.status === 'success') {
|
||||||
|
if (ready.data.ready) {
|
||||||
|
pushHistory(props.loading);
|
||||||
|
} else {
|
||||||
|
setReady(props.loading, ready.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [ready.status, getReady(), getProgress()]);
|
||||||
|
|
||||||
return <Card sx={{ maxWidth: params.width.default }}>
|
return <Card sx={{ maxWidth: params.width.default }}>
|
||||||
<CardContent sx={{ height: params.height.default }}>
|
<CardContent sx={{ height: params.height.default }}>
|
||||||
<div style={{
|
<Box sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
minHeight: params.height.default,
|
minHeight: params.height.default,
|
||||||
}}>
|
}}>
|
||||||
<CircularProgress value={progress()} />
|
<CircularProgress variant='determinate' value={getPercent()} />
|
||||||
<Button>Cancel</Button>
|
<Typography>{getProgress()} of {props.loading.params.steps}</Typography>
|
||||||
</div>
|
<Button onClick={() => cancel.mutate()}>Cancel</Button>
|
||||||
|
</Box>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>;
|
</Card>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ export function Img2Img() {
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
const setImg2Img = useStore(state, (s) => s.setImg2Img);
|
const setImg2Img = useStore(state, (s) => s.setImg2Img);
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
const setLoading = useStore(state, (s) => s.setLoading);
|
const setLoading = useStore(state, (s) => s.pushLoading);
|
||||||
|
|
||||||
return <Box>
|
return <Box>
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
|
|
|
@ -62,7 +62,7 @@ export function Inpaint() {
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
const setInpaint = useStore(state, (s) => s.setInpaint);
|
const setInpaint = useStore(state, (s) => s.setInpaint);
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
const setLoading = useStore(state, (s) => s.setLoading);
|
const setLoading = useStore(state, (s) => s.pushLoading);
|
||||||
|
|
||||||
const query = useQueryClient();
|
const query = useQueryClient();
|
||||||
const upload = useMutation(uploadSource, {
|
const upload = useMutation(uploadSource, {
|
||||||
|
|
|
@ -32,7 +32,7 @@ export function Txt2Img() {
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
const setTxt2Img = useStore(state, (s) => s.setTxt2Img);
|
const setTxt2Img = useStore(state, (s) => s.setTxt2Img);
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
const setLoading = useStore(state, (s) => s.setLoading);
|
const setLoading = useStore(state, (s) => s.pushLoading);
|
||||||
|
|
||||||
return <Box>
|
return <Box>
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
|
|
|
@ -33,7 +33,7 @@ export function Upscale() {
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
const setSource = useStore(state, (s) => s.setUpscaleTab);
|
const setSource = useStore(state, (s) => s.setUpscaleTab);
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
const setLoading = useStore(state, (s) => s.setLoading);
|
const setLoading = useStore(state, (s) => s.pushLoading);
|
||||||
|
|
||||||
return <Box>
|
return <Box>
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
|
|
|
@ -49,9 +49,10 @@ interface HistorySlice {
|
||||||
// TODO: hack until setLoading removes things
|
// TODO: hack until setLoading removes things
|
||||||
clearLoading(): void;
|
clearLoading(): void;
|
||||||
pushHistory(image: ImageResponse): void;
|
pushHistory(image: ImageResponse): void;
|
||||||
|
pushLoading(image: ImageResponse): void;
|
||||||
removeHistory(image: ImageResponse): void;
|
removeHistory(image: ImageResponse): void;
|
||||||
setLimit(limit: number): void;
|
setLimit(limit: number): void;
|
||||||
setLoading(image: ImageResponse, ready?: Maybe<ReadyResponse>): void;
|
setReady(image: ImageResponse, ready: ReadyResponse): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ModelSlice {
|
interface ModelSlice {
|
||||||
|
@ -135,7 +136,7 @@ export const StateContext = createContext<Maybe<StoreApi<OnnxState>>>(undefined)
|
||||||
/**
|
/**
|
||||||
* Current state version for zustand persistence.
|
* Current state version for zustand persistence.
|
||||||
*/
|
*/
|
||||||
export const STATE_VERSION = 4;
|
export const STATE_VERSION = 5;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default parameters for the inpaint brush.
|
* Default parameters for the inpaint brush.
|
||||||
|
@ -289,21 +290,20 @@ export function createStateSlices(server: ServerParams) {
|
||||||
loading: [],
|
loading: [],
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
setLoading(image, ready) {
|
pushLoading(image) {
|
||||||
set((prev) => {
|
set((prev) => ({
|
||||||
const loading = [...prev.loading];
|
...prev,
|
||||||
const idx = loading.findIndex((it) => it.image.output.key === image.output.key);
|
loading: [
|
||||||
if (idx >= 0) {
|
{
|
||||||
loading[idx].ready = ready;
|
image,
|
||||||
} else {
|
ready: {
|
||||||
loading.push({ image, ready });
|
progress: 0,
|
||||||
}
|
ready: false,
|
||||||
|
},
|
||||||
return {
|
},
|
||||||
...prev,
|
...prev.loading,
|
||||||
loading,
|
],
|
||||||
};
|
}));
|
||||||
});
|
|
||||||
},
|
},
|
||||||
removeHistory(image) {
|
removeHistory(image) {
|
||||||
set((prev) => ({
|
set((prev) => ({
|
||||||
|
@ -317,6 +317,22 @@ export function createStateSlices(server: ServerParams) {
|
||||||
limit,
|
limit,
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
setReady(image, ready) {
|
||||||
|
set((prev) => {
|
||||||
|
const loading = [...prev.loading];
|
||||||
|
const idx = loading.findIndex((it) => it.image.output.key === image.output.key);
|
||||||
|
if (idx >= 0) {
|
||||||
|
loading[idx].ready = ready;
|
||||||
|
} else {
|
||||||
|
// TODO: error
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
loading,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const createOutpaintSlice: Slice<OutpaintSlice> = (set) => ({
|
const createOutpaintSlice: Slice<OutpaintSlice> = (set) => ({
|
||||||
|
|
Loading…
Reference in New Issue