mirror of
https://github.com/koloml/philomena-tagging-assistant.git
synced 2026-05-09 15:12:21 +00:00
Added Tag Presets, popup editor for them, implemented presets image edit
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
{/if}
|
||||
<MenuItem href="/features/profiles">Tagging Profiles</MenuItem>
|
||||
<MenuItem href="/features/groups">Tag Groups</MenuItem>
|
||||
<MenuItem href="/features/presets">Tag Presets</MenuItem>
|
||||
<hr>
|
||||
<MenuItem href="/transporting">Import/Export</MenuItem>
|
||||
<MenuItem href="/preferences">Preferences</MenuItem>
|
||||
|
||||
19
src/routes/features/presets/+page.svelte
Normal file
19
src/routes/features/presets/+page.svelte
Normal file
@@ -0,0 +1,19 @@
|
||||
<script lang="ts">
|
||||
import Menu from "$components/ui/menu/Menu.svelte";
|
||||
import MenuItem from "$components/ui/menu/MenuItem.svelte";
|
||||
import { tagEditorPresets } from "$stores/entities/tag-editor-presets";
|
||||
import { sortEntitiesByField } from "$lib/utils";
|
||||
|
||||
let presets = $derived(sortEntitiesByField($tagEditorPresets, "name"))
|
||||
</script>
|
||||
|
||||
<Menu>
|
||||
<MenuItem href="/" icon="arrow-left">Back</MenuItem>
|
||||
<MenuItem href="/features/presets/new/edit" icon="plus">Create New</MenuItem>
|
||||
{#if presets.length}
|
||||
<hr>
|
||||
{#each presets as preset}
|
||||
<MenuItem href="/features/presets/{preset.id}">{preset.settings.name}</MenuItem>
|
||||
{/each}
|
||||
{/if}
|
||||
</Menu>
|
||||
42
src/routes/features/presets/[id]/+page.svelte
Normal file
42
src/routes/features/presets/[id]/+page.svelte
Normal file
@@ -0,0 +1,42 @@
|
||||
<script lang="ts">
|
||||
import { page } from "$app/state";
|
||||
import TagEditorPreset from "$entities/TagEditorPreset";
|
||||
import { tagEditorPresets } from "$stores/entities/tag-editor-presets";
|
||||
import { goto } from "$app/navigation";
|
||||
import { popupTitle } from "$stores/popup";
|
||||
import Menu from "$components/ui/menu/Menu.svelte";
|
||||
import MenuItem from "$components/ui/menu/MenuItem.svelte";
|
||||
import PresetView from "$components/features/PresetView.svelte";
|
||||
|
||||
let presetId = $derived(page.params.id);
|
||||
let preset = $derived<TagEditorPreset|null>(
|
||||
$tagEditorPresets.find(preset => preset.id === presetId) || null
|
||||
);
|
||||
|
||||
$effect(() => {
|
||||
if (presetId === 'new') {
|
||||
goto(`/features/presets/new/edit`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!preset) {
|
||||
console.warn(`Preset ${presetId} not found.`);
|
||||
goto('/features/presets');
|
||||
} else {
|
||||
$popupTitle = `Preset: ${preset.settings.name}`;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<Menu>
|
||||
<MenuItem href="/features/presets" icon="arrow-left">Back</MenuItem>
|
||||
<hr>
|
||||
</Menu>
|
||||
{#if preset}
|
||||
<PresetView {preset}></PresetView>
|
||||
{/if}
|
||||
<Menu>
|
||||
<hr>
|
||||
<MenuItem href="/features/presets/{presetId}/edit" icon="wrench">Edit Preset</MenuItem>
|
||||
<MenuItem href="/features/presets/{presetId}/delete">Delete Preset</MenuItem>
|
||||
</Menu>
|
||||
74
src/routes/features/presets/[id]/edit/+page.svelte
Normal file
74
src/routes/features/presets/[id]/edit/+page.svelte
Normal file
@@ -0,0 +1,74 @@
|
||||
<script lang="ts">
|
||||
import { page } from "$app/state";
|
||||
import TagEditorPreset from "$entities/TagEditorPreset";
|
||||
import { tagEditorPresets } from "$stores/entities/tag-editor-presets";
|
||||
import { popupTitle } from "$stores/popup";
|
||||
import { goto } from "$app/navigation";
|
||||
import Menu from "$components/ui/menu/Menu.svelte";
|
||||
import MenuItem from "$components/ui/menu/MenuItem.svelte";
|
||||
import FormContainer from "$components/ui/forms/FormContainer.svelte";
|
||||
import FormControl from "$components/ui/forms/FormControl.svelte";
|
||||
import TextField from "$components/ui/forms/TextField.svelte";
|
||||
import TagsEditor from "$components/tags/TagsEditor.svelte";
|
||||
|
||||
let presetId = $derived(page.params.id);
|
||||
|
||||
let targetPreset = $derived.by<TagEditorPreset | null>(() => {
|
||||
if (presetId === 'new') {
|
||||
return new TagEditorPreset(crypto.randomUUID(), {});
|
||||
}
|
||||
|
||||
return $tagEditorPresets.find(preset => preset.id === presetId) || null;
|
||||
});
|
||||
|
||||
let presetName = $state('');
|
||||
let tagsList = $state<string[]>([]);
|
||||
|
||||
$effect(() => {
|
||||
if (presetId === 'new') {
|
||||
$popupTitle = 'Create New Preset';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!targetPreset) {
|
||||
goto('/features/presets');
|
||||
return;
|
||||
}
|
||||
|
||||
$popupTitle = `Edit Tagging Preset: ${targetPreset.settings.name}}`;
|
||||
|
||||
presetName = targetPreset.settings.name;
|
||||
tagsList = [...targetPreset.settings.tags].sort((a, b) => a.localeCompare(b));
|
||||
});
|
||||
|
||||
async function savePreset() {
|
||||
if (!targetPreset) {
|
||||
console.warn('Attempting to save the preset, but the preset is not loaded yet.');
|
||||
return;
|
||||
}
|
||||
|
||||
targetPreset.settings.name = presetName;
|
||||
targetPreset.settings.tags = [...tagsList];
|
||||
|
||||
await targetPreset.save();
|
||||
await goto(`/features/presets/${targetPreset.id}`);
|
||||
}
|
||||
</script>
|
||||
|
||||
<Menu>
|
||||
<MenuItem href="/features/presets{presetId === 'new' ? '' : '/' + presetId}" icon="arrow-left">
|
||||
Back
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<FormContainer>
|
||||
<FormControl label="Preset Name">
|
||||
<TextField bind:value={presetName} placeholder="Preset Name"></TextField>
|
||||
</FormControl>
|
||||
<FormControl label="Tags">
|
||||
<TagsEditor bind:tags={tagsList}></TagsEditor>
|
||||
</FormControl>
|
||||
</FormContainer>
|
||||
<Menu>
|
||||
<hr>
|
||||
<MenuItem href="#" onclick={savePreset}>Save Preset</MenuItem>
|
||||
</Menu>
|
||||
@@ -9,11 +9,13 @@
|
||||
import FormControl from "$components/ui/forms/FormControl.svelte";
|
||||
import FormContainer from "$components/ui/forms/FormContainer.svelte";
|
||||
import { popupTitle } from "$stores/popup";
|
||||
import { tagEditorPresets } from "$stores/entities/tag-editor-presets";
|
||||
|
||||
const bulkTransporter = new BulkEntitiesTransporter();
|
||||
|
||||
let exportAllProfiles = $state(false);
|
||||
let exportAllGroups = $state(false);
|
||||
let exportAllPresets = $state(false);
|
||||
|
||||
let displayExportedString = $state(false);
|
||||
let shouldUseCompressed = $state(true);
|
||||
@@ -24,6 +26,7 @@
|
||||
const exportedEntities: Record<keyof App.EntityNamesMap, Record<string, boolean>> = $state({
|
||||
profiles: {},
|
||||
groups: {},
|
||||
presets: {},
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
@@ -42,6 +45,12 @@
|
||||
}
|
||||
});
|
||||
|
||||
$tagEditorPresets.forEach(preset => {
|
||||
if (exportedEntities.presets[preset.id]) {
|
||||
elementsToExport.push(preset);
|
||||
}
|
||||
});
|
||||
|
||||
plainExport = bulkTransporter.exportToJSON(elementsToExport);
|
||||
compressedExport = bulkTransporter.exportToCompressedJSON(elementsToExport);
|
||||
}
|
||||
@@ -57,6 +66,7 @@
|
||||
requestAnimationFrame(() => {
|
||||
exportAllProfiles = $taggingProfiles.every(profile => exportedEntities.profiles[profile.id]);
|
||||
exportAllGroups = $tagGroups.every(group => exportedEntities.groups[group.id]);
|
||||
exportAllPresets = $tagEditorPresets.every(preset => exportedEntities.presets[preset.id]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -74,6 +84,9 @@
|
||||
case "groups":
|
||||
$tagGroups.forEach(group => exportedEntities.groups[group.id] = exportAllGroups);
|
||||
break;
|
||||
case "presets":
|
||||
$tagEditorPresets.forEach(preset => exportedEntities.presets[preset.id] = exportAllPresets);
|
||||
break;
|
||||
default:
|
||||
console.warn(`Trying to toggle unsupported entity type: ${targetEntity}`);
|
||||
}
|
||||
@@ -116,6 +129,17 @@
|
||||
{/each}
|
||||
<hr>
|
||||
{/if}
|
||||
{#if $tagEditorPresets.length}
|
||||
<MenuCheckboxItem bind:checked={exportAllPresets} oninput={createToggleAllOnUserInput('presets')}>
|
||||
Export All Presets
|
||||
</MenuCheckboxItem>
|
||||
{#each $tagEditorPresets as preset}
|
||||
<MenuCheckboxItem bind:checked={exportedEntities.presets[preset.id]} oninput={refreshAreAllEntitiesChecked}>
|
||||
Preset: {preset.settings.name}
|
||||
</MenuCheckboxItem>
|
||||
{/each}
|
||||
<hr>
|
||||
{/if}
|
||||
<MenuItem icon="file-export" onclick={toggleExportedStringDisplay}>Export Selected</MenuItem>
|
||||
</Menu>
|
||||
{:else}
|
||||
|
||||
@@ -16,21 +16,26 @@
|
||||
import type { SameSiteStatus } from "$lib/extension/EntitiesTransporter";
|
||||
import { popupTitle } from "$stores/popup";
|
||||
import Notice from "$components/ui/Notice.svelte";
|
||||
import TagEditorPreset from "$entities/TagEditorPreset";
|
||||
import { tagEditorPresets } from "$stores/entities/tag-editor-presets";
|
||||
|
||||
let importedString = $state('');
|
||||
let errorMessage = $state('');
|
||||
|
||||
let importedProfiles = $state<TaggingProfile[]>([]);
|
||||
let importedGroups = $state<TagGroup[]>([]);
|
||||
let importedPresets = $state<TagEditorPreset[]>([]);
|
||||
|
||||
let saveAllProfiles = $state(false);
|
||||
let saveAllGroups = $state(false);
|
||||
let saveAllPresets = $state(false);
|
||||
|
||||
let isSaving = $state(false);
|
||||
|
||||
let selectedEntities: Record<keyof App.EntityNamesMap, Record<string, boolean>> = $state({
|
||||
profiles: {},
|
||||
groups: {},
|
||||
presets: {},
|
||||
});
|
||||
|
||||
let previewedEntity = $state<StorageEntity | null>(null);
|
||||
@@ -49,8 +54,15 @@
|
||||
}, new Map<string, TagGroup>())
|
||||
);
|
||||
|
||||
const existingPresetsMap = $derived(
|
||||
$tagEditorPresets.reduce((map, preset) => {
|
||||
map.set(preset.id, preset);
|
||||
return map;
|
||||
}, new Map<string, TagEditorPreset>())
|
||||
);
|
||||
|
||||
const hasImportedEntities = $derived(
|
||||
Boolean(importedProfiles.length || importedGroups.length)
|
||||
Boolean(importedProfiles.length || importedGroups.length || importedPresets.length)
|
||||
);
|
||||
|
||||
$effect(() => {
|
||||
@@ -70,6 +82,7 @@
|
||||
function tryBulkImport() {
|
||||
importedProfiles = [];
|
||||
importedGroups = [];
|
||||
importedPresets = [];
|
||||
errorMessage = '';
|
||||
|
||||
importedString = importedString.trim();
|
||||
@@ -103,6 +116,9 @@
|
||||
case "groups":
|
||||
importedGroups.push(targetImportedEntity as TagGroup);
|
||||
break;
|
||||
case "presets":
|
||||
importedPresets.push(targetImportedEntity as TagEditorPreset);
|
||||
break;
|
||||
default:
|
||||
console.warn(`Unprocessed entity type detected: ${targetImportedEntity.type}`, targetImportedEntity);
|
||||
}
|
||||
@@ -115,12 +131,14 @@
|
||||
function cancelImport() {
|
||||
importedProfiles = [];
|
||||
importedGroups = [];
|
||||
importedPresets = [];
|
||||
}
|
||||
|
||||
function refreshAreAllEntitiesChecked() {
|
||||
requestAnimationFrame(() => {
|
||||
saveAllProfiles = importedProfiles.every(profile => selectedEntities.profiles[profile.id]);
|
||||
saveAllGroups = importedGroups.every(group => selectedEntities.groups[group.id]);
|
||||
saveAllPresets = importedPresets.every(preset => selectedEntities.presets[preset.id]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -134,6 +152,9 @@
|
||||
case "groups":
|
||||
importedGroups.forEach(group => selectedEntities.groups[group.id] = saveAllGroups);
|
||||
break;
|
||||
case "presets":
|
||||
importedPresets.forEach(preset => selectedEntities.presets[preset.id] = saveAllPresets);
|
||||
break;
|
||||
default:
|
||||
console.warn(`Trying to toggle unsupported entity type: ${entityType}`);
|
||||
}
|
||||
@@ -171,6 +192,14 @@
|
||||
await group.save();
|
||||
}
|
||||
|
||||
for (const preset of importedPresets) {
|
||||
if (!selectedEntities.presets[preset.id]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
await preset.save();
|
||||
}
|
||||
|
||||
await goto("/transporting");
|
||||
}
|
||||
</script>
|
||||
@@ -251,10 +280,7 @@
|
||||
{/if}
|
||||
{#if importedGroups.length}
|
||||
<hr>
|
||||
<MenuCheckboxItem
|
||||
bind:checked={saveAllGroups}
|
||||
oninput={createToggleAllOnUserInput('groups')}
|
||||
>
|
||||
<MenuCheckboxItem bind:checked={saveAllGroups} oninput={createToggleAllOnUserInput('groups')}>
|
||||
Import All Groups
|
||||
</MenuCheckboxItem>
|
||||
{#each importedGroups as candidateGroup}
|
||||
@@ -272,6 +298,26 @@
|
||||
</MenuCheckboxItem>
|
||||
{/each}
|
||||
{/if}
|
||||
{#if importedPresets.length}
|
||||
<hr>
|
||||
<MenuCheckboxItem bind:checked={saveAllPresets} oninput={createToggleAllOnUserInput('presets')}>
|
||||
Import All Presets
|
||||
</MenuCheckboxItem>
|
||||
{#each importedPresets as candidatePreset}
|
||||
<MenuCheckboxItem
|
||||
bind:checked={selectedEntities.presets[candidatePreset.id]}
|
||||
oninput={refreshAreAllEntitiesChecked}
|
||||
onitemclick={createShowPreviewForEntity(candidatePreset)}
|
||||
>
|
||||
{#if existingPresetsMap.has(candidatePreset.id)}
|
||||
Update:
|
||||
{:else}
|
||||
New:
|
||||
{/if}
|
||||
{candidatePreset.settings.name || 'Unnamed Preset'}
|
||||
</MenuCheckboxItem>
|
||||
{/each}
|
||||
{/if}
|
||||
<hr>
|
||||
<MenuItem onclick={saveSelectedEntities}>
|
||||
Imported Selected
|
||||
|
||||
Reference in New Issue
Block a user