mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 16:13:15 +00:00
In the unified view each row can be shaded with its account colour instead of carrying an account dot, behind a layout setting. Tags keep their own generated hue on their chips.
75 lines
3.1 KiB
TypeScript
75 lines
3.1 KiB
TypeScript
"use client";
|
|
|
|
import { useMemo } from "react";
|
|
import {
|
|
useSettingsStore,
|
|
KEYWORD_PALETTE,
|
|
FALLBACK_KEYWORD_COLOR,
|
|
type KeywordColor,
|
|
} from "@/stores/settings-store";
|
|
import { formatKeyword, formatKeywordLabels, keywordRenderings } from "@/lib/keyword-format";
|
|
import { suggestKeywordColor } from "@/lib/keyword-discovery";
|
|
|
|
/**
|
|
* Names and colours tags for the screen, bound to the user's tag settings.
|
|
*
|
|
* Resolving the definitions and the nesting setting here rather than at every
|
|
* call site means no caller can forget the setting and render a nested name to
|
|
* someone who never asked for nesting. Subscribing to them also keeps tags in
|
|
* step the moment either changes: reading the store inside the formatter would
|
|
* leave every list stale until something else happened to re-render it.
|
|
*/
|
|
export function useKeywordFormat() {
|
|
const keywords = useSettingsStore((state) => state.emailKeywords);
|
|
const nested = useSettingsStore((state) => state.nestedTags);
|
|
|
|
return useMemo(
|
|
() => ({
|
|
/** The tag's display name. */
|
|
tagName: (id: string) => formatKeyword(id, keywords, nested),
|
|
|
|
/** Its progressively shorter forms, longest first, for `useShortenedText`. */
|
|
tagNameCandidates: (id: string) => keywordRenderings(formatKeywordLabels(id, keywords, nested)),
|
|
|
|
/**
|
|
* The tag's colour.
|
|
*
|
|
* A keyword this client has no definition for - one created on another
|
|
* device, applied by a server-side filter, or whose tag was deleted -
|
|
* still gets a hue of its own, derived from its id by the same function
|
|
* that proposes colours for recovered tags. One shared grey made every
|
|
* such tag look alike, which is exactly when telling them apart matters:
|
|
* a mailbox filling up with server-assigned tags would show a single
|
|
* undifferentiated colour. The hue is a pure function of the id, so it is
|
|
* stable across reloads and devices, and it degrades to grey only when
|
|
* the proposal names a colour the palette does not hold.
|
|
*/
|
|
tagColor: (id: string): KeywordColor => {
|
|
const color = keywords.find((keyword) => keyword.id === id)?.color;
|
|
if (color && KEYWORD_PALETTE[color]) return KEYWORD_PALETTE[color];
|
|
return KEYWORD_PALETTE[suggestKeywordColor(id)] ?? KEYWORD_PALETTE[FALLBACK_KEYWORD_COLOR];
|
|
},
|
|
|
|
/**
|
|
* Tag ids in the order the user arranged them in settings.
|
|
*
|
|
* The keywords on a message arrive as an unordered JMAP map, so without
|
|
* this the same two tags can swap places between rows. A tag with no
|
|
* local definition has no place in that order, so it sorts last, by name.
|
|
*/
|
|
sortTagIds: (ids: string[]): string[] => {
|
|
const rank = (id: string) => {
|
|
const index = keywords.findIndex((keyword) => keyword.id === id);
|
|
return index === -1 ? keywords.length : index;
|
|
};
|
|
return [...ids].sort(
|
|
(a, b) =>
|
|
rank(a) - rank(b) ||
|
|
formatKeyword(a, keywords, nested).localeCompare(formatKeyword(b, keywords, nested)),
|
|
);
|
|
},
|
|
}),
|
|
[keywords, nested],
|
|
);
|
|
}
|