feat(gui): add cancel to API client
This commit is contained in:
parent
294c831d02
commit
900a95eb61
|
@ -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;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>);
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
|
@ -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) => ({
|
||||||
|
|
Loading…
Reference in New Issue