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.
*/
export interface ReadyResponse {
progress: number;
ready: boolean;
}
@ -213,6 +214,8 @@ export interface ApiClient {
* Check whether some pipeline's output is ready yet.
*/
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);
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 = [];
if (doesExist(loading)) {
children.push(<LoadingCard key='loading' loading={loading} />);
if (loading.length > 0) {
children.push(...loading.map((item) => <LoadingCard key={`loading-${item.image.output.key}`} loading={item.image} />));
}
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 {
if (doesExist(loading) === false) {
children.push(<Typography>No results. Press Generate.</Typography>);

View File

@ -1,8 +1,8 @@
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 { useContext } from 'react';
import { useQuery } from 'react-query';
import { useMutation, useQuery } from 'react-query';
import { useStore } from 'zustand';
import { ImageResponse } from '../client.js';
@ -17,15 +17,34 @@ export function LoadingCard(props: LoadingCardProps) {
const client = mustExist(React.useContext(ClientContext));
const { params } = mustExist(useContext(ConfigContext));
const state = mustExist(useContext(StateContext));
// 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), {
// data will always be ready without this, even if the API says its not
cacheTime: 0,
refetchInterval: POLL_TIME,
});
function progress() {
if (doesExist(query.data)) {
return Math.ceil(query.data.progress / props.loading.params.steps);
}
return 0;
}
function ready() {
return doesExist(query.data) && query.data.ready;
}
@ -44,7 +63,8 @@ export function LoadingCard(props: LoadingCardProps) {
justifyContent: 'center',
minHeight: params.height.default,
}}>
<CircularProgress />
<CircularProgress value={progress()} />
<Button>Cancel</Button>
</div>
</CardContent>
</Card>;

View File

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