feat: implement email keyword management with color tagging

- Introduced a new settings section for managing email keywords (labels/tags) with associated colors.
- Updated email context menu, viewer, and list item components to support the new keyword system.
- Refactored color tag handling to accommodate both new $label: and legacy $color: prefixes.
- Added utility functions for retrieving email color tags based on keywords.
- Enhanced localization files to include translations for the new keyword management features.
- Updated the settings store to manage keyword definitions, including add, update, delete, and reorder functionalities.
- Added tests for the new keyword handling logic.
This commit is contained in:
Linus Rath
2026-03-11 18:40:09 +01:00
parent 61459c4923
commit ffeb534740
19 changed files with 558 additions and 100 deletions
+6 -2
View File
@@ -210,7 +210,11 @@ describe('mergeThreadEmails', () => {
});
describe('getEmailColorTag', () => {
it('returns color from $color: keyword', () => {
it('returns label from $label: keyword', () => {
expect(getEmailColorTag({ '$label:red': true, $seen: true })).toBe('red');
});
it('returns label from legacy $color: keyword', () => {
expect(getEmailColorTag({ '$color:red': true, $seen: true })).toBe('red');
});
@@ -227,7 +231,7 @@ describe('getThreadColorTag', () => {
it('returns first color found across thread emails', () => {
const emails = [
makeEmail({ id: 'e1', keywords: { $seen: true } }),
makeEmail({ id: 'e2', keywords: { '$color:blue': true } }),
makeEmail({ id: 'e2', keywords: { '$label:blue': true } }),
];
expect(getThreadColorTag(emails)).toBe('blue');
});
+11 -3
View File
@@ -136,15 +136,23 @@ export function mergeThreadEmails(
};
}
/** Active prefix for new keyword tags written to JMAP */
export const KEYWORD_PREFIX = "$label:";
/** Legacy prefix still recognised when reading */
export const KEYWORD_PREFIX_LEGACY = "$color:";
/**
* Gets color tag from email keywords (if any).
* Gets label/color tag from email keywords (if any).
* Reads both the current $label: prefix and the legacy $color: prefix.
*/
export function getEmailColorTag(keywords: Record<string, boolean> | undefined): string | null {
if (!keywords) return null;
for (const key of Object.keys(keywords)) {
if (key.startsWith("$color:") && keywords[key] === true) {
return key.replace("$color:", "");
if ((key.startsWith(KEYWORD_PREFIX) || key.startsWith(KEYWORD_PREFIX_LEGACY)) && keywords[key] === true) {
return key.startsWith(KEYWORD_PREFIX)
? key.slice(KEYWORD_PREFIX.length)
: key.slice(KEYWORD_PREFIX_LEGACY.length);
}
}