1
0
Fork 0

feat(gui): add cancel to API client

This commit is contained in:
Sean Sube 2023-02-04 10:50:20 -06:00
parent 294c831d02
commit 900a95eb61
Signed by: ssube
GPG Key ID: 3EED7B957D362AF1
4 changed files with 73 additions and 19 deletions

View File

@ -141,6 +141,7 @@ export interface ImageResponse {
* Status response from the ready endpoint. * Status response from the ready endpoint.
*/ */
export interface ReadyResponse { export interface ReadyResponse {
progress: number;
ready: boolean; ready: boolean;
} }
@ -213,6 +214,8 @@ export interface ApiClient {
* Check whether some pipeline's output is ready yet. * Check whether some pipeline's output is ready yet.
*/ */
ready(params: ImageResponse): Promise<ReadyResponse>; ready(params: ImageResponse): Promise<ReadyResponse>;
cancel(params: ImageResponse): Promise<boolean>;
} }
/** /**
@ -495,7 +498,14 @@ export function makeClient(root: string, f = fetch): ApiClient {
const res = await f(path); const res = await f(path);
return await res.json() as ReadyResponse; return await res.json() as ReadyResponse;
} },
async cancel(params: ImageResponse): Promise<boolean> {
const path = makeApiUrl(root, 'cancel');
path.searchParams.append('output', params.output.key);
const res = await f(path);
return res.status === STATUS_SUCCESS;
},
}; };
} }

View File

@ -17,12 +17,12 @@ export function ImageHistory() {
const children = []; const children = [];
if (doesExist(loading)) { if (loading.length > 0) {
children.push(<LoadingCard key='loading' loading={loading} />); children.push(...loading.map((item) => <LoadingCard key={`loading-${item.image.output.key}`} loading={item.image} />));
} }
if (history.length > 0) { if (history.length > 0) {
children.push(...history.map((item) => <ImageCard key={item.output.key} value={item} onDelete={removeHistory} />)); children.push(...history.map((item) => <ImageCard key={`history-${item.output.key}`} value={item} onDelete={removeHistory} />));
} else { } else {
if (doesExist(loading) === false) { if (doesExist(loading) === false) {
children.push(<Typography>No results. Press Generate.</Typography>); children.push(<Typography>No results. Press Generate.</Typography>);

View File

@ -1,8 +1,8 @@
import { doesExist, mustExist } from '@apextoaster/js-utils'; import { doesExist, mustExist } from '@apextoaster/js-utils';
import { Card, CardContent, CircularProgress } from '@mui/material'; import { Button, Card, CardContent, CircularProgress } from '@mui/material';
import * as React from 'react'; import * as React from 'react';
import { useContext } from 'react'; import { useContext } from 'react';
import { useQuery } from 'react-query'; import { useMutation, useQuery } from 'react-query';
import { useStore } from 'zustand'; import { useStore } from 'zustand';
import { ImageResponse } from '../client.js'; import { ImageResponse } from '../client.js';
@ -17,15 +17,34 @@ export function LoadingCard(props: LoadingCardProps) {
const client = mustExist(React.useContext(ClientContext)); const client = mustExist(React.useContext(ClientContext));
const { params } = mustExist(useContext(ConfigContext)); const { params } = mustExist(useContext(ConfigContext));
const state = mustExist(useContext(StateContext));
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const pushHistory = useStore(mustExist(useContext(StateContext)), (state) => state.pushHistory); const clearLoading = useStore(state, (s) => s.clearLoading);
// eslint-disable-next-line @typescript-eslint/unbound-method
const pushHistory = useStore(state, (s) => s.pushHistory);
async function doCancel() {
const cancelled = await client.cancel(props.loading);
if (cancelled) {
clearLoading();
}
}
const cancel = useMutation(doCancel);
const query = useQuery('ready', () => client.ready(props.loading), { 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() {
if (doesExist(query.data)) {
return Math.ceil(query.data.progress / props.loading.params.steps);
}
return 0;
}
function ready() { function ready() {
return doesExist(query.data) && query.data.ready; return doesExist(query.data) && query.data.ready;
} }
@ -44,7 +63,8 @@ export function LoadingCard(props: LoadingCardProps) {
justifyContent: 'center', justifyContent: 'center',
minHeight: params.height.default, minHeight: params.height.default,
}}> }}>
<CircularProgress /> <CircularProgress value={progress()} />
<Button>Cancel</Button>
</div> </div>
</CardContent> </CardContent>
</Card>; </Card>;

View File

@ -1,5 +1,5 @@
/* eslint-disable no-null/no-null */ /* eslint-disable no-null/no-null */
import { Maybe } from '@apextoaster/js-utils'; import { doesExist, Maybe } from '@apextoaster/js-utils';
import { createContext } from 'react'; import { createContext } from 'react';
import { StateCreator, StoreApi } from 'zustand'; import { StateCreator, StoreApi } from 'zustand';
@ -12,6 +12,7 @@ import {
InpaintParams, InpaintParams,
ModelParams, ModelParams,
OutpaintPixels, OutpaintPixels,
ReadyResponse,
Txt2ImgParams, Txt2ImgParams,
UpscaleParams, UpscaleParams,
UpscaleReqParams, UpscaleReqParams,
@ -23,6 +24,11 @@ import { Config, ConfigFiles, ConfigState, ServerParams } from './config.js';
*/ */
type TabState<TabParams> = ConfigFiles<Required<TabParams>> & ConfigState<Required<TabParams>>; type TabState<TabParams> = ConfigFiles<Required<TabParams>> & ConfigState<Required<TabParams>>;
interface LoadingItem {
image: ImageResponse;
ready: Maybe<ReadyResponse>;
}
interface BrushSlice { interface BrushSlice {
brush: BrushParams; brush: BrushParams;
@ -38,12 +44,14 @@ interface DefaultSlice {
interface HistorySlice { interface HistorySlice {
history: Array<ImageResponse>; history: Array<ImageResponse>;
limit: number; limit: number;
loading: Maybe<ImageResponse>; loading: Array<LoadingItem>;
// TODO: hack until setLoading removes things
clearLoading(): void;
pushHistory(image: ImageResponse): void; pushHistory(image: ImageResponse): void;
removeHistory(image: ImageResponse): void; removeHistory(image: ImageResponse): void;
setLimit(limit: number): void; setLimit(limit: number): void;
setLoading(image: Maybe<ImageResponse>): void; setLoading(image: ImageResponse, ready?: Maybe<ReadyResponse>): void;
} }
interface ModelSlice { interface ModelSlice {
@ -264,7 +272,13 @@ export function createStateSlices(server: ServerParams) {
const createHistorySlice: Slice<HistorySlice> = (set) => ({ const createHistorySlice: Slice<HistorySlice> = (set) => ({
history: [], history: [],
limit: DEFAULT_HISTORY.limit, limit: DEFAULT_HISTORY.limit,
loading: null, loading: [],
clearLoading() {
set((prev) => ({
...prev,
loading: [],
}));
},
pushHistory(image) { pushHistory(image) {
set((prev) => ({ set((prev) => ({
...prev, ...prev,
@ -272,9 +286,25 @@ export function createStateSlices(server: ServerParams) {
image, image,
...prev.history, ...prev.history,
].slice(0, prev.limit + DEFAULT_HISTORY.scrollback), ].slice(0, prev.limit + DEFAULT_HISTORY.scrollback),
loading: null, loading: [],
})); }));
}, },
setLoading(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 {
loading.push({ image, ready });
}
return {
...prev,
loading,
};
});
},
removeHistory(image) { removeHistory(image) {
set((prev) => ({ set((prev) => ({
...prev, ...prev,
@ -287,12 +317,6 @@ export function createStateSlices(server: ServerParams) {
limit, limit,
})); }));
}, },
setLoading(loading) {
set((prev) => ({
...prev,
loading,
}));
},
}); });
const createOutpaintSlice: Slice<OutpaintSlice> = (set) => ({ const createOutpaintSlice: Slice<OutpaintSlice> = (set) => ({