refactor: provider descriptor registry — eliminate provider-specific hardcoding
Replace all if/else chains keyed on provider type strings with a descriptor-driven architecture. Each provider type (immich, gitea, planka, scheduler, nut, google_photos) has a descriptor in frontend/src/lib/providers/ that declares config fields, event tracking fields, collection metadata, validation, and hooks. Components now use getDescriptor(type) and render dynamically. Dashboard provider card shows provider name + type when global filter is active. Grid-items derived from registry.
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
import { highlightFromUrl } from '$lib/highlight';
|
||||
import { providerDefaultIcon } from '$lib/grid-items';
|
||||
import { globalProviderFilter } from '$lib/stores/provider-filter.svelte';
|
||||
import { getDescriptor } from '$lib/providers';
|
||||
import type { Tracker, TrackerTarget, TrackingConfig, TemplateConfig, NotificationTarget } from '$lib/types';
|
||||
|
||||
import TrackerForm from './TrackerForm.svelte';
|
||||
@@ -153,33 +154,22 @@
|
||||
e.preventDefault(); error = '';
|
||||
if (submitting) return;
|
||||
|
||||
const newAlbumIds = form.collection_ids.filter(id => !previousCollectionIds.includes(id));
|
||||
if (newAlbumIds.length > 0 && form.provider_id && selectedProviderType === 'immich') {
|
||||
// Delegate provider-specific pre-save checks to the descriptor
|
||||
const desc = getDescriptor(selectedProviderType);
|
||||
if (desc?.onBeforeSave && form.provider_id) {
|
||||
linkCheckLoading = true;
|
||||
try {
|
||||
const missingAlbums: { id: string; name: string; issue: string }[] = [];
|
||||
for (const albumId of newAlbumIds) {
|
||||
interface SharedLink { is_accessible: boolean; is_expired: boolean; has_password: boolean }
|
||||
const links = await api<SharedLink[]>(`/providers/${form.provider_id}/albums/${albumId}/shared-links`);
|
||||
const validLink = links.find((l) => l.is_accessible && !l.is_expired);
|
||||
if (!validLink) {
|
||||
const album = collections.find(c => c.id === albumId);
|
||||
const problematicLink = links.find((l) => l.is_expired || l.has_password);
|
||||
missingAlbums.push({
|
||||
id: albumId,
|
||||
name: album?.albumName || album?.name || albumId,
|
||||
issue: problematicLink
|
||||
? (problematicLink.is_expired ? 'expired' : 'password-protected')
|
||||
: 'missing',
|
||||
});
|
||||
const result = await desc.onBeforeSave({
|
||||
form, previousCollectionIds, collections, api,
|
||||
});
|
||||
if (!result.proceed) {
|
||||
if (result.warnings?.length) {
|
||||
linkWarning = { albums: result.warnings, providerId: form.provider_id };
|
||||
}
|
||||
}
|
||||
if (missingAlbums.length > 0) {
|
||||
linkWarning = { albums: missingAlbums, providerId: form.provider_id };
|
||||
linkCheckLoading = false;
|
||||
return;
|
||||
}
|
||||
} catch (e) { console.warn('Shared link check failed, proceeding:', e); }
|
||||
} catch (err) { console.warn('Pre-save check failed, proceeding:', err); }
|
||||
linkCheckLoading = false;
|
||||
}
|
||||
|
||||
@@ -277,15 +267,10 @@
|
||||
return p?.name || `#${id}`;
|
||||
}
|
||||
|
||||
const collectionCountLabel: Record<string, string> = {
|
||||
immich: 'notificationTracker.albums_count',
|
||||
gitea: 'notificationTracker.repos_count',
|
||||
planka: 'notificationTracker.boards_count',
|
||||
nut: 'notificationTracker.devices_count',
|
||||
};
|
||||
function getCollectionLabel(tracker: Tracker): string {
|
||||
const pt = getProviderType(tracker);
|
||||
return t(collectionCountLabel[pt] || 'notificationTracker.albums_count');
|
||||
const desc = getDescriptor(pt);
|
||||
return desc?.collectionMeta ? t(desc.collectionMeta.countLabel) : t('notificationTracker.collections_count');
|
||||
}
|
||||
|
||||
function configsForTracker(tracker: Tracker, configs: (TrackingConfig | TemplateConfig)[]): (TrackingConfig | TemplateConfig)[] {
|
||||
|
||||
Reference in New Issue
Block a user