diff --git a/client/src/components/Collab/CollabNotes.tsx b/client/src/components/Collab/CollabNotes.tsx index 95e540576..1eb336f2a 100644 --- a/client/src/components/Collab/CollabNotes.tsx +++ b/client/src/components/Collab/CollabNotes.tsx @@ -18,6 +18,7 @@ import type { User } from '../../types' import type { CollabNote } from './CollabNotes.types' import { announceNoteFilesChanged, useCollabNotesData } from './useCollabNotesData' import { FONT, NOTE_COLORS } from './CollabNotes.constants' +import { buildCategoryColorMap } from './collabModel' import { NoteFormModal } from './CollabNotesFormModal' import { CategorySettingsModal } from './CollabNotesCategorySettingsModal' import { NoteCard } from './CollabNotesCard' @@ -68,13 +69,10 @@ function useCollabNotes({ tripId, currentUser }: CollabNotesProps) { } // Category colors: from notes first, then from empty categories - const categoryColors = useMemo(() => { - const map = { ...emptyCategories } - for (const n of notes) { - if (n.category && n.color) map[n.category] = n.color - } - return map - }, [notes, emptyCategories]) + const categoryColors = useMemo( + () => ({ ...emptyCategories, ...buildCategoryColorMap(notes) }), + [notes, emptyCategories] + ) const getCategoryColor = (cat) => { if (!cat) return NOTE_COLORS[0].value diff --git a/client/src/components/Collab/collabModel.ts b/client/src/components/Collab/collabModel.ts index 59c2b6b3e..f58d2c2c4 100644 --- a/client/src/components/Collab/collabModel.ts +++ b/client/src/components/Collab/collabModel.ts @@ -150,13 +150,16 @@ export interface CollabNoteData { /** Category swatch palette, the same 6 hex values the desktop picker uses. */ export const NOTE_COLORS = ['#6366f1', '#ef4444', '#f59e0b', '#10b981', '#3b82f6', '#8b5cf6']; -/** One color per category, taken from whatever any note in that category - * already has stored (every note in a category is written with the same - * color at create time, so first-seen === all-seen in practice). */ -export function buildCategoryColorMap(notes: CollabNoteData[]): Record { +/** One color per category, taken from the notes in it, for the desktop panel and the + * phone tab alike. Every note in a category is normally written with the same color, + * but a recolour that failed halfway or a note written over the API can leave two; + * then the last one in list order wins. */ +export function buildCategoryColorMap( + notes: readonly { category?: string | null; color?: string | null }[] +): Record { const map: Record = {}; for (const n of notes) { - if (n.category && n.color && !map[n.category]) map[n.category] = n.color; + if (n.category && n.color) map[n.category] = n.color; } return map; } diff --git a/client/tests/unit/mobile/trip/collabModel.test.ts b/client/tests/unit/mobile/trip/collabModel.test.ts index e676256df..4ea2f72c6 100644 --- a/client/tests/unit/mobile/trip/collabModel.test.ts +++ b/client/tests/unit/mobile/trip/collabModel.test.ts @@ -179,14 +179,23 @@ describe('collabModel — chat', () => { }); describe('collabModel — notes', () => { - it('FE-MOB-CLBM-011: buildCategoryColorMap keeps the first colour seen per category', () => { + it('FE-MOB-CLBM-011: buildCategoryColorMap keeps the last colour seen per category', () => { const notes = [ note({ id: 1, category: 'Ideas', color: '#ef4444' }), note({ id: 2, category: 'Ideas', color: '#10b981' }), note({ id: 3, category: 'Food', color: null }), note({ id: 4, category: null, color: '#3b82f6' }), ]; - expect(buildCategoryColorMap(notes)).toEqual({ Ideas: '#ef4444' }); + expect(buildCategoryColorMap(notes)).toEqual({ Ideas: '#10b981' }); + }); + + it('FE-MOB-CLBM-023: a category whose notes disagree takes the colour the desktop panel shows', () => { + // A category recolour that failed halfway leaves its notes on two colours. + const notes = [ + note({ id: 1, category: 'Ideas', color: '#ef4444' }), + note({ id: 2, category: 'Ideas', color: '#10b981' }), + ]; + expect(buildCategoryColorMap(notes)).toEqual({ Ideas: '#10b981' }); }); it('FE-MOB-CLBM-012: getCategoryColor round-robins fresh categories onto the palette', () => {