fix(collab): a note category shows the same colour on the phone as on desktop

When the notes of one category carried two colours (a category recolour
that failed halfway, or a note written over the API), the desktop panel
painted the category in the colour of the last such note in the list
while the phone notes tab used the first one, so the same category had
different colours on the two devices.

The phone's buildCategoryColorMap took the first colour on the stated
assumption that every note in a category has the same colour, so first
seen equals all seen; it was meant to match the desktop and only
differed where that assumption breaks. The desktop rule (since v2.6.0)
is the original. buildCategoryColorMap now keeps the last colour seen
and the desktop panel builds its map from the same function, merged
over its empty categories as before.

Red before the fix: tests/unit/mobile/trip/collabModel.test.ts
FE-MOB-CLBM-023 failed with "expected { Ideas: '#ef4444' } to deeply
equal { Ideas: '#10b981' }".

Changed assertion in tests/unit/mobile/trip/collabModel.test.ts
FE-MOB-CLBM-011 (renamed from "keeps the first colour seen per
category" to "keeps the last colour seen per category"): the expected
map was { Ideas: '#ef4444' } and is now { Ideas: '#10b981' }.
This commit is contained in:
Maurice
2026-10-09 17:49:49 +02:00
parent d118423570
commit 6c916cc257
3 changed files with 24 additions and 14 deletions
+5 -7
View File
@@ -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
+8 -5
View File
@@ -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<string, string> {
/** 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<string, string> {
const map: Record<string, string> = {};
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;
}
@@ -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', () => {