mirror of https://github.com/stashapp/stash.git
Support for assigning any image from a gallery as the cover (#5053)
Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
parent
8133aa8c91
commit
68738bd227
|
@ -317,6 +317,8 @@ type Mutation {
|
|||
|
||||
addGalleryImages(input: GalleryAddInput!): Boolean!
|
||||
removeGalleryImages(input: GalleryRemoveInput!): Boolean!
|
||||
setGalleryCover(input: GallerySetCoverInput!): Boolean!
|
||||
resetGalleryCover(input: GalleryResetCoverInput!): Boolean!
|
||||
|
||||
galleryChapterCreate(input: GalleryChapterCreateInput!): GalleryChapter
|
||||
galleryChapterUpdate(input: GalleryChapterUpdateInput!): GalleryChapter
|
||||
|
|
|
@ -115,3 +115,12 @@ input GalleryRemoveInput {
|
|||
gallery_id: ID!
|
||||
image_ids: [ID!]!
|
||||
}
|
||||
|
||||
input GallerySetCoverInput {
|
||||
gallery_id: ID!
|
||||
cover_image_id: ID!
|
||||
}
|
||||
|
||||
input GalleryResetCoverInput {
|
||||
gallery_id: ID!
|
||||
}
|
||||
|
|
|
@ -478,6 +478,61 @@ func (r *mutationResolver) RemoveGalleryImages(ctx context.Context, input Galler
|
|||
return true, nil
|
||||
}
|
||||
|
||||
func (r *mutationResolver) SetGalleryCover(ctx context.Context, input GallerySetCoverInput) (bool, error) {
|
||||
galleryID, err := strconv.Atoi(input.GalleryID)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("converting gallery id: %w", err)
|
||||
}
|
||||
|
||||
coverImageID, err := strconv.Atoi(input.CoverImageID)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("converting cover image id: %w", err)
|
||||
}
|
||||
|
||||
if err := r.withTxn(ctx, func(ctx context.Context) error {
|
||||
qb := r.repository.Gallery
|
||||
gallery, err := qb.Find(ctx, galleryID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if gallery == nil {
|
||||
return fmt.Errorf("gallery with id %d not found", galleryID)
|
||||
}
|
||||
|
||||
return r.galleryService.SetCover(ctx, gallery, coverImageID)
|
||||
}); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (r *mutationResolver) ResetGalleryCover(ctx context.Context, input GalleryResetCoverInput) (bool, error) {
|
||||
galleryID, err := strconv.Atoi(input.GalleryID)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("converting gallery id: %w", err)
|
||||
}
|
||||
|
||||
if err := r.withTxn(ctx, func(ctx context.Context) error {
|
||||
qb := r.repository.Gallery
|
||||
gallery, err := qb.Find(ctx, galleryID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if gallery == nil {
|
||||
return fmt.Errorf("gallery with id %d not found", galleryID)
|
||||
}
|
||||
|
||||
return r.galleryService.ResetCover(ctx, gallery)
|
||||
}); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (r *mutationResolver) getGalleryChapter(ctx context.Context, id int) (ret *models.GalleryChapter, err error) {
|
||||
if err := r.withTxn(ctx, func(ctx context.Context) error {
|
||||
ret, err = r.repository.GalleryChapter.Find(ctx, id)
|
||||
|
|
|
@ -24,6 +24,9 @@ type GalleryService interface {
|
|||
AddImages(ctx context.Context, g *models.Gallery, toAdd ...int) error
|
||||
RemoveImages(ctx context.Context, g *models.Gallery, toRemove ...int) error
|
||||
|
||||
SetCover(ctx context.Context, g *models.Gallery, coverImageId int) error
|
||||
ResetCover(ctx context.Context, g *models.Gallery) error
|
||||
|
||||
Destroy(ctx context.Context, i *models.Gallery, fileDeleter *image.FileDeleter, deleteGenerated, deleteFile bool) ([]*models.Image, error)
|
||||
|
||||
ValidateImageGalleryChange(ctx context.Context, i *models.Image, updateIDs models.UpdateIDs) error
|
||||
|
|
|
@ -52,6 +52,22 @@ func (s *Service) RemoveImages(ctx context.Context, g *models.Gallery, toRemove
|
|||
return s.Updated(ctx, g.ID)
|
||||
}
|
||||
|
||||
func (s *Service) SetCover(ctx context.Context, g *models.Gallery, coverImageID int) error {
|
||||
if err := s.Repository.SetCover(ctx, g.ID, coverImageID); err != nil {
|
||||
return fmt.Errorf("failed to set cover: %w", err)
|
||||
}
|
||||
|
||||
return s.Updated(ctx, g.ID)
|
||||
}
|
||||
|
||||
func (s *Service) ResetCover(ctx context.Context, g *models.Gallery) error {
|
||||
if err := s.Repository.ResetCover(ctx, g.ID); err != nil {
|
||||
return fmt.Errorf("failed to reset cover: %w", err)
|
||||
}
|
||||
|
||||
return s.Updated(ctx, g.ID)
|
||||
}
|
||||
|
||||
func AddPerformer(ctx context.Context, qb models.GalleryUpdater, o *models.Gallery, performerID int) error {
|
||||
galleryPartial := models.NewGalleryPartial()
|
||||
galleryPartial.PerformerIDs = &models.UpdateIDs{
|
||||
|
|
|
@ -107,6 +107,13 @@ func FindGalleryCover(ctx context.Context, r models.ImageQueryer, galleryID int,
|
|||
}
|
||||
|
||||
func findGalleryCover(ctx context.Context, r models.ImageQueryer, galleryID int, useCoverJpg bool, galleryCoverRegex string) (*models.Image, error) {
|
||||
img, err := r.CoverByGalleryID(ctx, galleryID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if img != nil {
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// try to find cover.jpg in the gallery
|
||||
perPage := 1
|
||||
sortBy := "path"
|
||||
|
|
|
@ -628,6 +628,34 @@ func (_m *GalleryReaderWriter) RemoveImages(ctx context.Context, galleryID int,
|
|||
return r0
|
||||
}
|
||||
|
||||
// ResetCover provides a mock function with given fields: ctx, galleryID
|
||||
func (_m *GalleryReaderWriter) ResetCover(ctx context.Context, galleryID int) error {
|
||||
ret := _m.Called(ctx, galleryID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int) error); ok {
|
||||
r0 = rf(ctx, galleryID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SetCover provides a mock function with given fields: ctx, galleryID, coverImageID
|
||||
func (_m *GalleryReaderWriter) SetCover(ctx context.Context, galleryID int, coverImageID int) error {
|
||||
ret := _m.Called(ctx, galleryID, coverImageID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int, int) error); ok {
|
||||
r0 = rf(ctx, galleryID, coverImageID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: ctx, updatedGallery
|
||||
func (_m *GalleryReaderWriter) Update(ctx context.Context, updatedGallery *models.Gallery) error {
|
||||
ret := _m.Called(ctx, updatedGallery)
|
||||
|
|
|
@ -114,6 +114,29 @@ func (_m *ImageReaderWriter) CountByGalleryID(ctx context.Context, galleryID int
|
|||
return r0, r1
|
||||
}
|
||||
|
||||
// CoverByGalleryID provides a mock function with given fields: ctx, galleryId
|
||||
func (_m *ImageReaderWriter) CoverByGalleryID(ctx context.Context, galleryId int) (*models.Image, error) {
|
||||
ret := _m.Called(ctx, galleryId)
|
||||
|
||||
var r0 *models.Image
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int) *models.Image); ok {
|
||||
r0 = rf(ctx, galleryId)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*models.Image)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
|
||||
r1 = rf(ctx, galleryId)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Create provides a mock function with given fields: ctx, newImage, fileIDs
|
||||
func (_m *ImageReaderWriter) Create(ctx context.Context, newImage *models.Image, fileIDs []models.FileID) error {
|
||||
ret := _m.Called(ctx, newImage, fileIDs)
|
||||
|
|
|
@ -83,6 +83,8 @@ type GalleryWriter interface {
|
|||
AddFileID(ctx context.Context, id int, fileID FileID) error
|
||||
AddImages(ctx context.Context, galleryID int, imageIDs ...int) error
|
||||
RemoveImages(ctx context.Context, galleryID int, imageIDs ...int) error
|
||||
SetCover(ctx context.Context, galleryID int, coverImageID int) error
|
||||
ResetCover(ctx context.Context, galleryID int) error
|
||||
}
|
||||
|
||||
// GalleryReaderWriter provides all gallery methods.
|
||||
|
|
|
@ -25,6 +25,7 @@ type ImageFinder interface {
|
|||
type ImageQueryer interface {
|
||||
Query(ctx context.Context, options ImageQueryOptions) (*ImageQueryResult, error)
|
||||
QueryCount(ctx context.Context, imageFilter *ImageFilterType, findFilter *FindFilterType) (int, error)
|
||||
CoverByGalleryID(ctx context.Context, galleryId int) (*Image, error)
|
||||
}
|
||||
|
||||
// ImageCounter provides methods to count images.
|
||||
|
|
|
@ -30,7 +30,7 @@ const (
|
|||
dbConnTimeout = 30
|
||||
)
|
||||
|
||||
var appSchemaVersion uint = 65
|
||||
var appSchemaVersion uint = 66
|
||||
|
||||
//go:embed migrations/*.sql
|
||||
var migrationsBox embed.FS
|
||||
|
|
|
@ -890,6 +890,14 @@ func (qb *GalleryStore) UpdateImages(ctx context.Context, galleryID int, imageID
|
|||
return galleryRepository.images.replace(ctx, galleryID, imageIDs)
|
||||
}
|
||||
|
||||
func (qb *GalleryStore) SetCover(ctx context.Context, galleryID int, coverImageID int) error {
|
||||
return imageGalleriesTableMgr.setCover(ctx, coverImageID, galleryID)
|
||||
}
|
||||
|
||||
func (qb *GalleryStore) ResetCover(ctx context.Context, galleryID int) error {
|
||||
return imageGalleriesTableMgr.resetCover(ctx, galleryID)
|
||||
}
|
||||
|
||||
func (qb *GalleryStore) GetSceneIDs(ctx context.Context, id int) ([]int, error) {
|
||||
return galleryRepository.scenes.getIDs(ctx, id)
|
||||
}
|
||||
|
|
|
@ -2973,6 +2973,34 @@ func TestGalleryQueryHasChapters(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestGallerySetAndResetCover(t *testing.T) {
|
||||
withTxn(func(ctx context.Context) error {
|
||||
sqb := db.Gallery
|
||||
|
||||
imagePath2 := getFilePath(folderIdxWithImageFiles, getImageBasename(imageIdx2WithGallery))
|
||||
|
||||
result, err := db.Image.CoverByGalleryID(ctx, galleryIDs[galleryIdxWithTwoImages])
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, result)
|
||||
|
||||
err = sqb.SetCover(ctx, galleryIDs[galleryIdxWithTwoImages], imageIDs[imageIdx2WithGallery])
|
||||
assert.Nil(t, err)
|
||||
|
||||
result, err = db.Image.CoverByGalleryID(ctx, galleryIDs[galleryIdxWithTwoImages])
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, result.Path, imagePath2)
|
||||
|
||||
err = sqb.ResetCover(ctx, galleryIDs[galleryIdxWithTwoImages])
|
||||
assert.Nil(t, err)
|
||||
|
||||
result, err = db.Image.CoverByGalleryID(ctx, galleryIDs[galleryIdxWithTwoImages])
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, result)
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// TODO Count
|
||||
// TODO All
|
||||
// TODO Query
|
||||
|
|
|
@ -480,6 +480,42 @@ func (qb *ImageStore) getMany(ctx context.Context, q *goqu.SelectDataset) ([]*mo
|
|||
return ret, nil
|
||||
}
|
||||
|
||||
// Returns the custom cover for the gallery, if one has been set.
|
||||
func (qb *ImageStore) CoverByGalleryID(ctx context.Context, galleryID int) (*models.Image, error) {
|
||||
table := qb.table()
|
||||
|
||||
sq := dialect.From(table).
|
||||
InnerJoin(
|
||||
galleriesImagesJoinTable,
|
||||
goqu.On(table.Col(idColumn).Eq(galleriesImagesJoinTable.Col(imageIDColumn))),
|
||||
).
|
||||
Select(table.Col(idColumn)).
|
||||
Where(goqu.And(
|
||||
galleriesImagesJoinTable.Col("gallery_id").Eq(galleryID),
|
||||
galleriesImagesJoinTable.Col("cover").Eq(true),
|
||||
))
|
||||
|
||||
q := qb.selectDataset().Prepared(true).Where(
|
||||
table.Col(idColumn).Eq(
|
||||
sq,
|
||||
),
|
||||
)
|
||||
|
||||
ret, err := qb.getMany(ctx, q)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting cover for gallery %d: %w", galleryID, err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(ret) > 1:
|
||||
return nil, fmt.Errorf("internal error: multiple covers returned for gallery %d", galleryID)
|
||||
case len(ret) == 1:
|
||||
return ret[0], nil
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (qb *ImageStore) GetFiles(ctx context.Context, id int) ([]models.File, error) {
|
||||
fileIDs, err := imageRepository.files.get(ctx, id)
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE `galleries_images` ADD COLUMN `cover` BOOLEAN NOT NULL DEFAULT 0;
|
||||
CREATE UNIQUE INDEX `index_galleries_images_gallery_id_cover` on `galleries_images` (`gallery_id`, `cover`) WHERE `cover` = 1;
|
|
@ -710,6 +710,45 @@ func (t *scenesGroupsTable) modifyJoins(ctx context.Context, id int, v []models.
|
|||
return nil
|
||||
}
|
||||
|
||||
type imageGalleriesTable struct {
|
||||
joinTable
|
||||
}
|
||||
|
||||
func (t *imageGalleriesTable) setCover(ctx context.Context, id int, galleryID int) error {
|
||||
if err := t.resetCover(ctx, galleryID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
table := t.table.table
|
||||
|
||||
q := dialect.Update(table).Prepared(true).Set(goqu.Record{
|
||||
"cover": true,
|
||||
}).Where(t.idColumn.Eq(id), table.Col(galleryIDColumn).Eq(galleryID))
|
||||
|
||||
if _, err := exec(ctx, q); err != nil {
|
||||
return fmt.Errorf("setting cover flag in %s: %w", t.table.table.GetTable(), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *imageGalleriesTable) resetCover(ctx context.Context, galleryID int) error {
|
||||
table := t.table.table
|
||||
|
||||
q := dialect.Update(table).Prepared(true).Set(goqu.Record{
|
||||
"cover": false,
|
||||
}).Where(
|
||||
table.Col(galleryIDColumn).Eq(galleryID),
|
||||
table.Col("cover").Eq(true),
|
||||
)
|
||||
|
||||
if _, err := exec(ctx, q); err != nil {
|
||||
return fmt.Errorf("unsetting cover flags in %s: %w", t.table.table.GetTable(), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type relatedFilesTable struct {
|
||||
table
|
||||
}
|
||||
|
|
|
@ -57,12 +57,14 @@ var (
|
|||
},
|
||||
}
|
||||
|
||||
imageGalleriesTableMgr = &joinTable{
|
||||
table: table{
|
||||
table: galleriesImagesJoinTable,
|
||||
idColumn: galleriesImagesJoinTable.Col(imageIDColumn),
|
||||
imageGalleriesTableMgr = &imageGalleriesTable{
|
||||
joinTable: joinTable{
|
||||
table: table{
|
||||
table: galleriesImagesJoinTable,
|
||||
idColumn: galleriesImagesJoinTable.Col(imageIDColumn),
|
||||
},
|
||||
fkColumn: galleriesImagesJoinTable.Col(galleryIDColumn),
|
||||
},
|
||||
fkColumn: galleriesImagesJoinTable.Col(galleryIDColumn),
|
||||
}
|
||||
|
||||
imagesTagsTableMgr = &joinTable{
|
||||
|
|
|
@ -43,3 +43,13 @@ mutation AddGalleryImages($gallery_id: ID!, $image_ids: [ID!]!) {
|
|||
mutation RemoveGalleryImages($gallery_id: ID!, $image_ids: [ID!]!) {
|
||||
removeGalleryImages(input: { gallery_id: $gallery_id, image_ids: $image_ids })
|
||||
}
|
||||
|
||||
mutation SetGalleryCover($gallery_id: ID!, $cover_image_id: ID!) {
|
||||
setGalleryCover(
|
||||
input: { gallery_id: $gallery_id, cover_image_id: $cover_image_id }
|
||||
)
|
||||
}
|
||||
|
||||
mutation ResetGalleryCover($gallery_id: ID!) {
|
||||
resetGalleryCover(input: { gallery_id: $gallery_id })
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import { Helmet } from "react-helmet";
|
|||
import * as GQL from "src/core/generated-graphql";
|
||||
import {
|
||||
mutateMetadataScan,
|
||||
mutateResetGalleryCover,
|
||||
useFindGallery,
|
||||
useGalleryUpdate,
|
||||
} from "src/core/StashService";
|
||||
|
@ -138,6 +139,25 @@ export const GalleryPage: React.FC<IProps> = ({ gallery, add }) => {
|
|||
);
|
||||
}
|
||||
|
||||
async function onResetCover() {
|
||||
try {
|
||||
await mutateResetGalleryCover({
|
||||
gallery_id: gallery.id!,
|
||||
});
|
||||
|
||||
Toast.success(
|
||||
intl.formatMessage(
|
||||
{ id: "toast.updated_entity" },
|
||||
{
|
||||
entity: intl.formatMessage({ id: "gallery" }).toLocaleLowerCase(),
|
||||
}
|
||||
)
|
||||
);
|
||||
} catch (e) {
|
||||
Toast.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function onClickChapter(imageindex: number) {
|
||||
showLightbox(imageindex - 1);
|
||||
}
|
||||
|
@ -176,7 +196,6 @@ export const GalleryPage: React.FC<IProps> = ({ gallery, add }) => {
|
|||
<Dropdown.Menu className="bg-secondary text-white">
|
||||
{path ? (
|
||||
<Dropdown.Item
|
||||
key="rescan"
|
||||
className="bg-secondary text-white"
|
||||
onClick={() => onRescan()}
|
||||
>
|
||||
|
@ -184,7 +203,12 @@ export const GalleryPage: React.FC<IProps> = ({ gallery, add }) => {
|
|||
</Dropdown.Item>
|
||||
) : undefined}
|
||||
<Dropdown.Item
|
||||
key="delete-gallery"
|
||||
className="bg-secondary text-white"
|
||||
onClick={() => onResetCover()}
|
||||
>
|
||||
<FormattedMessage id="actions.reset_cover" />
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
className="bg-secondary text-white"
|
||||
onClick={() => setIsDeleteAlertOpen(true)}
|
||||
>
|
||||
|
|
|
@ -3,8 +3,14 @@ import * as GQL from "src/core/generated-graphql";
|
|||
import { GalleriesCriterion } from "src/models/list-filter/criteria/galleries";
|
||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||
import { ImageList } from "src/components/Images/ImageList";
|
||||
import { mutateRemoveGalleryImages } from "src/core/StashService";
|
||||
import { showWhenSelected } from "src/components/List/ItemList";
|
||||
import {
|
||||
mutateRemoveGalleryImages,
|
||||
mutateSetGalleryCover,
|
||||
} from "src/core/StashService";
|
||||
import {
|
||||
showWhenSelected,
|
||||
showWhenSingleSelection,
|
||||
} from "src/components/List/ItemList";
|
||||
import { useToast } from "src/hooks/Toast";
|
||||
import { useIntl } from "react-intl";
|
||||
import { faMinus } from "@fortawesome/free-solid-svg-icons";
|
||||
|
@ -58,6 +64,35 @@ export const GalleryImagesPanel: React.FC<IGalleryDetailsProps> = ({
|
|||
return filter;
|
||||
}
|
||||
|
||||
async function setCover(
|
||||
result: GQL.FindImagesQueryResult,
|
||||
filter: ListFilterModel,
|
||||
selectedIds: Set<string>
|
||||
) {
|
||||
const coverImageID = selectedIds.values().next();
|
||||
if (coverImageID.done) {
|
||||
// operation should only be displayed when exactly one image is selected
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await mutateSetGalleryCover({
|
||||
gallery_id: gallery.id!,
|
||||
cover_image_id: coverImageID.value,
|
||||
});
|
||||
|
||||
Toast.success(
|
||||
intl.formatMessage(
|
||||
{ id: "toast.updated_entity" },
|
||||
{
|
||||
entity: intl.formatMessage({ id: "gallery" }).toLocaleLowerCase(),
|
||||
}
|
||||
)
|
||||
);
|
||||
} catch (e) {
|
||||
Toast.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function removeImages(
|
||||
result: GQL.FindImagesQueryResult,
|
||||
filter: ListFilterModel,
|
||||
|
@ -85,6 +120,11 @@ export const GalleryImagesPanel: React.FC<IGalleryDetailsProps> = ({
|
|||
}
|
||||
|
||||
const otherOperations = [
|
||||
{
|
||||
text: intl.formatMessage({ id: "actions.set_cover" }),
|
||||
onClick: setCover,
|
||||
isDisplayed: showWhenSingleSelection,
|
||||
},
|
||||
{
|
||||
text: intl.formatMessage({ id: "actions.remove_from_gallery" }),
|
||||
onClick: removeImages,
|
||||
|
|
|
@ -335,3 +335,11 @@ export const showWhenSelected = <T extends QueryResult>(
|
|||
) => {
|
||||
return selectedIds.size > 0;
|
||||
};
|
||||
|
||||
export const showWhenSingleSelection = <T extends QueryResult>(
|
||||
result: T,
|
||||
filter: ListFilterModel,
|
||||
selectedIds: Set<string>
|
||||
) => {
|
||||
return selectedIds.size == 1;
|
||||
};
|
||||
|
|
|
@ -1526,6 +1526,34 @@ export const mutateAddGalleryImages = (input: GQL.GalleryAddInput) =>
|
|||
},
|
||||
});
|
||||
|
||||
export const mutateSetGalleryCover = (input: GQL.GallerySetCoverInput) =>
|
||||
client.mutate<GQL.SetGalleryCoverMutation>({
|
||||
mutation: GQL.SetGalleryCoverDocument,
|
||||
variables: input,
|
||||
update(cache, result) {
|
||||
if (!result.data?.setGalleryCover) return;
|
||||
|
||||
cache.evict({
|
||||
id: cache.identify({ __typename: "Gallery", id: input.gallery_id }),
|
||||
fieldName: "cover",
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const mutateResetGalleryCover = (input: GQL.GalleryResetCoverInput) =>
|
||||
client.mutate<GQL.ResetGalleryCoverMutation>({
|
||||
mutation: GQL.ResetGalleryCoverDocument,
|
||||
variables: input,
|
||||
update(cache, result) {
|
||||
if (!result.data?.resetGalleryCover) return;
|
||||
|
||||
cache.evict({
|
||||
id: cache.identify({ __typename: "Gallery", id: input.gallery_id }),
|
||||
fieldName: "cover",
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const mutateRemoveGalleryImages = (input: GQL.GalleryRemoveInput) =>
|
||||
client.mutate<GQL.RemoveGalleryImagesMutation>({
|
||||
mutation: GQL.RemoveGalleryImagesDocument,
|
||||
|
|
|
@ -94,6 +94,7 @@
|
|||
"remove_from_gallery": "Remove from Gallery",
|
||||
"rename_gen_files": "Rename generated files",
|
||||
"rescan": "Rescan",
|
||||
"reset_cover": "Restore Default Cover",
|
||||
"reshuffle": "Reshuffle",
|
||||
"running": "running",
|
||||
"save": "Save",
|
||||
|
@ -114,6 +115,7 @@
|
|||
"selective_scan": "Selective Scan",
|
||||
"set_as_default": "Set as default",
|
||||
"set_back_image": "Back image…",
|
||||
"set_cover": "Set as Cover",
|
||||
"set_front_image": "Front image…",
|
||||
"set_image": "Set image…",
|
||||
"show": "Show",
|
||||
|
|
Loading…
Reference in New Issue