diff --git a/frontend-next/package.json b/frontend-next/package.json index 6f0dfa9..de1b628 100644 --- a/frontend-next/package.json +++ b/frontend-next/package.json @@ -13,6 +13,7 @@ "@vitejs/plugin-react": "^5.1.2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "dompurify": "^3.3.0", "lucide-react": "^0.561.0", "next-themes": "^0.4.6", "radix-ui": "^1.6.0", diff --git a/frontend-next/pnpm-lock.yaml b/frontend-next/pnpm-lock.yaml index 4407a85..1d8ee88 100644 --- a/frontend-next/pnpm-lock.yaml +++ b/frontend-next/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: clsx: specifier: ^2.1.1 version: 2.1.1 + dompurify: + specifier: ^3.3.0 + version: 3.4.13 lucide-react: specifier: ^0.561.0 version: 0.561.0(react@19.2.7) @@ -1267,6 +1270,9 @@ packages: '@types/react@19.2.17': resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@vitejs/plugin-react@5.2.0': resolution: {integrity: sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1319,6 +1325,9 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + dompurify@3.4.13: + resolution: {integrity: sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ==} + electron-to-chromium@1.5.379: resolution: {integrity: sha512-v/qV5aV5EUA2pGilzUCq5/eyOloZAqDZBu9UMBIzgPpLlprjSR6zswsWBTv0KpqxLGUAZEwhO95ZCt7srymNVA==} @@ -2811,6 +2820,9 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/trusted-types@2.0.7': + optional: true + '@vitejs/plugin-react@5.2.0(vite@7.3.6(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.32.0))': dependencies: '@babel/core': 7.29.7 @@ -2857,6 +2869,10 @@ snapshots: detect-node-es@1.1.0: {} + dompurify@3.4.13: + optionalDependencies: + '@types/trusted-types': 2.0.7 + electron-to-chromium@1.5.379: {} enhanced-resolve@5.21.6: diff --git a/frontend-next/src/App.tsx b/frontend-next/src/App.tsx index 950c504..03ed608 100644 --- a/frontend-next/src/App.tsx +++ b/frontend-next/src/App.tsx @@ -1,4 +1,5 @@ import { useCallback, useEffect, useMemo, useState, type ReactNode } from "react" +import DOMPurify from "dompurify" import { Archive, Check, @@ -171,6 +172,7 @@ function App() { setMails([]) setSelectedId("") setSettings(defaultAddressSettings) + setPage(1) } const logout = () => { @@ -178,6 +180,7 @@ function App() { setSettings({ ...defaultAddressSettings, fetched: true }) setMails([]) setSelectedId("") + setPage(1) } return ( @@ -899,10 +902,14 @@ function NavItem({ function AddressPill({ address, showNotice }: { address: string; showNotice: (notice: Notice) => void }) { const [copied, setCopied] = useState(false) const copyAddress = async () => { - await navigator.clipboard.writeText(address) - setCopied(true) - showNotice({ type: "success", text: "Address copied" }) - window.setTimeout(() => setCopied(false), 1400) + try { + await navigator.clipboard.writeText(address) + setCopied(true) + showNotice({ type: "success", text: "Address copied" }) + window.setTimeout(() => setCopied(false), 1400) + } catch (error) { + showNotice({ type: "error", text: `Copy failed: ${getErrorMessage(error)}` }) + } } return ( @@ -1010,8 +1017,10 @@ function MessageReader({ ) } - const body = mail.message || mail.text || mail.raw || "" - const isHtml = /<\/?[a-z][\s\S]*>/i.test(body) + const htmlBody = mail.html || (mail.message && /<\/?[a-z][\s\S]*>/i.test(mail.message) ? mail.message : "") + const body = htmlBody || mail.text || mail.message || "" + const sender = mail.sender || mail.source + const safeHtml = htmlBody ? createSafeMailHtml(htmlBody) : "" return (
@@ -1034,10 +1043,10 @@ function MessageReader({
- {senderInitials(mail.source)} + {senderInitials(sender)}

{mail.subject || "No Subject"}

-

{mail.source || "Unknown sender"}

+

{sender || "Unknown sender"}

to {address} ยท {formatDate(mail.created_at)}

@@ -1045,8 +1054,14 @@ function MessageReader({
- {isHtml ? ( -