feat: sender favicon avatars

This commit is contained in:
Linus Rath
2026-03-09 18:55:44 +01:00
parent cf02f587de
commit 481d566eb6
12 changed files with 176 additions and 3 deletions
+6 -1
View File
@@ -9,7 +9,7 @@ import { Button } from '@/components/ui/button';
export function AdvancedSettings() {
const t = useTranslations('settings.advanced');
const tCommon = useTranslations('common');
const { debugMode, updateSetting, resetToDefaults, exportSettings, importSettings } =
const { debugMode, senderFavicons, updateSetting, resetToDefaults, exportSettings, importSettings } =
useSettingsStore();
const [showResetConfirm, setShowResetConfirm] = useState(false);
const fileInputRef = useRef<HTMLInputElement>(null);
@@ -66,6 +66,11 @@ export function AdvancedSettings() {
<ToggleSwitch checked={debugMode} onChange={(checked) => updateSetting('debugMode', checked)} />
</SettingItem>
{/* Sender Favicons (Experimental) */}
<SettingItem label={t('sender_favicons.label')} description={t('sender_favicons.description')}>
<ToggleSwitch checked={senderFavicons} onChange={(checked) => updateSetting('senderFavicons', checked)} />
</SettingItem>
{/* Export Settings */}
<SettingItem label={t('export_settings.label')} description={t('export_settings.description')}>
<Button variant="outline" size="sm" onClick={handleExport}>
+32 -2
View File
@@ -1,4 +1,18 @@
"use client";
import { useState } from "react";
import { cn } from "@/lib/utils";
import { useSettingsStore } from "@/stores/settings-store";
// Personal email domains where the favicon is the mail provider logo, not the sender
const PERSONAL_DOMAINS = new Set([
"gmail.com", "googlemail.com", "outlook.com", "hotmail.com", "live.com",
"msn.com", "yahoo.com", "yahoo.fr", "yahoo.co.uk", "yahoo.co.jp",
"aol.com", "icloud.com", "me.com", "mac.com", "mail.com",
"proton.me", "protonmail.com", "pm.me", "tutanota.com", "tuta.com",
"zoho.com", "yandex.com", "yandex.ru", "gmx.com", "gmx.net",
"fastmail.com", "hey.com", "posteo.de", "mailbox.org",
]);
interface AvatarProps {
name?: string;
@@ -8,6 +22,9 @@ interface AvatarProps {
}
export function Avatar({ name, email, size = "md", className }: AvatarProps) {
const [faviconError, setFaviconError] = useState(false);
const senderFavicons = useSettingsStore((s) => s.senderFavicons);
const getInitials = () => {
if (name) {
const parts = name.trim().split(/\s+/);
@@ -38,17 +55,30 @@ export function Avatar({ name, email, size = "md", className }: AvatarProps) {
lg: "w-12 h-12 text-base",
};
const domain = email?.split("@")[1]?.toLowerCase();
const showFavicon =
senderFavicons && domain && !PERSONAL_DOMAINS.has(domain) && !faviconError;
return (
<div
className={cn(
"rounded-full flex items-center justify-center font-semibold text-white",
"rounded-full flex items-center justify-center font-semibold text-white overflow-hidden",
sizeClasses[size],
className
)}
style={{ backgroundColor: getBackgroundColor() }}
title={name || email}
>
{getInitials()}
{showFavicon ? (
<img
src={`/api/favicon?domain=${encodeURIComponent(domain)}`}
alt=""
className="w-full h-full object-cover"
onError={() => setFaviconError(true)}
/>
) : (
getInitials()
)}
</div>
);
}