fix: logo optimization, deprecated meta tag, and clipboard copy fallback (#306)

- Replace 2 MB favicon.svg (base64-PNG-in-SVG) with optimized 43 KB app-logo.png (256x256)
- Update AppHeader and AboutModal references to use new logo
- Remove SVG favicon link from index.html (PNG/ICO favicons remain)
- Fix deprecated apple-mobile-web-app-capable → mobile-web-app-capable meta tag
- Add clipboard copy fallback for non-secure contexts (LAN IP over HTTP)

Closes #303
This commit is contained in:
Daniel Volz
2026-02-25 00:04:35 +01:00
committed by GitHub
parent 96b2a0c96f
commit 6161c14a7b
7 changed files with 39 additions and 13 deletions
+1 -2
View File
@@ -6,7 +6,6 @@
<title>MedAssist-ng</title>
<!-- Favicons -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
@@ -14,7 +13,7 @@
<!-- Theme color -->
<meta name="theme-color" content="#0f172a" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
</head>
<body>
Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 1.9 MiB

+1 -1
View File
@@ -73,7 +73,7 @@ export default function AboutModal({ isOpen, onClose }: AboutModalProps) {
</button>
<div className="about-header">
<div className="about-logo">
<img src="/favicon.svg" alt="MedAssist-ng" />
<img src="/app-logo.png" alt="MedAssist-ng" />
</div>
<h2>{t("about.appName", "MedAssist-ng")}</h2>
<p className="about-tagline">{t("about.description", "Personal medication tracking and reminder app")}</p>
+1 -1
View File
@@ -73,7 +73,7 @@ export function AppHeader({ onOpenProfile, onOpenAbout }: AppHeaderProps) {
return (
<header className="hero">
<div className="hero-title">
<img src="/favicon.svg" alt="MedAssist-ng" className="hero-logo" />
<img src="/app-logo.png" alt="MedAssist-ng" className="hero-logo" />
<div>
<p className="eyebrow">{pageInfo.eyebrow}</p>
<h1>{pageInfo.title}</h1>
+34 -4
View File
@@ -106,10 +106,40 @@ export function useShare(): UseShareReturn {
const copyShareLink = useCallback(() => {
if (shareLink) {
navigator.clipboard.writeText(shareLink);
setShareCopied(true);
log.debug("[ShareDialog] Share link copied to clipboard");
setTimeout(() => setShareCopied(false), 2000);
if (navigator.clipboard?.writeText) {
navigator.clipboard.writeText(shareLink).then(
() => {
setShareCopied(true);
log.debug("[ShareDialog] Share link copied to clipboard");
setTimeout(() => setShareCopied(false), 2000);
},
() => {
// Clipboard API blocked (non-secure context / permissions)
fallbackCopyToClipboard(shareLink);
}
);
} else {
fallbackCopyToClipboard(shareLink);
}
}
function fallbackCopyToClipboard(text: string) {
const textarea = document.createElement("textarea");
textarea.value = text;
textarea.style.position = "fixed";
textarea.style.opacity = "0";
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand("copy");
setShareCopied(true);
log.debug("[ShareDialog] Share link copied via fallback");
setTimeout(() => setShareCopied(false), 2000);
} catch {
log.warn("[ShareDialog] Clipboard copy failed — not in secure context");
} finally {
document.body.removeChild(textarea);
}
}
}, [shareLink]);
+2 -2
View File
@@ -14,7 +14,7 @@ describe("useShare", () => {
mockAlert = vi.fn();
global.alert = mockAlert;
mockClipboard = { writeText: vi.fn() };
mockClipboard = { writeText: vi.fn().mockResolvedValue(undefined) };
Object.defineProperty(navigator, "clipboard", {
value: mockClipboard,
writable: true,
@@ -237,7 +237,7 @@ describe("useShare", () => {
result.current.setShareLink("http://localhost:5173/share/test-token");
});
act(() => {
await act(async () => {
result.current.copyShareLink();
});