1
0
mirror of https://github.com/koloml/furbooru-tagging-assistant.git synced 2025-12-24 07:12:57 +00:00

Added separate menu for bulk exporting of all entities

This commit is contained in:
2025-07-27 19:02:19 +04:00
parent fcca26e128
commit b956b6f7bc
5 changed files with 171 additions and 5 deletions

View File

@@ -1,5 +1,5 @@
import type StorageEntity from "$lib/extension/base/StorageEntity";
import { decompressFromEncodedURIComponent } from "lz-string";
import { compressToEncodedURIComponent, decompressFromEncodedURIComponent } from "lz-string";
import type { ImportableElementsList, ImportableEntityObject } from "$lib/extension/transporting/importables";
import EntitiesTransporter from "$lib/extension/EntitiesTransporter";
import MaintenanceProfile from "$entities/MaintenanceProfile";
@@ -42,6 +42,30 @@ export default class BulkEntitiesTransporter {
);
}
exportToJSON(entities: StorageEntity[]): string {
return JSON.stringify({
$type: 'list',
elements: entities
.map(entity => {
switch (true) {
case entity instanceof MaintenanceProfile:
return BulkEntitiesTransporter.#transporters.profiles.exportToObject(entity);
case entity instanceof TagGroup:
return BulkEntitiesTransporter.#transporters.groups.exportToObject(entity);
}
return null;
})
.filter(value => !!value)
} as ImportableElementsList<ImportableEntityObject<StorageEntity>>, null, 2);
}
exportToCompressedJSON(entities: StorageEntity[]): string {
return compressToEncodedURIComponent(
this.exportToJSON(entities)
);
}
static isList(targetObject: any): targetObject is ImportableElementsList<ImportableEntityObject<StorageEntity>> {
return targetObject.$type
&& targetObject.$type === 'list'

View File

@@ -32,6 +32,10 @@ export default class EntitiesTransporter<EntityType> {
this.#targetEntityConstructor = entityConstructor;
}
isCorrectEntity(entityObject: unknown): entityObject is EntityType {
return entityObject instanceof this.#targetEntityConstructor;
}
importFromObject(importedObject: Record<string, any>): EntityType {
validateImportedEntity(
this.#entityName,
@@ -60,8 +64,8 @@ export default class EntitiesTransporter<EntityType> {
)
}
exportToJSON(entityObject: EntityType): string {
if (!(entityObject instanceof this.#targetEntityConstructor)) {
exportToObject(entityObject: EntityType) {
if (!this.isCorrectEntity(entityObject)) {
throw new TypeError('Transporter should be connected to the same entity to export!');
}
@@ -69,12 +73,18 @@ export default class EntitiesTransporter<EntityType> {
throw new TypeError('Only storage entities could be exported!');
}
const exportableObject = exportEntityToObject(
return exportEntityToObject(
this.#entityName,
entityObject
);
}
return JSON.stringify(exportableObject, null, 2);
exportToJSON(entityObject: EntityType): string {
return JSON.stringify(
this.exportToObject(entityObject),
null,
2
);
}
exportToCompressedJSON(entityObject: EntityType): string {

View File

@@ -24,6 +24,7 @@
<MenuItem href="/features/maintenance">Tagging Profiles</MenuItem>
<MenuItem href="/features/groups">Tag Groups</MenuItem>
<hr>
<MenuItem href="/transporting">Import/Export</MenuItem>
<MenuItem href="/preferences">Preferences</MenuItem>
<MenuItem href="/about">About</MenuItem>
</Menu>

View File

@@ -0,0 +1,10 @@
<script lang="ts">
import Menu from "$components/ui/menu/Menu.svelte";
import MenuItem from "$components/ui/menu/MenuItem.svelte";
</script>
<Menu>
<MenuItem href="/" icon="arrow-left">Back</MenuItem>
<hr>
<MenuItem href="/transporting/export">Export</MenuItem>
</Menu>

View File

@@ -0,0 +1,121 @@
<script lang="ts">
import Menu from "$components/ui/menu/Menu.svelte";
import MenuItem from "$components/ui/menu/MenuItem.svelte";
import { maintenanceProfiles } from "$stores/entities/maintenance-profiles";
import MenuCheckboxItem from "$components/ui/menu/MenuCheckboxItem.svelte";
import { tagGroups } from "$stores/entities/tag-groups";
import BulkEntitiesTransporter from "$lib/extension/BulkEntitiesTransporter";
import type StorageEntity from "$lib/extension/base/StorageEntity";
import FormControl from "$components/ui/forms/FormControl.svelte";
import FormContainer from "$components/ui/forms/FormContainer.svelte";
const bulkTransporter = new BulkEntitiesTransporter();
let exportAllProfiles = $state(false);
let exportAllGroups = $state(false);
let displayExportedString = $state(false);
let shouldUseCompressed = $state(true);
let compressedExport = $state('');
let plainExport = $state('');
let selectedExportString = $derived(shouldUseCompressed ? compressedExport : plainExport);
const exportedEntities: Record<keyof App.EntityNamesMap, Record<string, boolean>> = $state({
profiles: {},
groups: {},
});
$effect(() => {
if (displayExportedString) {
const elementsToExport: StorageEntity[] = [];
$maintenanceProfiles.forEach(profile => {
if (exportedEntities.profiles[profile.id]) {
elementsToExport.push(profile);
}
});
$tagGroups.forEach(group => {
if (exportedEntities.groups[group.id]) {
elementsToExport.push(group);
}
});
plainExport = bulkTransporter.exportToJSON(elementsToExport);
compressedExport = bulkTransporter.exportToCompressedJSON(elementsToExport);
}
});
function refreshAreAllEntitiesChecked() {
requestAnimationFrame(() => {
exportAllProfiles = $maintenanceProfiles.every(profile => exportedEntities.profiles[profile.id]);
exportAllGroups = $tagGroups.every(group => exportedEntities.groups[group.id]);
});
}
function toggleSelectionOnUserInput() {
requestAnimationFrame(() => {
$maintenanceProfiles.forEach(profile => exportedEntities.profiles[profile.id] = exportAllProfiles);
$tagGroups.forEach(group => exportedEntities.groups[group.id] = exportAllGroups);
});
}
function toggleExportedStringDisplay() {
displayExportedString = !displayExportedString;
}
function toggleExportedStringType() {
shouldUseCompressed = !shouldUseCompressed;
}
</script>
{#if !displayExportedString}
<Menu>
<MenuItem href="/transporting" icon="arrow-left">Back</MenuItem>
<hr>
{#if $maintenanceProfiles.length}
<MenuCheckboxItem bind:checked={exportAllProfiles} oninput={toggleSelectionOnUserInput}>
Export All Profiles
</MenuCheckboxItem>
{#each $maintenanceProfiles as profile}
<MenuCheckboxItem bind:checked={exportedEntities.profiles[profile.id]} oninput={refreshAreAllEntitiesChecked}>
Profile: {profile.settings.name}
</MenuCheckboxItem>
{/each}
<hr>
{/if}
{#if $tagGroups.length}
<MenuCheckboxItem bind:checked={exportAllGroups} oninput={toggleSelectionOnUserInput}>
Export All Groups
</MenuCheckboxItem>
{#each $tagGroups as group}
<MenuCheckboxItem bind:checked={exportedEntities.groups[group.id]} oninput={refreshAreAllEntitiesChecked}>
Group: {group.settings.name}
</MenuCheckboxItem>
{/each}
<hr>
{/if}
<MenuItem icon="file-export" onclick={toggleExportedStringDisplay}>Export Selected</MenuItem>
</Menu>
{:else}
<Menu>
<MenuItem onclick={toggleExportedStringDisplay} icon="arrow-left">Back to Selection</MenuItem>
<hr>
</Menu>
<FormContainer>
<FormControl label="Export string">
<textarea readonly rows="6">{selectedExportString}</textarea>
</FormControl>
</FormContainer>
<Menu>
<hr>
<MenuItem onclick={toggleExportedStringType}>
Switch Format:
{#if shouldUseCompressed}
Base64-Encoded
{:else}
Raw JSON
{/if}
</MenuItem>
</Menu>
{/if}