Merge branch 'dev' of https://github.com/bulwarkmail/webmail into dev
This commit is contained in:
@@ -51,9 +51,9 @@ export function EmailListItem({ email, selected, onClick, onContextMenu, onToggl
|
||||
const isFocusedMailLayout = mailLayout === 'focus';
|
||||
const inlinePreview = showPreview && email.preview ? ` ${email.preview}` : '';
|
||||
|
||||
// Resolve color tags using keyword definitions from settings
|
||||
// Resolve color tags using keyword definitions from settings; unknown tags fall back to gray
|
||||
const colorTagIds = getEmailColorTags(email.keywords);
|
||||
const keywordDefs = colorTagIds.map(id => emailKeywords.find(k => k.id === id)).filter(Boolean) as typeof emailKeywords;
|
||||
const keywordDefs = colorTagIds.map(id => emailKeywords.find(k => k.id === id) ?? { id, label: id, color: 'gray' });
|
||||
// Use first tag for background coloring
|
||||
const keywordDef = keywordDefs[0] ?? null;
|
||||
const colorTag = keywordDef ? KEYWORD_PALETTE[keywordDef.color]?.bg ?? null : null;
|
||||
|
||||
@@ -3070,13 +3070,13 @@ export function EmailViewer({
|
||||
<>
|
||||
<span className="flex items-center gap-0.5">
|
||||
{currentColors.slice(0, 3).map((tagId) => {
|
||||
const kw = emailKeywords.find(k => k.id === tagId);
|
||||
return kw ? <span key={tagId} className={cn("w-3 h-3 rounded-full", KEYWORD_PALETTE[kw.color]?.dot)} /> : null;
|
||||
const kw = emailKeywords.find(k => k.id === tagId) ?? { id: tagId, label: tagId, color: 'gray' };
|
||||
return <span key={tagId} className={cn("w-3 h-3 rounded-full", KEYWORD_PALETTE[kw.color]?.dot || 'bg-gray-500')} />;
|
||||
})}
|
||||
</span>
|
||||
{showToolbarLabels && currentColors.length === 1 && (
|
||||
<span className="text-xs font-medium text-foreground">
|
||||
{emailKeywords.find(k => k.id === currentColors[0])?.label}
|
||||
{emailKeywords.find(k => k.id === currentColors[0])?.label ?? currentColors[0]}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
@@ -3674,11 +3674,11 @@ export function EmailViewer({
|
||||
{currentColors.length > 0 && (
|
||||
<span className="flex items-center gap-0.5">
|
||||
{currentColors.map((tagId) => {
|
||||
const kw = emailKeywords.find(k => k.id === tagId);
|
||||
const dotClass = kw ? KEYWORD_PALETTE[kw.color]?.dot : null;
|
||||
return dotClass ? (
|
||||
<span key={tagId} className={cn("w-2.5 h-2.5 rounded-full flex-shrink-0", dotClass)} title={kw!.label} />
|
||||
) : null;
|
||||
const kw = emailKeywords.find(k => k.id === tagId) ?? { id: tagId, label: tagId, color: 'gray' };
|
||||
const dotClass = KEYWORD_PALETTE[kw.color]?.dot || 'bg-gray-500';
|
||||
return (
|
||||
<span key={tagId} className={cn("w-2.5 h-2.5 rounded-full flex-shrink-0", dotClass)} title={kw.label} />
|
||||
);
|
||||
})}
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -67,9 +67,9 @@ const SingleEmailItem = React.forwardRef<HTMLDivElement, SingleEmailItemProps>(
|
||||
const isFocusedMailLayout = mailLayout === 'focus';
|
||||
const inlinePreview = showPreview && email.preview ? ` ${email.preview}` : '';
|
||||
|
||||
// Resolve color tags using keyword definitions
|
||||
// Resolve color tags using keyword definitions; unknown tags fall back to gray
|
||||
const tagIds = getEmailColorTags(email.keywords);
|
||||
const resolvedKeywordDefs = tagIds.map(id => emailKeywords.find(k => k.id === id)).filter(Boolean) as typeof emailKeywords;
|
||||
const resolvedKeywordDefs = tagIds.map(id => emailKeywords.find(k => k.id === id) ?? { id, label: id, color: 'gray' });
|
||||
const resolvedKeywordDef = resolvedKeywordDefs[0] ?? null;
|
||||
const resolvedColorTag = (() => {
|
||||
if (colorTag) return colorTag;
|
||||
@@ -375,7 +375,7 @@ export const ThreadListItem = React.forwardRef<HTMLDivElement, ThreadListItemPro
|
||||
|
||||
const threadColor = getThreadColorTag(thread.emails);
|
||||
const emailKeywordDefs = useSettingsStore((state) => state.emailKeywords);
|
||||
const keywordDef = threadColor ? emailKeywordDefs.find(k => k.id === threadColor) : null;
|
||||
const keywordDef = threadColor ? (emailKeywordDefs.find(k => k.id === threadColor) ?? { id: threadColor, label: threadColor, color: 'gray' }) : null;
|
||||
const colorTag = keywordDef ? KEYWORD_PALETTE[keywordDef.color]?.bg ?? null : null;
|
||||
|
||||
const isSelected = selectedEmailId === latestEmail.id ||
|
||||
|
||||
@@ -17,6 +17,7 @@ import type {
|
||||
} from "@/lib/jmap/sieve-types";
|
||||
import type { Mailbox } from "@/lib/jmap/types";
|
||||
import { buildMailboxTree, flattenMailboxTree, type MailboxNode, generateUUID } from "@/lib/utils";
|
||||
import { useSettingsStore } from "@/stores/settings-store";
|
||||
|
||||
interface FilterRuleModalProps {
|
||||
rule?: FilterRule;
|
||||
@@ -58,6 +59,7 @@ export function FilterRuleModal({
|
||||
}: FilterRuleModalProps) {
|
||||
const t = useTranslations("settings.filters");
|
||||
const isEdit = !!rule;
|
||||
const emailKeywords = useSettingsStore((state) => state.emailKeywords);
|
||||
|
||||
const [name, setName] = useState(rule?.name || "");
|
||||
const [matchType, setMatchType] = useState<"all" | "any">(rule?.matchType || "all");
|
||||
@@ -375,12 +377,17 @@ export function FilterRuleModal({
|
||||
)}
|
||||
|
||||
{action.type === "add_label" && (
|
||||
<Input
|
||||
<select
|
||||
value={action.value || ""}
|
||||
onChange={(e) => updateAction(index, { value: e.target.value })}
|
||||
placeholder={t("label_placeholder")}
|
||||
className="flex-1 min-w-[140px]"
|
||||
/>
|
||||
className={`${selectClass} flex-1 min-w-[140px]`}
|
||||
aria-label={t("label_placeholder")}
|
||||
>
|
||||
<option value="">{t("label_placeholder")}</option>
|
||||
{emailKeywords.map((kw) => (
|
||||
<option key={kw.id} value={kw.id}>{kw.label}</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user