Skip to content

feat(ui): (optional) Protect starred images from accidental deletion #8406

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions invokeai/frontend/web/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
"selectBoard": "Select a Board",
"shared": "Shared Boards",
"topMessage": "This selection contains images used in the following features:",
"containsStarredTitle": "Warning: Starred images",
"containsStarredConfirm": "This selection contains starred images. Delete anyway? This cannot be undone.",
"unarchiveBoard": "Unarchive Board",
"uncategorized": "Uncategorized",
"viewBoards": "View Boards",
Expand Down Expand Up @@ -354,6 +356,7 @@
"boardsSettings": "Boards Settings",
"copy": "Copy",
"currentlyInUse": "This image is currently in use in the following features:",
"cannotDeleteStarred": "Starred images are protected — remove the star or disable protection.",
"drop": "Drop",
"dropOrUpload": "Drop or Upload",
"dropToUpload": "$t(gallery.drop) to Upload",
Expand Down Expand Up @@ -1335,6 +1338,8 @@
"antialiasProgressImages": "Antialias Progress Images",
"beta": "Beta",
"confirmOnDelete": "Confirm On Delete",
"protectStarredImages": "Protect starred images",
"protectStarredImagesDesc1": "In case the image have the Star mark, this option will protect it from accidental deletion",
"confirmOnNewSession": "Confirm On New Session",
"developer": "Developer",
"displayInProgress": "Display Progress Images",
Expand Down
5 changes: 5 additions & 0 deletions invokeai/frontend/web/public/locales/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
"bulkDownloadRequested": "Подготовка к скачиванию",
"bulkDownloadRequestedDesc": "Ваш запрос на скачивание готовится. Это может занять несколько минут.",
"bulkDownloadRequestFailed": "Возникла проблема при подготовке скачивания",
"cannotDeleteStarred": "Изображения, отмеченные звездочкой, защищены от удаления. Снимите отметку чтобы удалить.",
"alwaysShowImageSizeBadge": "Всегда показывать значок размера изображения",
"openInViewer": "Открыть в просмотрщике",
"selectForCompare": "Выбрать для сравнения",
Expand Down Expand Up @@ -690,6 +691,8 @@
"models": "Модели",
"displayInProgress": "Показывать процесс генерации",
"confirmOnDelete": "Подтверждать удаление",
"protectStarredImages": "Защитить помеченные изображения",
"protectStarredImagesDesc1": "Если изображение помечено звездочкой - эта опция защищает его от случайного удаления",
"resetWebUI": "Сброс настроек веб-интерфейса",
"resetWebUIDesc1": "Сброс настроек веб-интерфейса удаляет только локальный кэш браузера с вашими изображениями и настройками. Он не удаляет изображения с диска.",
"resetWebUIDesc2": "Если изображения не отображаются в галерее или не работает что-то еще, пожалуйста, попробуйте сбросить настройки, прежде чем сообщать о проблеме на GitHub.",
Expand Down Expand Up @@ -963,6 +966,8 @@
"deleteBoard": "Удалить доску",
"deleteBoardAndImages": "Удалить доску и изображения",
"deletedBoardsCannotbeRestored": "Удаленные доски не могут быть восстановлены. Выбор «Удалить только доску» переведет изображения в состояние без категории.",
"containsStarredTitle": "Внимание: Помеченные изображения",
"containsStarredConfirm": "Эта доска содержит помеченные изображения. Вы уверены, что хотите продолжить? Это действие необратимо.",
"assetsWithCount_one": "{{count}} актив",
"assetsWithCount_few": "{{count}} актива",
"assetsWithCount_many": "{{count}} активов",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,20 @@ import {
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
import type { CanvasState, RefImagesState } from 'features/controlLayers/store/types';
import type { ImageUsage } from 'features/deleteImageModal/store/types';
import { selectGetImageNamesQueryArgs } from 'features/gallery/store/gallerySelectors';
import { selectGetImageNamesQueryArgs, selectImageByName } from 'features/gallery/store/gallerySelectors';
import { imageSelected } from 'features/gallery/store/gallerySlice';
import { fieldImageCollectionValueChanged, fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
import { selectNodesSlice } from 'features/nodes/store/selectors';
import type { NodesState } from 'features/nodes/store/types';
import { isImageFieldCollectionInputInstance, isImageFieldInputInstance } from 'features/nodes/types/field';
import { isInvocationNode } from 'features/nodes/types/invocation';
import { selectUpscaleSlice, type UpscaleState } from 'features/parameters/store/upscaleSlice';
import { selectSystemShouldConfirmOnDelete } from 'features/system/store/systemSlice';
import {
selectSystemShouldConfirmOnDelete,
selectSystemShouldProtectStarredImages,
} from 'features/system/store/systemSlice';
import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { atom } from 'nanostores';
import { useMemo } from 'react';
import { imagesApi } from 'services/api/endpoints/images';
Expand Down Expand Up @@ -57,6 +62,26 @@ const deleteImagesWithDialog = async (image_names: string[], store: AppStore): P
const { getState } = store;
const imageUsage = getImageUsageFromImageNames(image_names, getState());
const shouldConfirmOnDelete = selectSystemShouldConfirmOnDelete(getState());
const shouldProtectStarred = selectSystemShouldProtectStarredImages(getState());

if (shouldProtectStarred) {
// find which of the incoming names are starred
const starred = image_names.filter((name) => selectImageByName(getState(), name)?.starred);
if (starred.length) {
if (selectSystemShouldConfirmOnDelete(getState())) {
// show toast explaining why we refuse only if we are not in "silent mode"
toast({
status: 'warning',
title: t('gallery.cannotDeleteStarred'),
});
}

image_names = image_names.filter((n) => !starred.includes(n));
if (!image_names.length) {
return;
}
}
}

if (!shouldConfirmOnDelete && !isAnyImageInUse(imageUsage)) {
// If we don't need to confirm and the images are not in use, delete them directly
Expand Down
Loading