feat: persist people rules
This commit is contained in:
parent
947132ac77
commit
e8fdddf08e
6 changed files with 75 additions and 76 deletions
|
@ -38,7 +38,7 @@ export class ImmichApi {
|
||||||
public personApi: PersonApi;
|
public personApi: PersonApi;
|
||||||
public systemConfigApi: SystemConfigApi;
|
public systemConfigApi: SystemConfigApi;
|
||||||
public userApi: UserApi;
|
public userApi: UserApi;
|
||||||
|
|
||||||
private config: Configuration;
|
private config: Configuration;
|
||||||
|
|
||||||
constructor(params: ConfigurationParameters) {
|
constructor(params: ConfigurationParameters) {
|
||||||
|
|
|
@ -6,17 +6,15 @@
|
||||||
import Button from '$lib/components/elements/buttons/button.svelte';
|
import Button from '$lib/components/elements/buttons/button.svelte';
|
||||||
import FaceThumbnail from '$lib/components/assets/thumbnail/face-thumbnail.svelte';
|
import FaceThumbnail from '$lib/components/assets/thumbnail/face-thumbnail.svelte';
|
||||||
|
|
||||||
export let selectedPeople: Set<PersonResponseDto> = new Set();
|
export let selectedIds: string[] = [];
|
||||||
let people: PersonResponseDto[] = [];
|
let people: PersonResponseDto[] = [];
|
||||||
let newPeople: PersonResponseDto[] = [];
|
let newPeople: PersonResponseDto[] = [];
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{ close: void; confirm: { people: PersonResponseDto[] } }>();
|
const dispatch = createEventDispatcher<{ close: void; confirm: PersonResponseDto[] }>();
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
const { data } = await api.personApi.getAllPeople({ withHidden: false });
|
const { data } = await api.personApi.getAllPeople({ withHidden: false });
|
||||||
|
people = data.people.filter(({ id }) => !selectedIds.includes(id));
|
||||||
const selectedPeopleIds = Array.from(selectedPeople).map((p) => p.id);
|
|
||||||
people = data.people.filter((p) => !selectedPeopleIds.includes(p.id));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSelection = (e: CustomEvent<{ person: PersonResponseDto }>) => {
|
const handleSelection = (e: CustomEvent<{ person: PersonResponseDto }>) => {
|
||||||
|
@ -28,15 +26,11 @@
|
||||||
newPeople = [...newPeople, person];
|
newPeople = [...newPeople, person];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onConfirmClicked = () => {
|
|
||||||
dispatch('confirm', { people: newPeople });
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ControlAppBar showBackButton backIcon={ArrowLeft} on:close-button-click={() => dispatch('close')}>
|
<ControlAppBar showBackButton backIcon={ArrowLeft} on:close-button-click={() => dispatch('close')}>
|
||||||
<svelte:fragment slot="leading">
|
<svelte:fragment slot="leading">
|
||||||
<p class="text-immich-fg dark:text-immich-dark-fg font-medium">
|
<p class="font-medium text-immich-fg dark:text-immich-dark-fg">
|
||||||
{#if newPeople.length === 0}
|
{#if newPeople.length === 0}
|
||||||
Select faces
|
Select faces
|
||||||
{:else if newPeople.length === 1}
|
{:else if newPeople.length === 1}
|
||||||
|
@ -48,12 +42,14 @@
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
|
||||||
<svelte:fragment slot="trailing">
|
<svelte:fragment slot="trailing">
|
||||||
<Button disabled={newPeople.length === 0} size="sm" title="Confirm" on:click={onConfirmClicked}>Confirm</Button>
|
<Button disabled={newPeople.length === 0} size="sm" title="Confirm" on:click={() => dispatch('confirm', newPeople)}
|
||||||
|
>Confirm</Button
|
||||||
|
>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</ControlAppBar>
|
</ControlAppBar>
|
||||||
|
|
||||||
<div class="mt-24 flex flex-wrap gap-2 px-8">
|
<div class="mt-24 flex flex-wrap gap-2 px-8">
|
||||||
{#each people as person}
|
{#each people as person (person.id)}
|
||||||
<FaceThumbnail
|
<FaceThumbnail
|
||||||
{person}
|
{person}
|
||||||
thumbnailSize={180}
|
thumbnailSize={180}
|
||||||
|
|
|
@ -1,60 +1,33 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import BaseModal from '$lib/components/shared-components/base-modal.svelte';
|
import BaseModal from '$lib/components/shared-components/base-modal.svelte';
|
||||||
import { createEventDispatcher, onMount } from 'svelte';
|
import { PersonResponseDto, RuleKey, RuleResponseDto, api } from '@api';
|
||||||
import { RuleKey, type AlbumResponseDto, type PersonResponseDto, api } from '@api';
|
import { createEventDispatcher } from 'svelte';
|
||||||
import Plus from 'svelte-material-icons/Plus.svelte';
|
import Plus from 'svelte-material-icons/Plus.svelte';
|
||||||
|
import { fly } from 'svelte/transition';
|
||||||
import Button from '../../elements/buttons/button.svelte';
|
import Button from '../../elements/buttons/button.svelte';
|
||||||
import Portal from '../../shared-components/portal/portal.svelte';
|
import Portal from '../../shared-components/portal/portal.svelte';
|
||||||
import FaceSelection from './face-selection.svelte';
|
import FaceSelection from './face-selection.svelte';
|
||||||
import { fly } from 'svelte/transition';
|
|
||||||
|
|
||||||
export let album: AlbumResponseDto;
|
export let rules: RuleResponseDto[] = [];
|
||||||
|
|
||||||
let peopleSelection = false;
|
let peopleSelection = false;
|
||||||
let locationSelection = false;
|
let locationSelection = false;
|
||||||
let selectedPeople = new Set<PersonResponseDto>();
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{ close: void }>();
|
$: peopleRules = rules.filter((rule) => rule.key === RuleKey.Person);
|
||||||
|
|
||||||
const handlePeopleSelected = async (e: CustomEvent<{ people: PersonResponseDto[] }>) => {
|
const dispatch = createEventDispatcher<{
|
||||||
|
submit: RuleResponseDto[];
|
||||||
|
close: void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const handleSelectPeople = async (people: PersonResponseDto[]) => {
|
||||||
|
rules = [...rules, ...people.map((person) => ({ key: RuleKey.Person, value: person.id } as RuleResponseDto))];
|
||||||
peopleSelection = false;
|
peopleSelection = false;
|
||||||
const people = e.detail.people;
|
|
||||||
|
|
||||||
selectedPeople = new Set([...selectedPeople, ...people]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRemovePerson = (person: PersonResponseDto) => {
|
const handleRemoveRule = async (rule: RuleResponseDto) => {
|
||||||
const temp = new Set(selectedPeople);
|
rules = rules.filter((_rule) => rule !== _rule);
|
||||||
temp.delete(person);
|
|
||||||
selectedPeople = temp;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateRule = async () => {
|
|
||||||
// for (const person of people) {
|
|
||||||
// const { data } = await api.ruleApi.createRule({
|
|
||||||
// createRuleDto: {
|
|
||||||
// albumId: album.id,
|
|
||||||
// key: RuleKey.Person,
|
|
||||||
// value: person.id,
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// album.rules = [...album.rules, data];
|
|
||||||
// }
|
|
||||||
};
|
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
const addedPeople: PersonResponseDto[] = [];
|
|
||||||
|
|
||||||
for (const rule of album.rules) {
|
|
||||||
if (rule.key === RuleKey.Person) {
|
|
||||||
const personId = String(rule.value);
|
|
||||||
const { data } = await api.personApi.getPerson({ id: personId });
|
|
||||||
addedPeople.push(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedPeople = new Set([...selectedPeople, ...addedPeople]);
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BaseModal
|
<BaseModal
|
||||||
|
@ -65,7 +38,7 @@
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="title">
|
<svelte:fragment slot="title">
|
||||||
<div class="flex place-items-center gap-2">
|
<div class="flex place-items-center gap-2">
|
||||||
<p class="text-immich-fg dark:text-immich-dark-fg font-medium">Automatically add photos</p>
|
<p class="font-medium text-immich-fg dark:text-immich-dark-fg">Automatically add photos</p>
|
||||||
</div>
|
</div>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
|
||||||
|
@ -75,9 +48,9 @@
|
||||||
<p class="text-sm">PEOPLE</p>
|
<p class="text-sm">PEOPLE</p>
|
||||||
|
|
||||||
<div class="mt-4 flex flex-wrap gap-2">
|
<div class="mt-4 flex flex-wrap gap-2">
|
||||||
{#each selectedPeople as person (person.id)}
|
{#each peopleRules as rule}
|
||||||
<button on:click={() => handleRemovePerson(person)}>
|
<button on:click={() => handleRemoveRule(rule)}>
|
||||||
<img src={api.getPeopleThumbnailUrl(person.id)} alt={person.id} class="h-20 w-20 rounded-lg" />
|
<img src={api.getPeopleThumbnailUrl(rule.value)} alt={rule.value} class="h-20 w-20 rounded-lg" />
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
|
@ -122,7 +95,7 @@
|
||||||
<svelte:fragment slot="sticky-bottom">
|
<svelte:fragment slot="sticky-bottom">
|
||||||
<div class="flex justify-end gap-2">
|
<div class="flex justify-end gap-2">
|
||||||
<Button size="sm" color="secondary" on:click={() => dispatch('close')}>Cancel</Button>
|
<Button size="sm" color="secondary" on:click={() => dispatch('close')}>Cancel</Button>
|
||||||
<Button size="sm" color="primary">Confirm</Button>
|
<Button size="sm" color="primary" on:click={() => dispatch('submit', rules)}>Confirm</Button>
|
||||||
</div>
|
</div>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</BaseModal>
|
</BaseModal>
|
||||||
|
@ -131,9 +104,13 @@
|
||||||
{#if peopleSelection}
|
{#if peopleSelection}
|
||||||
<section
|
<section
|
||||||
transition:fly={{ y: 500 }}
|
transition:fly={{ y: 500 }}
|
||||||
class="dark:bg-immich-dark-bg absolute left-0 top-0 z-[10000] h-full min-h-max w-full overflow-y-auto bg-gray-200"
|
class="absolute left-0 top-0 z-[10000] h-full min-h-max w-full overflow-y-auto bg-gray-200 dark:bg-immich-dark-bg"
|
||||||
>
|
>
|
||||||
<FaceSelection on:close={() => (peopleSelection = false)} on:confirm={handlePeopleSelected} {selectedPeople} />
|
<FaceSelection
|
||||||
|
on:close={() => (peopleSelection = false)}
|
||||||
|
on:confirm={({ detail: people }) => handleSelectPeople(people)}
|
||||||
|
selectedIds={peopleRules.map(({ value }) => value)}
|
||||||
|
/>
|
||||||
</section>
|
</section>
|
||||||
{/if}
|
{/if}
|
||||||
</Portal>
|
</Portal>
|
||||||
|
|
|
@ -100,7 +100,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="dark:bg-immich-dark-gray absolute h-full w-full select-none bg-gray-100 transition-transform"
|
class="absolute h-full w-full select-none bg-gray-100 transition-transform dark:bg-immich-dark-gray"
|
||||||
class:scale-[0.85]={selected}
|
class:scale-[0.85]={selected}
|
||||||
class:rounded-xl={selected}
|
class:rounded-xl={selected}
|
||||||
>
|
>
|
||||||
|
@ -120,7 +120,7 @@
|
||||||
</div>
|
</div>
|
||||||
{#if selectionCandidate}
|
{#if selectionCandidate}
|
||||||
<div
|
<div
|
||||||
class="bg-immich-primary absolute top-0 h-full w-full opacity-40"
|
class="absolute top-0 h-full w-full bg-immich-primary opacity-40"
|
||||||
in:fade={{ duration: 100 }}
|
in:fade={{ duration: 100 }}
|
||||||
out:fade={{ duration: 100 }}
|
out:fade={{ duration: 100 }}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -37,9 +37,9 @@
|
||||||
<div
|
<div
|
||||||
use:clickOutside
|
use:clickOutside
|
||||||
on:outclick={() => !ignoreClickOutside && dispatch('close')}
|
on:outclick={() => !ignoreClickOutside && dispatch('close')}
|
||||||
class="bg-immich-bg dark:bg-immich-dark-gray dark:text-immich-dark-fg max-h-[700px] min-h-[200px] w-[450px] overflow-y-auto rounded-lg shadow-md"
|
class="max-h-[700px] min-h-[200px] w-[450px] overflow-y-auto rounded-lg bg-immich-bg shadow-md dark:bg-immich-dark-gray dark:text-immich-dark-fg"
|
||||||
>
|
>
|
||||||
<div class="dark:bg-immich-dark-gray bg-immich-bg sticky top-0 flex place-items-center justify-between px-5 py-3">
|
<div class="sticky top-0 flex place-items-center justify-between bg-immich-bg px-5 py-3 dark:bg-immich-dark-gray">
|
||||||
<div>
|
<div>
|
||||||
<slot name="title">
|
<slot name="title">
|
||||||
<p>Modal Title</p>
|
<p>Modal Title</p>
|
||||||
|
@ -54,7 +54,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if $$slots['sticky-bottom']}
|
{#if $$slots['sticky-bottom']}
|
||||||
<div class="dark:bg-immich-dark-gray bg-immich-bg sticky bottom-0 px-5 pb-5 pt-3">
|
<div class="sticky bottom-0 bg-immich-bg px-5 pb-5 pt-3 dark:bg-immich-dark-gray">
|
||||||
<slot name="sticky-bottom" />
|
<slot name="sticky-bottom" />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
import { downloadArchive } from '$lib/utils/asset-utils';
|
import { downloadArchive } from '$lib/utils/asset-utils';
|
||||||
import { openFileUploadDialog } from '$lib/utils/file-uploader';
|
import { openFileUploadDialog } from '$lib/utils/file-uploader';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { TimeBucketSize, UserResponseDto, api } from '@api';
|
import { RuleResponseDto, TimeBucketSize, UserResponseDto, api } from '@api';
|
||||||
import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte';
|
import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte';
|
||||||
import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte';
|
import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte';
|
||||||
import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
|
import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
|
||||||
|
@ -279,6 +279,28 @@
|
||||||
handleError(error, 'Error updating album description');
|
handleError(error, 'Error updating album description');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleUpdateRules = async (rules: RuleResponseDto[]) => {
|
||||||
|
let ids = rules.filter((rule) => !!rule.id).map((rule) => rule.id);
|
||||||
|
|
||||||
|
for (const rule of album.rules) {
|
||||||
|
if (!ids.includes(rule.id)) {
|
||||||
|
await api.ruleApi.removeRule({ id: rule.id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const { id, key, value } of rules) {
|
||||||
|
if (!id) {
|
||||||
|
await api.ruleApi.createRule({ createRuleDto: { albumId: album.id, key, value } });
|
||||||
|
} else {
|
||||||
|
await api.ruleApi.updateRule({ id, updateRuleDto: { key, value } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await refreshAlbum();
|
||||||
|
|
||||||
|
viewMode = ViewMode.VIEW;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
|
@ -354,7 +376,7 @@
|
||||||
{#if viewMode === ViewMode.SELECT_ASSETS}
|
{#if viewMode === ViewMode.SELECT_ASSETS}
|
||||||
<ControlAppBar on:close-button-click={handleCloseSelectAssets}>
|
<ControlAppBar on:close-button-click={handleCloseSelectAssets}>
|
||||||
<svelte:fragment slot="leading">
|
<svelte:fragment slot="leading">
|
||||||
<p class="dark:text-immich-dark-fg text-lg">
|
<p class="text-lg dark:text-immich-dark-fg">
|
||||||
{#if $timelineSelected.size === 0}
|
{#if $timelineSelected.size === 0}
|
||||||
Add to album
|
Add to album
|
||||||
{:else}
|
{:else}
|
||||||
|
@ -366,7 +388,7 @@
|
||||||
<svelte:fragment slot="trailing">
|
<svelte:fragment slot="trailing">
|
||||||
<button
|
<button
|
||||||
on:click={handleSelectFromComputer}
|
on:click={handleSelectFromComputer}
|
||||||
class="text-immich-primary hover:bg-immich-primary/10 dark:text-immich-dark-primary dark:hover:bg-immich-dark-primary/25 rounded-lg px-6 py-2 text-sm font-medium transition-all"
|
class="rounded-lg px-6 py-2 text-sm font-medium text-immich-primary transition-all hover:bg-immich-primary/10 dark:text-immich-dark-primary dark:hover:bg-immich-dark-primary/25"
|
||||||
>
|
>
|
||||||
Select from computer
|
Select from computer
|
||||||
</button>
|
</button>
|
||||||
|
@ -385,7 +407,7 @@
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main
|
<main
|
||||||
class="bg-immich-bg dark:bg-immich-dark-bg relative h-screen overflow-hidden px-6 pt-[var(--navbar-height)] sm:px-12 md:px-24 lg:px-40"
|
class="relative h-screen overflow-hidden bg-immich-bg px-6 pt-[var(--navbar-height)] dark:bg-immich-dark-bg sm:px-12 md:px-24 lg:px-40"
|
||||||
>
|
>
|
||||||
{#if viewMode === ViewMode.SELECT_ASSETS}
|
{#if viewMode === ViewMode.SELECT_ASSETS}
|
||||||
<AssetGrid assetStore={timelineStore} assetInteractionStore={timelineInteractionStore} isSelectionMode={true} />
|
<AssetGrid assetStore={timelineStore} assetInteractionStore={timelineInteractionStore} isSelectionMode={true} />
|
||||||
|
@ -403,9 +425,9 @@
|
||||||
<input
|
<input
|
||||||
on:keydown={(e) => e.key === 'Enter' && titleInput.blur()}
|
on:keydown={(e) => e.key === 'Enter' && titleInput.blur()}
|
||||||
on:blur={handleUpdateName}
|
on:blur={handleUpdateName}
|
||||||
class="text-immich-primary dark:text-immich-dark-primary w-[99%] border-b-2 border-transparent text-6xl outline-none transition-all {isOwned
|
class="w-[99%] border-b-2 border-transparent text-6xl text-immich-primary outline-none transition-all dark:text-immich-dark-primary {isOwned
|
||||||
? 'hover:border-gray-400'
|
? 'hover:border-gray-400'
|
||||||
: 'hover:border-transparent'} bg-immich-bg focus:border-immich-primary dark:bg-immich-dark-bg dark:focus:border-immich-dark-primary dark:focus:bg-immich-dark-gray focus:border-b-2 focus:outline-none"
|
: 'hover:border-transparent'} bg-immich-bg focus:border-b-2 focus:border-immich-primary focus:outline-none dark:bg-immich-dark-bg dark:focus:border-immich-dark-primary dark:focus:bg-immich-dark-gray"
|
||||||
type="text"
|
type="text"
|
||||||
bind:value={album.albumName}
|
bind:value={album.albumName}
|
||||||
disabled={!isOwned}
|
disabled={!isOwned}
|
||||||
|
@ -479,11 +501,11 @@
|
||||||
{#if album.assetCount === 0}
|
{#if album.assetCount === 0}
|
||||||
<section id="empty-album" class=" mt-[200px] flex flex-col place-content-center place-items-center">
|
<section id="empty-album" class=" mt-[200px] flex flex-col place-content-center place-items-center">
|
||||||
<div class="w-[340px]">
|
<div class="w-[340px]">
|
||||||
<p class="dark:text-immich-dark-fg text-sm">ADD PHOTOS</p>
|
<p class="text-sm dark:text-immich-dark-fg">ADD PHOTOS</p>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
on:click={() => (viewMode = ViewMode.RULE_SELECTION)}
|
on:click={() => (viewMode = ViewMode.RULE_SELECTION)}
|
||||||
class="bg-immich-bg text-immich-fg hover:text-immich-primary dark:bg-immich-dark-gray dark:text-immich-dark-fg dark:hover:text-immich-dark-primary mt-5 flex w-full place-items-center gap-6 rounded-md border px-8 py-8 transition-all hover:bg-gray-100 dark:border-none"
|
class="mt-5 flex w-full place-items-center gap-6 rounded-md border bg-immich-bg px-8 py-8 text-immich-fg transition-all hover:bg-gray-100 hover:text-immich-primary dark:border-none dark:bg-immich-dark-gray dark:text-immich-dark-fg dark:hover:text-immich-dark-primary"
|
||||||
>
|
>
|
||||||
<span class="immich-text-primary"><FaceMan size="34" /> </span>
|
<span class="immich-text-primary"><FaceMan size="34" /> </span>
|
||||||
<div class="text-left">
|
<div class="text-left">
|
||||||
|
@ -494,7 +516,7 @@
|
||||||
|
|
||||||
<button
|
<button
|
||||||
on:click={() => (viewMode = ViewMode.SELECT_ASSETS)}
|
on:click={() => (viewMode = ViewMode.SELECT_ASSETS)}
|
||||||
class="bg-immich-bg text-immich-fg hover:text-immich-primary dark:bg-immich-dark-gray dark:text-immich-dark-fg dark:hover:text-immich-dark-primary mt-5 flex w-full place-items-center gap-6 rounded-md border px-8 py-8 transition-all hover:bg-gray-100 dark:border-none"
|
class="mt-5 flex w-full place-items-center gap-6 rounded-md border bg-immich-bg px-8 py-8 text-immich-fg transition-all hover:bg-gray-100 hover:text-immich-primary dark:border-none dark:bg-immich-dark-gray dark:text-immich-dark-fg dark:hover:text-immich-dark-primary"
|
||||||
>
|
>
|
||||||
<span class="immich-text-primary"><Plus size="34" /> </span>
|
<span class="immich-text-primary"><Plus size="34" /> </span>
|
||||||
<span class="text-lg">Select photos</span>
|
<span class="text-lg">Select photos</span>
|
||||||
|
@ -550,5 +572,9 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if viewMode === ViewMode.RULE_SELECTION}
|
{#if viewMode === ViewMode.RULE_SELECTION}
|
||||||
<RuleSelection on:close={() => (viewMode = ViewMode.VIEW)} {album} />
|
<RuleSelection
|
||||||
|
on:close={() => (viewMode = ViewMode.VIEW)}
|
||||||
|
rules={album.rules}
|
||||||
|
on:submit={({ detail: rules }) => handleUpdateRules(rules)}
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
Loading…
Reference in a new issue