All files / lib gallery.ts

0% Statements 0/49
0% Branches 0/37
0% Functions 0/9
0% Lines 0/40

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136                                                                                                                                                                                                                                                                               
import type { MetadataValues } from './database.js';
import { list, tables } from './idb.svelte.js';
import {
	compareByMetadataValue,
	metadataOptionsKeyRange,
	metadataValueGrouper
} from './metadata/index.js';
import { removeNamespaceFromMetadataId, splitMetadataId } from './schemas/metadata.js';
import type { GroupSettings, SortSettings } from './schemas/sessions.js';
import { getSettings } from './settings.svelte.js';
import { applySortDirection, compareBy, type Comparator } from './utils.js';
 
export type GalleryItem<AdditionalData> = {
	sessionId: string;
	id: string;
	name: string;
	addedAt: Date;
	virtual: boolean;
	metadata: MetadataValues;
	data: AdditionalData;
};
 
export async function galleryItemsSorter<D>(
	settings: typeof SortSettings.infer
): Promise<Comparator<GalleryItem<D>>> {
	const dir = (comparator: Comparator<GalleryItem<D>>) =>
		applySortDirection(settings.direction, comparator);
 
	switch (settings.field) {
		case 'id':
			return dir(compareBy('id'));
		case 'name':
			return dir(compareBy((item) => item.name));
		case 'metadataConfidence': {
			if (!settings.metadata) {
				throw new Error(
					'Tried to sort by metadata confidence without specifying metadata ID'
				);
			}
 
			return dir(compareBy((item) => item.metadata?.[settings.metadata!]?.confidence ?? 0));
		}
		case 'metadataValue': {
			if (!settings.metadata) {
				throw new Error('Tried to sort by metadata value without specifying metadata ID');
			}
 
			const metadataId = splitMetadataId(settings.metadata);
			if (!metadataId.namespace) {
				throw new Error(
					`Tried to sort by metadata value with non-namespaced metadata ID ${JSON.stringify(settings.metadata)}`
				);
			}
 
			return dir(
				compareByMetadataValue({
					metadata: await tables.Metadata.getOrThrow(settings.metadata),
					options: await list(
						'MetadataOption',
						metadataOptionsKeyRange(metadataId.namespace, metadataId.id)
					)
				})
			);
		}
	}
}
 
/**
 *
 * @returns Either null (no grouping) or a function that takes a gallery item and returns [key to sort the groups on, group label]
 */
export async function galleryItemsGrouper<D>(
	settings: typeof GroupSettings.infer
): Promise<null | ((item: GalleryItem<D>) => [number | string, string])> {
	if (settings.field === 'none') return null;
 
	if (!settings.metadata) {
		throw new Error(`tried to group by ${settings.field} without specifying metadata ID`);
	}
 
	const metadata = await tables.Metadata.getOrThrow(settings.metadata);
	const label = metadata.label || removeNamespaceFromMetadataId(metadata.id);
 
	switch (settings.field) {
		case 'metadataPresence': {
			return (item) => {
				const has = item.metadata[settings.metadata!] !== undefined;
 
				return has ? [0, `Avec ${label}`] : [1, `Sans ${label}`];
			};
		}
 
		case 'metadataConfidence': {
			return (item) => {
				const confidence = item.metadata[settings.metadata!]?.confidence;
 
				if (confidence >= 0.75) return [0, `${label}: confiance à 75%-100%`];
				if (confidence >= 0.5) return [1, `${label}: confiance à 50%-75%`];
				if (confidence >= 0.25) return [2, `${label}: confiance à 25%-50%`];
				if (confidence !== undefined) return [3, `${label}: confiance à 0%-25%`];
				else return [4, `Sans ${label}`];
			};
		}
 
		case 'metadataValue': {
			const metadataId = splitMetadataId(settings.metadata);
			if (!metadataId.namespace) {
				throw new Error(
					`Tried to group by metadata value with non-namespaced metadata ID ${JSON.stringify(settings.metadata)}`
				);
			}
 
			const options = await list(
				'MetadataOption',
				metadataOptionsKeyRange(metadataId.namespace, metadataId.id)
			);
 
			const grouper = metadataValueGrouper({
				type: metadata.type,
				language: getSettings().language,
				options
			});
 
			return (item) => {
				const value = item.metadata[settings.metadata!]?.value;
				if (value === undefined) return [options.length, `Sans ${label}`];
				const group = grouper(value);
				return [
					options.find((opt) => opt.key === value)?.index ?? group,
					`${label} = ${group}`
				];
			};
		}
	}
}