From 4a39ca14683dae1182cbc19db86c63530dbabbf4 Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Thu, 28 May 2026 20:38:25 -0400 Subject: [PATCH] refactor(sort): introduce build_tmp_sort utility; apply to event_presentation and journals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Creates src/lib/ae_core/core__idb_sort.ts with build_tmp_sort() — a shared helper for computing tmp_sort_1/2/3 fields stored in Dexie. Fixes two bugs present in all generic _process_generic_props implementations: - priority encoded as 0/1 ASC (true sorted last); now inverted: true→'0' - sort stored as unpadded string ("10" < "2"); now 8-char zero-padded Applied to: - ae_events__event_presentation: replaces inline specific_processor code - ae_journals__journal + ae_journals__journal_entry: replaces manual formulas; journal liveQueries (.reverse().sortBy()) updated to plain .sortBy() since the inverted encoding handles direction without needing Dexie's .reverse() Other modules (sessions, presenters, locations, posts, core) left unchanged until their sort behavior is reviewed separately. Co-Authored-By: Claude Sonnet 4.6 --- src/lib/ae_core/core__idb_sort.ts | 54 +++++++++++++++++++ .../ae_events__event_presentation.ts | 24 ++++----- src/lib/ae_journals/ae_journals__journal.ts | 14 +++-- .../ae_journals/ae_journals__journal_entry.ts | 24 +++++---- src/routes/journals/+page.svelte | 1 - .../entry/[journal_entry_id]/+page.svelte | 1 - 6 files changed, 89 insertions(+), 29 deletions(-) create mode 100644 src/lib/ae_core/core__idb_sort.ts 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