mirror of
https://github.com/immich-app/immich.git
synced 2025-12-08 17:23:14 +03:00
more refactor
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { formatLabel, getComponentFromSchema } from '$lib/utils/workflow';
|
||||
import { formatLabel, getComponentFromSchema, type ComponentConfig } from '$lib/utils/workflow';
|
||||
import { Field, Input, MultiSelect, Select, Switch, Text, type SelectItem } from '@immich/ui';
|
||||
import WorkflowPickerField from './WorkflowPickerField.svelte';
|
||||
|
||||
@@ -25,59 +25,78 @@
|
||||
config = configKey ? { ...config, [configKey]: { ...actualConfig, ...updates } } : { ...config, ...updates };
|
||||
};
|
||||
|
||||
// Helper to determine default value for a component based on its type
|
||||
const getDefaultValue = (component: ComponentConfig): unknown => {
|
||||
if (component.defaultValue !== undefined) {
|
||||
return component.defaultValue;
|
||||
}
|
||||
|
||||
// Initialize with appropriate empty value based on component type
|
||||
if (component.type === 'multiselect' || (component.type === 'text' && component.subType === 'people-picker')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (component.type === 'switch') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
// Derive which keys need initialization (missing from actualConfig)
|
||||
const uninitializedKeys = $derived.by(() => {
|
||||
if (!components) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Object.entries(components)
|
||||
.filter(([key]) => actualConfig[key] === undefined)
|
||||
.map(([key, component]) => ({ key, component, defaultValue: getDefaultValue(component) }));
|
||||
});
|
||||
|
||||
// Derive the batch updates needed
|
||||
const pendingUpdates = $derived.by(() => {
|
||||
const updates: Record<string, unknown> = {};
|
||||
for (const { key, defaultValue } of uninitializedKeys) {
|
||||
updates[key] = defaultValue;
|
||||
}
|
||||
return updates;
|
||||
});
|
||||
|
||||
let selectValue = $state<SelectItem>();
|
||||
let switchValue = $state<boolean>(false);
|
||||
let multiSelectValue = $state<SelectItem[]>([]);
|
||||
|
||||
// Initialize config namespace if needed
|
||||
$effect(() => {
|
||||
// Initialize config for actions/filters with empty schemas
|
||||
if (configKey && !config[configKey]) {
|
||||
config = { ...config, [configKey]: {} };
|
||||
}
|
||||
});
|
||||
|
||||
if (components) {
|
||||
const updates: Record<string, unknown> = {};
|
||||
// Apply pending config updates
|
||||
$effect(() => {
|
||||
if (Object.keys(pendingUpdates).length > 0) {
|
||||
updateConfigBatch(pendingUpdates);
|
||||
}
|
||||
});
|
||||
|
||||
for (const [key, component] of Object.entries(components)) {
|
||||
// Only initialize if the key doesn't exist in config yet
|
||||
if (actualConfig[key] === undefined) {
|
||||
// Use default value if available, otherwise use appropriate empty value based on type
|
||||
const hasDefault = component.defaultValue !== undefined;
|
||||
|
||||
if (hasDefault) {
|
||||
updates[key] = component.defaultValue;
|
||||
} else {
|
||||
// Initialize with appropriate empty value based on component type
|
||||
if (
|
||||
component.type === 'multiselect' ||
|
||||
(component.type === 'text' && component.subType === 'people-picker')
|
||||
) {
|
||||
updates[key] = [];
|
||||
} else if (component.type === 'switch') {
|
||||
updates[key] = false;
|
||||
} else {
|
||||
updates[key] = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Update UI state for components with default values
|
||||
if (hasDefault) {
|
||||
if (component.type === 'select') {
|
||||
selectValue = {
|
||||
label: formatLabel(String(component.defaultValue)),
|
||||
value: String(component.defaultValue),
|
||||
};
|
||||
}
|
||||
|
||||
if (component.type === 'switch') {
|
||||
switchValue = Boolean(component.defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sync UI state for components with default values
|
||||
$effect(() => {
|
||||
for (const { component } of uninitializedKeys) {
|
||||
if (component.defaultValue === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Object.keys(updates).length > 0) {
|
||||
updateConfigBatch(updates);
|
||||
if (component.type === 'select') {
|
||||
selectValue = {
|
||||
label: formatLabel(String(component.defaultValue)),
|
||||
value: String(component.defaultValue),
|
||||
};
|
||||
}
|
||||
|
||||
if (component.type === 'switch') {
|
||||
switchValue = Boolean(component.defaultValue);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
import WorkflowPickerItemCard from '$lib/components/workflows/WorkflowPickerItemCard.svelte';
|
||||
import AlbumPickerModal from '$lib/modals/AlbumPickerModal.svelte';
|
||||
import PeoplePickerModal from '$lib/modals/PeoplePickerModal.svelte';
|
||||
import { fetchPickerMetadata, type PickerMetadata } from '$lib/services/workflow.service';
|
||||
import type { ComponentConfig } from '$lib/utils/workflow';
|
||||
import { getAlbumInfo, getPerson, type AlbumResponseDto, type PersonResponseDto } from '@immich/sdk';
|
||||
import type { AlbumResponseDto, PersonResponseDto } from '@immich/sdk';
|
||||
import { Button, Field, modalManager } from '@immich/ui';
|
||||
import { mdiPlus } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
@@ -22,7 +23,7 @@
|
||||
const isAlbum = $derived(subType === 'album-picker');
|
||||
const multiple = $derived(component.type === 'multiselect' || Array.isArray(value));
|
||||
|
||||
let pickerMetadata = $state<AlbumResponseDto | PersonResponseDto | AlbumResponseDto[] | PersonResponseDto[]>();
|
||||
let pickerMetadata = $state<PickerMetadata | undefined>();
|
||||
|
||||
$effect(() => {
|
||||
if (!value) {
|
||||
@@ -30,34 +31,20 @@
|
||||
return;
|
||||
}
|
||||
|
||||
void fetchMetadata();
|
||||
if (!pickerMetadata) {
|
||||
void loadMetadata();
|
||||
}
|
||||
});
|
||||
|
||||
const fetchMetadata = async () => {
|
||||
if (!value || pickerMetadata) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (Array.isArray(value) && value.length > 0) {
|
||||
// Multiple selection
|
||||
pickerMetadata = await (isAlbum
|
||||
? Promise.all(value.map((id) => getAlbumInfo({ id })))
|
||||
: Promise.all(value.map((id) => getPerson({ id }))));
|
||||
} else if (typeof value === 'string' && value) {
|
||||
// Single selection
|
||||
pickerMetadata = await (isAlbum ? getAlbumInfo({ id: value }) : getPerson({ id: value }));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to fetch metadata for ${configKey}:`, error);
|
||||
}
|
||||
const loadMetadata = async () => {
|
||||
pickerMetadata = await fetchPickerMetadata(value, subType);
|
||||
};
|
||||
|
||||
const handlePicker = async () => {
|
||||
if (isAlbum) {
|
||||
const albums = await modalManager.show(AlbumPickerModal, { shared: false });
|
||||
if (albums && albums.length > 0) {
|
||||
const newValue = multiple ? albums.map((a) => a.id) : albums[0].id;
|
||||
const newValue = multiple ? albums.map((album) => album.id) : albums[0].id;
|
||||
onchange(newValue);
|
||||
pickerMetadata = multiple ? albums : albums[0];
|
||||
}
|
||||
@@ -66,7 +53,7 @@
|
||||
const excludedIds = multiple ? currentIds : [];
|
||||
const people = await modalManager.show(PeoplePickerModal, { multiple, excludedIds });
|
||||
if (people && people.length > 0) {
|
||||
const newValue = multiple ? people.map((p) => p.id) : people[0].id;
|
||||
const newValue = multiple ? people.map((person) => person.id) : people[0].id;
|
||||
onchange(newValue);
|
||||
pickerMetadata = multiple ? people : people[0];
|
||||
}
|
||||
|
||||
@@ -6,8 +6,12 @@ import { getFormatter } from '$lib/utils/i18n';
|
||||
import {
|
||||
createWorkflow,
|
||||
deleteWorkflow,
|
||||
getAlbumInfo,
|
||||
getPerson,
|
||||
PluginTriggerType,
|
||||
updateWorkflow,
|
||||
type AlbumResponseDto,
|
||||
type PersonResponseDto,
|
||||
type PluginActionResponseDto,
|
||||
type PluginContextType,
|
||||
type PluginFilterResponseDto,
|
||||
@@ -21,6 +25,9 @@ import { modalManager, toastManager, type ActionItem } from '@immich/ui';
|
||||
import { mdiCodeJson, mdiDelete, mdiPause, mdiPencil, mdiPlay } from '@mdi/js';
|
||||
import type { MessageFormatter } from 'svelte-i18n';
|
||||
|
||||
export type PickerSubType = 'album-picker' | 'people-picker';
|
||||
export type PickerMetadata = AlbumResponseDto | PersonResponseDto | AlbumResponseDto[] | PersonResponseDto[];
|
||||
|
||||
export interface WorkflowPayload {
|
||||
name: string;
|
||||
description: string;
|
||||
@@ -412,3 +419,30 @@ export const handleDeleteWorkflow = async (workflow: WorkflowResponseDto): Promi
|
||||
export const handleNavigateToWorkflow = async (workflow: WorkflowResponseDto): Promise<void> => {
|
||||
await goto(`${AppRoute.WORKFLOWS}/${workflow.id}`);
|
||||
};
|
||||
|
||||
export const fetchPickerMetadata = async (
|
||||
value: string | string[] | undefined,
|
||||
subType: PickerSubType,
|
||||
): Promise<PickerMetadata | undefined> => {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const isAlbum = subType === 'album-picker';
|
||||
|
||||
try {
|
||||
if (Array.isArray(value) && value.length > 0) {
|
||||
// Multiple selection
|
||||
return isAlbum
|
||||
? await Promise.all(value.map((id) => getAlbumInfo({ id })))
|
||||
: await Promise.all(value.map((id) => getPerson({ id })));
|
||||
} else if (typeof value === 'string' && value) {
|
||||
// Single selection
|
||||
return isAlbum ? await getAlbumInfo({ id: value }) : await getPerson({ id: value });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to fetch picker metadata:`, error);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user