/** * 打手端公告弹窗的「已确认」记忆。 * * 后台公告只有一条全局配置(`worker_announcement = { enabled, content }`),没有公告 ID, * 因此这里用**内容指纹**充当版本号:只要运营改了公告正文,指纹就变,打手端会重新弹一次。 * 确认记录存在 localStorage(方案 B:服务端不落库),换设备或清缓存后会再弹一次。 */ const ANNOUNCEMENT_ACK_STORAGE_KEY = 'order-site-worker-announcement-ack' type AnnouncementPopupState = { enabled?: boolean content?: string /** 本地已确认过的公告版本(来自 localStorage) */ acknowledgedVersion?: string } /** 公告正文的内容指纹(FNV-1a 32 位),空内容返回空串表示「没有可确认的公告」。 */ export function resolveAnnouncementVersion(content: string): string { const normalized = String(content || '').trim() if (!normalized) return '' let hash = 0x811c9dc5 for (let index = 0; index < normalized.length; index += 1) { hash ^= normalized.charCodeAt(index) hash = Math.imul(hash, 0x01000193) >>> 0 } return `v1-${hash.toString(16).padStart(8, '0')}` } /** * 是否需要弹窗:公告开启 + 有正文 + 当前版本还没被这台设备确认过。 * 已确认过的版本不再弹;运营改了正文(版本变化)后会重新弹。 */ export function shouldShowAnnouncementPopup(state: AnnouncementPopupState): boolean { if (state.enabled !== true) return false const version = resolveAnnouncementVersion(state.content || '') if (!version) return false return state.acknowledgedVersion !== version } /** 读取本机已确认的公告版本(隐私模式/禁用存储时返回空串,页面照常可用)。 */ export function readAnnouncementAck(): string { try { if (typeof window === 'undefined') return '' return window.localStorage.getItem(ANNOUNCEMENT_ACK_STORAGE_KEY) || '' } catch { return '' } } /** 记录本机已确认的公告版本。 */ export function writeAnnouncementAck(version: string): void { if (!version) return try { if (typeof window === 'undefined') return window.localStorage.setItem(ANNOUNCEMENT_ACK_STORAGE_KEY, version) } catch { // 存储不可用(隐私模式/配额满)时忽略,下一次打开会再弹一次。 } }