mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-01 00:07:01 +00:00
fix: show localized system folder names in the filter rule folder picker
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
// The filter rule's "Move to folder" picker listed the server's folder names
|
||||
// ("Sent Items", "Junk Mail", "Deleted Items") where the rest of the app
|
||||
// shows the localized role names (localizeMailboxName).
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
vi.mock('expo-localization', () => ({ getLocales: () => [] }));
|
||||
|
||||
import { translate } from '../../i18n';
|
||||
import { buildMailboxTargets } from '../../lib/sieve/rule-actions';
|
||||
import { folderOptionLabel, mailboxesByTargetId } from '../filters/folder-option-label';
|
||||
import type { Mailbox } from '../../api/types';
|
||||
|
||||
const mb = (id: string, name: string, role: string | null, parentId: string | null = null): Mailbox => ({
|
||||
id, name, role, parentId, sortOrder: 0, totalEmails: 0, unreadEmails: 0, totalThreads: 0, unreadThreads: 0,
|
||||
myRights: {} as Mailbox['myRights'], isSubscribed: true,
|
||||
} as unknown as Mailbox);
|
||||
|
||||
const mailboxes = [
|
||||
mb('i', 'Inbox', 'inbox'),
|
||||
mb('s', 'Sent Items', 'sent'),
|
||||
mb('j', 'Junk Mail', 'junk'),
|
||||
mb('d', 'Deleted Items', 'trash'),
|
||||
mb('p', 'Projects', null),
|
||||
mb('a', 'Alpha', null, 'p'),
|
||||
];
|
||||
|
||||
function labels(locale: 'en' | 'de') {
|
||||
const t = (key: string, fallback?: string) => translate(locale, key, fallback);
|
||||
const byId = mailboxesByTargetId(mailboxes);
|
||||
return Object.fromEntries(buildMailboxTargets(mailboxes).map((target) => [target.id, folderOptionLabel(target, byId.get(target.id), t)]));
|
||||
}
|
||||
|
||||
describe('filter rule folder picker labels', () => {
|
||||
it('names system folders by role, like the sidebar', () => {
|
||||
const en = labels('en');
|
||||
expect(en.s).toBe(translate('en', 'sidebar.mailboxes.sent'));
|
||||
expect(en.j).toBe(translate('en', 'sidebar.mailboxes.spam'));
|
||||
expect(en.d).toBe(translate('en', 'sidebar.mailboxes.trash'));
|
||||
expect(en.s).not.toBe('Sent Items');
|
||||
expect(labels('de').d).toBe(translate('de', 'sidebar.mailboxes.trash'));
|
||||
});
|
||||
|
||||
it('keeps user folders and their indent', () => {
|
||||
const en = labels('en');
|
||||
expect(en.p).toBe('Projects');
|
||||
expect(en.a).toBe(' Alpha');
|
||||
});
|
||||
|
||||
it('is what the rule editor lists', () => {
|
||||
const source = fs.readFileSync(path.resolve(__dirname, '../filters/FilterRuleModal.tsx'), 'utf8');
|
||||
expect(source).toContain('label: folderOptionLabel(target, mailboxById.get(target.id), t)');
|
||||
});
|
||||
});
|
||||
@@ -45,6 +45,7 @@ import { SafeAreaModal } from '../SafeAreaModal';
|
||||
import { useFilterStore } from '../../stores/filter-store';
|
||||
import { forwardsOfEditedRule, ruleForwardLimit } from '../../lib/sieve/forward-limit';
|
||||
import { MODAL_ORIENTATIONS } from '../../lib/modal-orientations';
|
||||
import { folderOptionLabel, mailboxesByTargetId } from './folder-option-label';
|
||||
import { useModalKeyboardPadding } from '../../lib/sheet-bottom-inset';
|
||||
|
||||
const ALL_FIELDS: FilterConditionField[] = ['from', 'to', 'cc', 'subject', 'header', 'size', 'body', 'attachment', 'all'];
|
||||
@@ -131,6 +132,7 @@ export function FilterRuleModal({
|
||||
const periodProblem = periodOn ? periodError(activeFrom, activeUntil) : null;
|
||||
|
||||
const mailboxTargets = useMemo(() => buildMailboxTargets(mailboxes), [mailboxes]);
|
||||
const mailboxById = useMemo(() => mailboxesByTargetId(mailboxes), [mailboxes]);
|
||||
|
||||
// A message can collect only so many forwards on the server (#1155, #1208).
|
||||
const listedRules = useFilterStore((s) => s.rules);
|
||||
@@ -216,7 +218,10 @@ export function FilterRuleModal({
|
||||
// listed (deleted, or its account's folders not loaded) stays selectable
|
||||
// under its path, so saving doesn't silently retarget the rule.
|
||||
const folderOptions = (action: FilterAction) => {
|
||||
const options = mailboxTargets.map((target) => ({ value: target.id, label: target.label }));
|
||||
const options = mailboxTargets.map((target) => ({
|
||||
value: target.id,
|
||||
label: folderOptionLabel(target, mailboxById.get(target.id), t),
|
||||
}));
|
||||
const selected = mailboxIdFor(action, mailboxTargets);
|
||||
if (!options.some((o) => o.value === selected) && (selected || action.value)) {
|
||||
options.unshift({ value: selected, label: action.value || selected });
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { localizeMailboxName } from '../../lib/mailbox-label';
|
||||
import type { MailboxTarget } from '../../lib/sieve/rule-actions';
|
||||
import type { Mailbox } from '../../api/types';
|
||||
|
||||
type Translate = (key: string, fallback?: string) => string;
|
||||
|
||||
/**
|
||||
* A filter rule's folder picker row: the folder's indent and its name as the
|
||||
* rest of the app shows it. The targets carry the server name, so system
|
||||
* folders read "Sent Items", "Junk Mail" or "Deleted Items" here and
|
||||
* "Sent", "Spam" or "Trash" everywhere else. The rule itself keeps the
|
||||
* server path (fileinto needs it); only the label changes.
|
||||
*/
|
||||
export function folderOptionLabel(
|
||||
target: MailboxTarget,
|
||||
mailbox: Pick<Mailbox, 'role' | 'name'> | undefined,
|
||||
t: Translate,
|
||||
): string {
|
||||
if (!mailbox?.role) return target.label;
|
||||
const indent = /^ */.exec(target.label)?.[0] ?? '';
|
||||
return indent + localizeMailboxName(mailbox.role, mailbox.name, t);
|
||||
}
|
||||
|
||||
/** Mailboxes by the id the targets use (the server id of a shared folder). */
|
||||
export function mailboxesByTargetId(mailboxes: Mailbox[]): Map<string, Mailbox> {
|
||||
return new Map(mailboxes.map((m) => [m.originalId ?? m.id, m]));
|
||||
}
|
||||
Reference in New Issue
Block a user