diff --git a/src/lib/ae_core/core__idb_sort.ts b/src/lib/ae_core/core__idb_sort.ts new file mode 100644 index 00000000..779cf3ee --- /dev/null +++ b/src/lib/ae_core/core__idb_sort.ts @@ -0,0 +1,54 @@ +/** + * src/lib/ae_core/core__idb_sort.ts + * + * Shared utility for computing tmp_sort_* fields stored in Dexie. + * All fields are designed for ascending .sortBy() — no .reverse() needed. + * + * Encoding rules: + * priority — inverted boolean: true→'0', false→'1' so priority=true sorts first (ASC) + * sort — zero-padded integer string so "00000010" < "00000020" (correct numeric order) + * all other fields — appended as-is; ISO 8601 datetimes already sort correctly + * + * Usage: + * const { tmp_sort_1, tmp_sort_2, tmp_sort_3 } = build_tmp_sort({ + * prefix: [obj.group ?? '0'], // fields before priority (optional) + * priority: obj.priority, + * sort: obj.sort, + * fields_1: [obj.start_datetime], // appended to base for tmp_sort_1 + * fields_2: [obj.name], // appended after fields_1 for tmp_sort_2 + * fields_3: [obj.updated_on], // appended after fields_2 for tmp_sort_3 + * }); + */ +export function build_tmp_sort({ + prefix = [], + priority, + sort, + fields_1 = [], + fields_2 = [], + fields_3 = [], + pad_width = 8 +}: { + prefix?: (string | null | undefined)[]; + priority?: boolean | null; + sort?: number | string | null; + fields_1?: (string | null | undefined)[]; + fields_2?: (string | null | undefined)[]; + fields_3?: (string | null | undefined)[]; + pad_width?: number; +}): { tmp_sort_1: string; tmp_sort_2: string; tmp_sort_3: string } { + const clean = (v: string | null | undefined): string => v ?? ''; + + const p = priority ? '0' : '1'; + const s = String(Number(sort ?? 0)).padStart(pad_width, '0'); + + const parts_base = [...prefix.map(clean), p, s].join('_'); + const parts_1 = fields_1.map(clean).filter(Boolean).join('_'); + const parts_2 = fields_2.map(clean).filter(Boolean).join('_'); + const parts_3 = fields_3.map(clean).filter(Boolean).join('_'); + + const tmp_sort_1 = [parts_base, parts_1].filter(Boolean).join('_'); + const tmp_sort_2 = [tmp_sort_1, parts_2].filter(Boolean).join('_'); + const tmp_sort_3 = [tmp_sort_2, parts_3].filter(Boolean).join('_'); + + return { tmp_sort_1, tmp_sort_2, tmp_sort_3 }; +} diff --git a/src/lib/ae_events/ae_events__event_presentation.ts b/src/lib/ae_events/ae_events__event_presentation.ts index 4f6c7b7d..73a2acf7 100644 --- a/src/lib/ae_events/ae_events__event_presentation.ts +++ b/src/lib/ae_events/ae_events__event_presentation.ts @@ -2,6 +2,7 @@ import type { key_val } from '$lib/stores/ae_stores'; import { api } from '$lib/api/api'; import { db_save_ae_obj_li__ae_obj } from '$lib/ae_core/core__idb_dexie'; +import { build_tmp_sort } from '$lib/ae_core/core__idb_sort'; import { db_events } from '$lib/ae_events/db_events'; import type { ae_EventPresentation } from '$lib/types/ae_types'; @@ -681,19 +682,16 @@ export async function process_ae_obj__event_presentation_props({ obj.event_session_id = obj.event_session_id_random; if (obj.event_id_random) obj.event_id = obj.event_id_random; - // Override generic tmp_sort_* with presentation-specific encoding. - // WHY: Generic _process_generic_props encodes priority as 0/1 ASC (backwards) - // and sort as an unpadded string ("10" < "2"). Presentations also need - // start_datetime and code in the sort chain, which are not generic fields. - // Desired order: priority DESC → sort ASC → start_datetime ASC → code ASC → name ASC - // Encoding trick: invert priority so true→'0' sorts before false→'1' ascending. - const p = obj.priority ? '0' : '1'; - const s = String(Number(obj.sort ?? 0)).padStart(8, '0'); - const dt = obj.start_datetime ?? ''; - const code = obj.code ?? ''; - const name = obj.name ?? ''; - obj.tmp_sort_1 = `${p}_${s}_${dt}_${code}`; - obj.tmp_sort_2 = `${p}_${s}_${dt}_${code}_${name}`; + // Override generic tmp_sort_* with presentation-specific encoding via + // build_tmp_sort. Order: priority DESC → sort ASC → start_datetime ASC → code ASC → name ASC + const { tmp_sort_1, tmp_sort_2 } = build_tmp_sort({ + priority: obj.priority, + sort: obj.sort, + fields_1: [obj.start_datetime, obj.code], + fields_2: [obj.name] + }); + obj.tmp_sort_1 = tmp_sort_1; + obj.tmp_sort_2 = tmp_sort_2; return obj; } }); diff --git a/src/lib/ae_journals/ae_journals__journal.ts b/src/lib/ae_journals/ae_journals__journal.ts index ddee6337..523b5be7 100644 --- a/src/lib/ae_journals/ae_journals__journal.ts +++ b/src/lib/ae_journals/ae_journals__journal.ts @@ -4,6 +4,7 @@ import type { key_val } from '$lib/stores/ae_stores'; import { api } from '$lib/api/api'; import { db_save_ae_obj_li__ae_obj } from '$lib/ae_core/core__idb_dexie'; +import { build_tmp_sort } from '$lib/ae_core/core__idb_sort'; import { db_journals } from '$lib/ae_journals/db_journals'; import type { ae_Journal } from '$lib/types/ae_types'; @@ -885,9 +886,16 @@ export async function process_ae_obj__journal_props({ const updated = obj.updated_on ?? obj.created_on ?? new Date(0).toISOString(); - obj.tmp_sort_3 = `${obj.group ?? '0'}_${obj.priority ? 1 : 0}_${obj.sort ?? '0'}_${ - obj.name - }_${updated}`; + const { tmp_sort_1, tmp_sort_2, tmp_sort_3 } = build_tmp_sort({ + prefix: [obj.group ?? '0'], + priority: obj.priority, + sort: obj.sort, + fields_2: [obj.name], + fields_3: [updated] + }); + obj.tmp_sort_1 = tmp_sort_1; + obj.tmp_sort_2 = tmp_sort_2; + obj.tmp_sort_3 = tmp_sort_3; obj.combined_passcode = `${obj.passcode ?? ''}:${obj.private_passcode ?? ''}`; return obj; diff --git a/src/lib/ae_journals/ae_journals__journal_entry.ts b/src/lib/ae_journals/ae_journals__journal_entry.ts index c86d0064..c70513ad 100644 --- a/src/lib/ae_journals/ae_journals__journal_entry.ts +++ b/src/lib/ae_journals/ae_journals__journal_entry.ts @@ -4,6 +4,7 @@ import type { key_val } from '$lib/stores/ae_stores'; import { api } from '$lib/api/api'; import { db_save_ae_obj_li__ae_obj } from '$lib/ae_core/core__idb_dexie'; +import { build_tmp_sort } from '$lib/ae_core/core__idb_sort'; import { db_journals } from '$lib/ae_journals/db_journals'; import type { ae_JournalEntry } from '$lib/types/ae_types'; @@ -1050,19 +1051,20 @@ export async function process_ae_obj__journal_entry_props({ obj.history = history; obj.history_md_html = history_md_html; - // Journal entry-specific computed sort fields, overriding generic ones if needed - const sort_val = (obj.sort ?? 0).toString().padStart(3, '0'); + // Journal entry-specific computed sort fields via build_tmp_sort. + // Order: priority DESC → sort ASC → name ASC → updated ASC (all ascending, no .reverse()) const updated = obj.updated_on ?? obj.created_on ?? new Date(0).toISOString(); - obj.tmp_sort_1 = `${obj.group ?? ''}_${obj.priority ? '1' : '0'}_${ - sort_val - }_${updated}`; - obj.tmp_sort_2 = `${obj.group ?? ''}_${obj.priority ? '1' : '0'}_${ - sort_val - }_${obj.name ?? ''}_${updated}`; - obj.tmp_sort_3 = `${obj.group ?? ''}_${obj.priority ? '1' : '0'}_${ - sort_val - }_${obj.name ?? ''}_${updated}`; + const { tmp_sort_1, tmp_sort_2, tmp_sort_3 } = build_tmp_sort({ + prefix: [obj.group ?? ''], + priority: obj.priority, + sort: obj.sort, + fields_2: [obj.name], + fields_3: [updated] + }); + obj.tmp_sort_1 = tmp_sort_1; + obj.tmp_sort_2 = tmp_sort_2; + obj.tmp_sort_3 = tmp_sort_3; return obj; } diff --git a/src/routes/journals/+page.svelte b/src/routes/journals/+page.svelte index 7f9f894e..e06c4d80 100644 --- a/src/routes/journals/+page.svelte +++ b/src/routes/journals/+page.svelte @@ -59,7 +59,6 @@ let lq__journal_obj_li = $derived( return await db_journals.journal .where('person_id') .equals($ae_loc.person_id) - .reverse() .sortBy('tmp_sort_3'); }) ); diff --git a/src/routes/journals/[journal_id]/entry/[journal_entry_id]/+page.svelte b/src/routes/journals/[journal_id]/entry/[journal_entry_id]/+page.svelte index a94120da..171ef9ba 100644 --- a/src/routes/journals/[journal_id]/entry/[journal_entry_id]/+page.svelte +++ b/src/routes/journals/[journal_id]/entry/[journal_entry_id]/+page.svelte @@ -105,7 +105,6 @@ let lq__journal_obj_li = $derived( let results = await db_journals.journal .where('person_id') .equals($ae_loc.person_id) - .reverse() .sortBy('tmp_sort_2'); // Check if results are different than the current session version stored under $journals_slct