Version the brand images' URLs, so a new mark reaches returning visitors
The logo, favicons and app icons are served from public/img under fixed names with a browser cache of hours, and the service worker fetches them through that cache. After the mark changed on 2026-09-27, returning visitors kept the old cat until their copies expired, and the favicon and an installed app's icon hold on longer still. Every URL that names one now carries ?v=BRAND_V (src/lib/brand.ts, brandImage()): the header, sign-in, About, the mail empty state, the notification icons, index.html's favicon links, the manifest's icons and the service worker's shell and notification icons. Date-stamped, never a counter, for the sites' ASSET_V reason; the three static files carry the value written out, and the comment says to keep them in step.
This commit is contained in:
1 parent
2a30a32c79
commit
0adc402629
9 files changed
+49
-25
No files matched your search
@@ -2,8 +2,7 @@ import { lazy, Suspense, useEffect, useRef, useState, type ReactNode } from "rea
|
||||
import { Link, useLocation } from "wouter";
|
||||
import { Calendar, ChevronsUpDown, FolderOpen, Globe, HelpCircle, LogOut, Mail, Menu as MenuIcon, Moon, PenSquare, Plus, RefreshCw, Settings, ShieldCheck, Sun, Upload, Users, X } from "lucide-react";
|
||||
import { useSession } from "@/store/session";
|
||||
import { withBase } from "@/lib/basePath";
|
||||
import { DEFAULT_APP_NAME } from "@/lib/brand";
|
||||
import { DEFAULT_APP_NAME, brandImage } from "@/lib/brand";
|
||||
import { InbuxaWordmark } from "@/ui/InbuxaWordmark";
|
||||
import { useEffectiveTheme, useSettings } from "@/store/settings";
|
||||
import { toggleTarget } from "@/lib/palette";
|
||||
@@ -148,7 +147,7 @@ export function AppShell({ children }: { children: ReactNode }) {
|
||||
<MenuIcon size={22} />
|
||||
</button>
|
||||
<Link href="/mail" className="brand">
|
||||
<img src={withBase(appName === DEFAULT_APP_NAME ? "/img/inbuxa-mark.png" : "/img/logo.png")} alt="" />
|
||||
<img src={brandImage(appName === DEFAULT_APP_NAME ? "/img/inbuxa-mark.png" : "/img/logo.png")} alt="" />
|
||||
{/* A product name, not a word: translated it is a different product.
|
||||
Read from the session rather than written here, so a deployment
|
||||
that set APP_NAME is called what it calls itself -- the document
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ApiError } from "@/jmap/client";
|
||||
import { withBase } from "@/lib/basePath";
|
||||
import { APP_VERSION } from "@/lib/version";
|
||||
import { DEFAULT_SOURCE_URL } from "@/lib/source";
|
||||
import { DEFAULT_APP_NAME } from "@/lib/brand";
|
||||
import { DEFAULT_APP_NAME, brandImage } from "@/lib/brand";
|
||||
import { t } from "@/lib/i18n";
|
||||
import { InbuxaWordmark } from "@/ui/InbuxaWordmark";
|
||||
|
||||
@@ -92,7 +92,7 @@ export function LoginPage() {
|
||||
<div className="login-page">
|
||||
<form className="login-card" onSubmit={submit}>
|
||||
<div className="logo">
|
||||
<img src={withBase(appName === DEFAULT_APP_NAME ? "/img/inbuxa-mark.png" : "/img/logo.png")} alt="" width={120} height={126} />
|
||||
<img src={brandImage(appName === DEFAULT_APP_NAME ? "/img/inbuxa-mark.png" : "/img/logo.png")} alt="" width={120} height={126} />
|
||||
{/* A product name, not a word: not translated, and not guessed at
|
||||
from the page it is on. INBUXA's is its wordmark. */}
|
||||
<h1 className="notranslate" translate="no">
|
||||
|
||||
@@ -4,7 +4,6 @@ import { DEFAULT_SORT, useMail, type ListQuery } from "@/store/mail";
|
||||
import { appliesTo, comparatorsFor } from "@/lib/listSort";
|
||||
import type { Comparator } from "@/jmap/types";
|
||||
import { useSettings } from "@/store/settings";
|
||||
import { withBase } from "@/lib/basePath";
|
||||
import { useCompose } from "@/store/compose";
|
||||
import { buildFilter, describeFilter, parseQuery } from "@/lib/search";
|
||||
import { keyboard } from "@/lib/input/keyboard";
|
||||
@@ -21,6 +20,7 @@ import { isUnknownMailbox } from "@/lib/mailbox/mailboxRoute";
|
||||
import { scheduledMailboxIdFrom, useScheduled } from "@/store/scheduled";
|
||||
import { plural, t as translate, tNode } from "@/lib/i18n";
|
||||
import { mailboxDisplayName } from "@/lib/mailbox/mailboxName";
|
||||
import { brandImage } from "@/lib/brand";
|
||||
|
||||
export function MailView({ mailboxId, threadId, search }: { mailboxId?: string; threadId?: string; search?: boolean }) {
|
||||
const [, navigate] = useLocation();
|
||||
@@ -468,7 +468,7 @@ export function MailView({ mailboxId, threadId, search }: { mailboxId?: string;
|
||||
<ThreadView key={`${threadId}:${openMessageId ?? ""}`} threadId={threadId} messageId={openMessageId} mailboxId={mailboxId ?? null} onBack={() => openThread(null)} actions={actions} onNavigate={(delta) => { const idx = currentRowIndex; const next = ids[idx + delta]; const t = next ? rowThreadId(next) : undefined; if (t) { setFocusId(next!); openThread(t, settings.conversationMode ? null : next!); } }} hasPrev={currentRowIndex > 0} hasNext={currentRowIndex >= 0 && currentRowIndex < ids.length - 1} />
|
||||
) : (
|
||||
<div className="no-thread">
|
||||
<img src={withBase("/img/logo.png")} alt="" />
|
||||
<img src={brandImage("/img/logo.png")} alt="" />
|
||||
<div>
|
||||
{list?.total
|
||||
? settings.conversationMode
|
||||
|
||||
@@ -4,9 +4,9 @@ import { useAppName } from "@/lib/brand";
|
||||
import { apiFetch, client } from "@/jmap/client";
|
||||
import { APP_VERSION } from "@/lib/version";
|
||||
import { DEFAULT_SOURCE_URL } from "@/lib/source";
|
||||
import { withBase } from "@/lib/basePath";
|
||||
import { t, tNode } from "@/lib/i18n";
|
||||
import { InbuxaWordmark } from "@/ui/InbuxaWordmark";
|
||||
import { brandImage } from "@/lib/brand";
|
||||
|
||||
/** ihasmail-inbuxa: which webmail node answered and which mail node it talks to (server/src/nodes.ts). */
|
||||
interface Nodes {
|
||||
@@ -47,7 +47,7 @@ export function AboutSettings() {
|
||||
inbuxa's own, not APP_NAME. */}
|
||||
<p className="lead">{tNode("{inbuxa} is a complete mail suite: a mail server, the console that administers it, and this webmail, each its own program, installed together and free under the AGPL.", { inbuxa: <strong className="brand-inbuxa notranslate" translate="no">inbuxa</strong> })}</p>
|
||||
<div className="row" style={{ gap: 16, alignItems: "center", marginBottom: 16 }}>
|
||||
<img src={withBase("/img/inbuxa-mark.png")} alt="" width={80} />
|
||||
<img src={brandImage("/img/inbuxa-mark.png")} alt="" width={80} />
|
||||
<div>
|
||||
<InbuxaWordmark height={26} />
|
||||
{/* A product name and a version string: neither is a word to translate. */}
|
||||
|
||||
Reference in new issue
Block a user