mirror of
https://github.com/immich-app/immich.git
synced 2025-07-09 03:04:16 -04:00
refactor: asset tag modal (#18867)
This commit is contained in:
parent
72401aa6b1
commit
97e86e409a
@ -1,11 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { shortcut } from '$lib/actions/shortcut';
|
import { shortcut } from '$lib/actions/shortcut';
|
||||||
import Icon from '$lib/components/elements/icon.svelte';
|
import Icon from '$lib/components/elements/icon.svelte';
|
||||||
import TagAssetForm from '$lib/components/forms/tag-asset-form.svelte';
|
|
||||||
import Portal from '$lib/components/shared-components/portal/portal.svelte';
|
|
||||||
import { AppRoute } from '$lib/constants';
|
import { AppRoute } from '$lib/constants';
|
||||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { removeTag, tagAssets } from '$lib/utils/asset-utils';
|
import { modalManager } from '$lib/managers/modal-manager.svelte';
|
||||||
|
import AssetTagModal from '$lib/modals/AssetTagModal.svelte';
|
||||||
|
import { removeTag } from '$lib/utils/asset-utils';
|
||||||
import { getAssetInfo, type AssetResponseDto } from '@immich/sdk';
|
import { getAssetInfo, type AssetResponseDto } from '@immich/sdk';
|
||||||
import { mdiClose, mdiPlus } from '@mdi/js';
|
import { mdiClose, mdiPlus } from '@mdi/js';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
@ -19,19 +19,12 @@
|
|||||||
|
|
||||||
let tags = $derived(asset.tags || []);
|
let tags = $derived(asset.tags || []);
|
||||||
|
|
||||||
let isOpen = $state(false);
|
const handleAddTag = async () => {
|
||||||
|
const success = await modalManager.show(AssetTagModal, { assetIds: [asset.id] });
|
||||||
|
|
||||||
const handleAdd = () => (isOpen = true);
|
if (success) {
|
||||||
|
asset = await getAssetInfo({ id: asset.id });
|
||||||
const handleCancel = () => (isOpen = false);
|
|
||||||
|
|
||||||
const handleTag = async (tagIds: string[]) => {
|
|
||||||
const ids = await tagAssets({ tagIds, assetIds: [asset.id], showNotification: false });
|
|
||||||
if (ids) {
|
|
||||||
isOpen = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
asset = await getAssetInfo({ id: asset.id });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRemove = async (tagId: string) => {
|
const handleRemove = async (tagId: string) => {
|
||||||
@ -42,7 +35,7 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:document use:shortcut={{ shortcut: { key: 't' }, onShortcut: () => (isOpen = true) }} />
|
<svelte:document use:shortcut={{ shortcut: { key: 't' }, onShortcut: handleAddTag }} />
|
||||||
|
|
||||||
{#if isOwner && !authManager.key}
|
{#if isOwner && !authManager.key}
|
||||||
<section class="px-4 mt-4">
|
<section class="px-4 mt-4">
|
||||||
@ -75,16 +68,10 @@
|
|||||||
type="button"
|
type="button"
|
||||||
class="rounded-full bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700 hover:text-gray-700 dark:hover:text-gray-200 flex place-items-center place-content-center gap-1 px-2 py-1"
|
class="rounded-full bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700 hover:text-gray-700 dark:hover:text-gray-200 flex place-items-center place-content-center gap-1 px-2 py-1"
|
||||||
title="Add tag"
|
title="Add tag"
|
||||||
onclick={handleAdd}
|
onclick={handleAddTag}
|
||||||
>
|
>
|
||||||
<span class="text-sm px-1 flex place-items-center place-content-center gap-1"><Icon path={mdiPlus} />Add</span>
|
<span class="text-sm px-1 flex place-items-center place-content-center gap-1"><Icon path={mdiPlus} />Add</span>
|
||||||
</button>
|
</button>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if isOpen}
|
|
||||||
<Portal>
|
|
||||||
<TagAssetForm onTag={(tagsIds) => handleTag(tagsIds)} onCancel={handleCancel} />
|
|
||||||
</Portal>
|
|
||||||
{/if}
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { shortcut } from '$lib/actions/shortcut';
|
import { shortcut } from '$lib/actions/shortcut';
|
||||||
import TagAssetForm from '$lib/components/forms/tag-asset-form.svelte';
|
import { modalManager } from '$lib/managers/modal-manager.svelte';
|
||||||
import { tagAssets } from '$lib/utils/asset-utils';
|
import AssetTagModal from '$lib/modals/AssetTagModal.svelte';
|
||||||
import { mdiTagMultipleOutline, mdiTimerSand } from '@mdi/js';
|
|
||||||
import { t } from 'svelte-i18n';
|
|
||||||
import { IconButton } from '@immich/ui';
|
import { IconButton } from '@immich/ui';
|
||||||
|
import { mdiTagMultipleOutline } from '@mdi/js';
|
||||||
|
import { t } from 'svelte-i18n';
|
||||||
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
|
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
|
||||||
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
||||||
|
|
||||||
@ -17,45 +17,24 @@
|
|||||||
const text = $t('tag');
|
const text = $t('tag');
|
||||||
const icon = mdiTagMultipleOutline;
|
const icon = mdiTagMultipleOutline;
|
||||||
|
|
||||||
let loading = $state(false);
|
|
||||||
let isOpen = $state(false);
|
|
||||||
|
|
||||||
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
||||||
|
|
||||||
const handleOpen = () => (isOpen = true);
|
const handleTagAssets = async () => {
|
||||||
const handleCancel = () => (isOpen = false);
|
|
||||||
const handleTag = async (tagIds: string[]) => {
|
|
||||||
const assets = [...getOwnedAssets()];
|
const assets = [...getOwnedAssets()];
|
||||||
loading = true;
|
const success = await modalManager.show(AssetTagModal, { assetIds: assets.map(({ id }) => id) });
|
||||||
const ids = await tagAssets({ tagIds, assetIds: assets.map((asset) => asset.id) });
|
|
||||||
if (ids) {
|
if (success) {
|
||||||
clearSelect();
|
clearSelect();
|
||||||
}
|
}
|
||||||
loading = false;
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:document use:shortcut={{ shortcut: { key: 't' }, onShortcut: () => (isOpen = true) }} />
|
<svelte:document use:shortcut={{ shortcut: { key: 't' }, onShortcut: handleTagAssets }} />
|
||||||
|
|
||||||
{#if menuItem}
|
{#if menuItem}
|
||||||
<MenuOption {text} {icon} onClick={handleOpen} />
|
<MenuOption {text} {icon} onClick={handleTagAssets} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if !menuItem}
|
{#if !menuItem}
|
||||||
{#if loading}
|
<IconButton shape="round" color="secondary" variant="ghost" aria-label={text} {icon} onclick={handleTagAssets} />
|
||||||
<IconButton
|
|
||||||
shape="round"
|
|
||||||
color="secondary"
|
|
||||||
variant="ghost"
|
|
||||||
aria-label={$t('loading')}
|
|
||||||
icon={mdiTimerSand}
|
|
||||||
onclick={() => {}}
|
|
||||||
/>
|
|
||||||
{:else}
|
|
||||||
<IconButton shape="round" color="secondary" variant="ghost" aria-label={text} {icon} onclick={handleOpen} />
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if isOpen}
|
|
||||||
<TagAssetForm onTag={(tagIds) => handleTag(tagIds)} onCancel={handleCancel} />
|
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Icon from '$lib/components/elements/icon.svelte';
|
import Icon from '$lib/components/elements/icon.svelte';
|
||||||
|
import { tagAssets } from '$lib/utils/asset-utils';
|
||||||
import { getAllTags, upsertTags, type TagResponseDto } from '@immich/sdk';
|
import { getAllTags, upsertTags, type TagResponseDto } from '@immich/sdk';
|
||||||
import { Button, Modal, ModalBody, ModalFooter } from '@immich/ui';
|
import { Button, Modal, ModalBody, ModalFooter } from '@immich/ui';
|
||||||
import { mdiClose, mdiTag } from '@mdi/js';
|
import { mdiClose, mdiTag } from '@mdi/js';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { SvelteSet } from 'svelte/reactivity';
|
import { SvelteSet } from 'svelte/reactivity';
|
||||||
import Combobox, { type ComboBoxOption } from '../shared-components/combobox.svelte';
|
import Combobox, { type ComboBoxOption } from '../components/shared-components/combobox.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onTag: (tagIds: string[]) => void;
|
onClose: (success?: true) => void;
|
||||||
onCancel: () => void;
|
assetIds: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
let { onTag, onCancel }: Props = $props();
|
let { onClose, assetIds }: Props = $props();
|
||||||
|
|
||||||
let allTags: TagResponseDto[] = $state([]);
|
let allTags: TagResponseDto[] = $state([]);
|
||||||
let tagMap = $derived(Object.fromEntries(allTags.map((tag) => [tag.id, tag])));
|
let tagMap = $derived(Object.fromEntries(allTags.map((tag) => [tag.id, tag])));
|
||||||
@ -25,7 +26,10 @@
|
|||||||
allTags = await getAllTags();
|
allTags = await getAllTags();
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSubmit = () => onTag([...selectedIds]);
|
const handleSubmit = async () => {
|
||||||
|
await tagAssets({ tagIds: [...selectedIds], assetIds, showNotification: false });
|
||||||
|
onClose(true);
|
||||||
|
};
|
||||||
|
|
||||||
const handleSelect = async (option?: ComboBoxOption) => {
|
const handleSelect = async (option?: ComboBoxOption) => {
|
||||||
if (!option) {
|
if (!option) {
|
||||||
@ -45,13 +49,13 @@
|
|||||||
selectedIds.delete(tag);
|
selectedIds.delete(tag);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onsubmit = (event: Event) => {
|
const onsubmit = async (event: Event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
handleSubmit();
|
await handleSubmit();
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal size="small" title={$t('tag_assets')} icon={mdiTag} onClose={onCancel}>
|
<Modal size="small" title={$t('tag_assets')} icon={mdiTag} {onClose}>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<form {onsubmit} autocomplete="off" id="create-tag-form">
|
<form {onsubmit} autocomplete="off" id="create-tag-form">
|
||||||
<div class="my-4 flex flex-col gap-2">
|
<div class="my-4 flex flex-col gap-2">
|
||||||
@ -95,7 +99,7 @@
|
|||||||
|
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<div class="flex w-full gap-2">
|
<div class="flex w-full gap-2">
|
||||||
<Button shape="round" fullWidth color="secondary" onclick={onCancel}>{$t('cancel')}</Button>
|
<Button shape="round" fullWidth color="secondary" onclick={() => onClose()}>{$t('cancel')}</Button>
|
||||||
<Button type="submit" shape="round" fullWidth form="create-tag-form" {disabled}>{$t('tag_assets')}</Button>
|
<Button type="submit" shape="round" fullWidth form="create-tag-form" {disabled}>{$t('tag_assets')}</Button>
|
||||||
</div>
|
</div>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
Loading…
x
Reference in New Issue
Block a user