mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-01-15 08:28:14 -05:00
Compare commits
3 Commits
v5.4.2
...
maryhipp/b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c8a5c0788 | ||
|
|
548750534b | ||
|
|
918470e454 |
@@ -1171,7 +1171,7 @@
|
||||
"setNodeField": "Set as node field",
|
||||
"somethingWentWrong": "Something Went Wrong",
|
||||
"uploadFailed": "Upload failed",
|
||||
"uploadFailedInvalidUploadDesc": "Must be single PNG or JPEG image",
|
||||
"uploadFailedInvalidUploadDesc": "Must be PNG or JPEG images",
|
||||
"workflowLoaded": "Workflow Loaded",
|
||||
"problemRetrievingWorkflow": "Problem Retrieving Workflow",
|
||||
"workflowDeleted": "Workflow Deleted",
|
||||
|
||||
@@ -47,26 +47,32 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
|
||||
status: 'success',
|
||||
} as const;
|
||||
|
||||
const BATCH_UPLOADED_TOAST = {
|
||||
id: 'BATCH_UPLOADED',
|
||||
title: t('toast.imageUploaded'),
|
||||
status: 'info',
|
||||
withCount: true,
|
||||
duration: null
|
||||
} as const;
|
||||
|
||||
// default action - just upload and alert user
|
||||
if (postUploadAction?.type === 'TOAST') {
|
||||
if (!autoAddBoardId || autoAddBoardId === 'none') {
|
||||
const title = postUploadAction.title || DEFAULT_UPLOADED_TOAST.title;
|
||||
toast({ ...DEFAULT_UPLOADED_TOAST, title });
|
||||
if (postUploadAction.batchCount) {
|
||||
toast({ ...BATCH_UPLOADED_TOAST, title });
|
||||
} else {
|
||||
toast({ ...DEFAULT_UPLOADED_TOAST, title });
|
||||
}
|
||||
|
||||
dispatch(boardIdSelected({ boardId: 'none' }));
|
||||
dispatch(galleryViewChanged('assets'));
|
||||
} else {
|
||||
// Add this image to the board
|
||||
dispatch(
|
||||
imagesApi.endpoints.addImageToBoard.initiate({
|
||||
board_id: autoAddBoardId,
|
||||
imageDTO,
|
||||
})
|
||||
);
|
||||
|
||||
// Attempt to get the board's name for the toast
|
||||
const queryArgs = selectListBoardsQueryArgs(state);
|
||||
const { data } = boardsApi.endpoints.listAllBoards.select(queryArgs)(state);
|
||||
|
||||
const title = postUploadAction.title || DEFAULT_UPLOADED_TOAST.title;
|
||||
// Fall back to just the board id if we can't find the board for some reason
|
||||
const board = data?.find((b) => b.board_id === autoAddBoardId);
|
||||
const description = board
|
||||
@@ -75,6 +81,7 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
|
||||
|
||||
toast({
|
||||
...DEFAULT_UPLOADED_TOAST,
|
||||
title,
|
||||
description,
|
||||
});
|
||||
dispatch(boardIdSelected({ boardId: autoAddBoardId }));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { Text } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectAutoAddBoardId } from 'features/gallery/store/gallerySelectors';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
@@ -9,28 +9,31 @@ import { useDropzone } from 'react-dropzone';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useUploadImageMutation } from 'services/api/endpoints/images';
|
||||
import type { PostUploadAction } from 'services/api/types';
|
||||
import { batchIndexIncremented, uploadingBatchChanged } from '../../features/gallery/store/gallerySlice';
|
||||
|
||||
const accept: Accept = {
|
||||
'image/png': ['.png'],
|
||||
'image/jpeg': ['.jpg', '.jpeg', '.png'],
|
||||
};
|
||||
|
||||
const selectPostUploadAction = createMemoizedSelector(selectActiveTab, (activeTabName) => {
|
||||
let postUploadAction: PostUploadAction = { type: 'TOAST' };
|
||||
|
||||
if (activeTabName === 'upscaling') {
|
||||
postUploadAction = { type: 'SET_UPSCALE_INITIAL_IMAGE' };
|
||||
}
|
||||
|
||||
return postUploadAction;
|
||||
});
|
||||
|
||||
export const useFullscreenDropzone = () => {
|
||||
const { t } = useTranslation();
|
||||
const autoAddBoardId = useAppSelector(selectAutoAddBoardId);
|
||||
const [isHandlingUpload, setIsHandlingUpload] = useState<boolean>(false);
|
||||
const postUploadAction = useAppSelector(selectPostUploadAction);
|
||||
const [uploadImage] = useUploadImageMutation();
|
||||
const activeTabName = useAppSelector(selectActiveTab);
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const getPostUploadAction = useCallback(
|
||||
(singleImage: boolean): PostUploadAction | undefined => {
|
||||
if (singleImage && activeTabName === 'upscaling') {
|
||||
return { type: 'SET_UPSCALE_INITIAL_IMAGE' };
|
||||
} else if (singleImage) {
|
||||
return { type: 'TOAST' };
|
||||
}
|
||||
},
|
||||
[activeTabName]
|
||||
);
|
||||
|
||||
const fileRejectionCallback = useCallback(
|
||||
(rejection: FileRejection) => {
|
||||
@@ -47,7 +50,7 @@ export const useFullscreenDropzone = () => {
|
||||
);
|
||||
|
||||
const fileAcceptedCallback = useCallback(
|
||||
(file: File) => {
|
||||
(file: File, postUploadAction: PostUploadAction | undefined) => {
|
||||
uploadImage({
|
||||
file,
|
||||
image_category: 'user',
|
||||
@@ -56,7 +59,7 @@ export const useFullscreenDropzone = () => {
|
||||
board_id: autoAddBoardId === 'none' ? undefined : autoAddBoardId,
|
||||
});
|
||||
},
|
||||
[autoAddBoardId, postUploadAction, uploadImage]
|
||||
[autoAddBoardId, uploadImage]
|
||||
);
|
||||
|
||||
const onDrop = useCallback(
|
||||
@@ -75,11 +78,41 @@ export const useFullscreenDropzone = () => {
|
||||
fileRejectionCallback(rejection);
|
||||
});
|
||||
|
||||
acceptedFiles.forEach((file: File) => {
|
||||
fileAcceptedCallback(file);
|
||||
const postUploadAction = getPostUploadAction(acceptedFiles.length === 1);
|
||||
|
||||
if (acceptedFiles.length === 1) {
|
||||
acceptedFiles.forEach((file: File) => {
|
||||
fileAcceptedCallback(file, postUploadAction);
|
||||
});
|
||||
} else {
|
||||
const batchTotal = acceptedFiles.length
|
||||
let index = 0
|
||||
// dispatch(uploadingBatchChanged({uploadingBatch: true, batchTotal: acceptedFiles.length}))
|
||||
|
||||
|
||||
toast({
|
||||
id: 'BATCH_UPLOADING',
|
||||
title: "Batch uploading",
|
||||
status: 'info',
|
||||
updateDescription: true,
|
||||
description: `Uploading ${index} or ${batchTotal}`,
|
||||
duration: null
|
||||
});
|
||||
|
||||
acceptedFiles.forEach((file: File) => {
|
||||
fileAcceptedCallback(file, undefined);
|
||||
toast({
|
||||
id: 'BATCH_UPLOADING',
|
||||
title: "Batch uploading",
|
||||
status: 'info',
|
||||
updateDescription: true,
|
||||
description: `Uploading ${index} of ${batchTotal}`,
|
||||
duration: null
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
[t, fileAcceptedCallback, fileRejectionCallback]
|
||||
[t, fileAcceptedCallback, fileRejectionCallback, getPostUploadAction]
|
||||
);
|
||||
|
||||
const onDragOver = useCallback(() => {
|
||||
@@ -91,7 +124,6 @@ export const useFullscreenDropzone = () => {
|
||||
noClick: true,
|
||||
onDrop,
|
||||
onDragOver,
|
||||
multiple: false,
|
||||
noKeyboard: true,
|
||||
});
|
||||
|
||||
@@ -120,3 +152,5 @@ export const useFullscreenDropzone = () => {
|
||||
|
||||
return { dropzone, isHandlingUpload, setIsHandlingUpload };
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import type { PostUploadAction } from 'services/api/types';
|
||||
type UseImageUploadButtonArgs = {
|
||||
postUploadAction?: PostUploadAction;
|
||||
isDisabled?: boolean;
|
||||
allowMultiple?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -29,24 +30,20 @@ type UseImageUploadButtonArgs = {
|
||||
* <Button {...getUploadButtonProps()} /> // will open the file dialog on click
|
||||
* <input {...getUploadInputProps()} /> // hidden, handles native upload functionality
|
||||
*/
|
||||
export const useImageUploadButton = ({ postUploadAction, isDisabled }: UseImageUploadButtonArgs) => {
|
||||
export const useImageUploadButton = ({ postUploadAction, isDisabled, allowMultiple }: UseImageUploadButtonArgs) => {
|
||||
const autoAddBoardId = useAppSelector(selectAutoAddBoardId);
|
||||
const [uploadImage] = useUploadImageMutation();
|
||||
const onDropAccepted = useCallback(
|
||||
(files: File[]) => {
|
||||
const file = files[0];
|
||||
|
||||
if (!file) {
|
||||
return;
|
||||
for (const file of files) {
|
||||
uploadImage({
|
||||
file,
|
||||
image_category: 'user',
|
||||
is_intermediate: false,
|
||||
postUploadAction: postUploadAction ?? { type: 'TOAST' },
|
||||
board_id: autoAddBoardId === 'none' ? undefined : autoAddBoardId,
|
||||
});
|
||||
}
|
||||
|
||||
uploadImage({
|
||||
file,
|
||||
image_category: 'user',
|
||||
is_intermediate: false,
|
||||
postUploadAction: postUploadAction ?? { type: 'TOAST' },
|
||||
board_id: autoAddBoardId === 'none' ? undefined : autoAddBoardId,
|
||||
});
|
||||
},
|
||||
[autoAddBoardId, postUploadAction, uploadImage]
|
||||
);
|
||||
@@ -60,7 +57,7 @@ export const useImageUploadButton = ({ postUploadAction, isDisabled }: UseImageU
|
||||
onDropAccepted,
|
||||
disabled: isDisabled,
|
||||
noDrag: true,
|
||||
multiple: false,
|
||||
multiple: allowMultiple || false,
|
||||
});
|
||||
|
||||
return { getUploadButtonProps, getUploadInputProps, openUploader };
|
||||
|
||||
@@ -24,6 +24,7 @@ import { PiMagnifyingGlassBold } from 'react-icons/pi';
|
||||
import { useBoardName } from 'services/api/hooks/useBoardName';
|
||||
|
||||
import GallerySettingsPopover from './GallerySettingsPopover/GallerySettingsPopover';
|
||||
import { GalleryUploadButton } from './GalleryUploadButton';
|
||||
import GalleryImageGrid from './ImageGrid/GalleryImageGrid';
|
||||
import { GalleryPagination } from './ImageGrid/GalleryPagination';
|
||||
import { GallerySearch } from './ImageGrid/GallerySearch';
|
||||
@@ -87,6 +88,7 @@ export const Gallery = () => {
|
||||
</Tab>
|
||||
</Tooltip>
|
||||
<Flex h="full" justifyContent="flex-end">
|
||||
<GalleryUploadButton />
|
||||
<GallerySettingsPopover />
|
||||
<IconButton
|
||||
size="sm"
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { IconButton } from '@invoke-ai/ui-library';
|
||||
import { useImageUploadButton } from 'common/hooks/useImageUploadButton';
|
||||
import { t } from 'i18next';
|
||||
import { PiUploadBold } from 'react-icons/pi';
|
||||
|
||||
export const GalleryUploadButton = () => {
|
||||
const uploadApi = useImageUploadButton({ postUploadAction: { type: 'TOAST' }, allowMultiple: true });
|
||||
return (
|
||||
<>
|
||||
<IconButton
|
||||
size="sm"
|
||||
alignSelf="stretch"
|
||||
variant="link"
|
||||
aria-label={t('accessibility.uploadImage')}
|
||||
tooltip={t('accessibility.uploadImage')}
|
||||
icon={<PiUploadBold />}
|
||||
{...uploadApi.getUploadButtonProps()}
|
||||
/>
|
||||
<input {...uploadApi.getUploadInputProps()} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -27,6 +27,7 @@ const initialGalleryState: GalleryState = {
|
||||
shouldShowArchivedBoards: false,
|
||||
boardsListOrderBy: 'created_at',
|
||||
boardsListOrderDir: 'DESC',
|
||||
uploadingBatch: false,
|
||||
};
|
||||
|
||||
export const gallerySlice = createSlice({
|
||||
@@ -169,6 +170,14 @@ export const gallerySlice = createSlice({
|
||||
boardsListOrderDirChanged: (state, action: PayloadAction<OrderDir>) => {
|
||||
state.boardsListOrderDir = action.payload;
|
||||
},
|
||||
uploadingBatchChanged: (state, action: PayloadAction<{uploadingBatch: boolean; batchTotal?: number}>) => {
|
||||
const {uploadingBatch, batchTotal} = action.payload
|
||||
state.uploadingBatch = uploadingBatch
|
||||
state.batchTotal = batchTotal
|
||||
},
|
||||
batchIndexIncremented: (state ) => {
|
||||
state.batchIndex = (state.batchIndex || 0) + 1
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -196,6 +205,8 @@ export const {
|
||||
searchTermChanged,
|
||||
boardsListOrderByChanged,
|
||||
boardsListOrderDirChanged,
|
||||
uploadingBatchChanged,
|
||||
batchIndexIncremented
|
||||
} = gallerySlice.actions;
|
||||
|
||||
export const selectGallerySlice = (state: RootState) => state.gallery;
|
||||
|
||||
@@ -30,4 +30,7 @@ export type GalleryState = {
|
||||
shouldShowArchivedBoards: boolean;
|
||||
boardsListOrderBy: BoardRecordOrderBy;
|
||||
boardsListOrderDir: OrderDir;
|
||||
uploadingBatch: boolean;
|
||||
batchTotal?: number;
|
||||
batchIndex?: number;
|
||||
};
|
||||
|
||||
@@ -222,6 +222,7 @@ type UpscaleInitialImageAction = {
|
||||
type ToastAction = {
|
||||
type: 'TOAST';
|
||||
title?: string;
|
||||
batchCount?: number;
|
||||
};
|
||||
|
||||
type AddToBatchAction = {
|
||||
|
||||
Reference in New Issue
Block a user