add account localization and switchable rooms
Centralize control and OBS copy in a shared TOML catalog, persist the selected locale per account, and broadcast language changes to component streams. Allow account owners to atomically switch their Bilibili room and restart the shared listener without changing component URLs.
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
* one-time-secret or dirty-form blocker has cleared.
|
||||
*/
|
||||
import { useEffect, useSyncExternalStore } from 'react'
|
||||
import { translate, useI18n } from './i18n'
|
||||
|
||||
interface InstallChoice {
|
||||
outcome: 'accepted' | 'dismissed'
|
||||
@@ -108,7 +109,7 @@ async function removeLegacyRootWorker() {
|
||||
export function cleanupLegacyPwa() {
|
||||
if (!import.meta.env.PROD || !('serviceWorker' in navigator)) return
|
||||
void removeLegacyRootWorker().catch(error => {
|
||||
console.warn('旧版 PWA 清理失败', error)
|
||||
console.warn('Legacy PWA cleanup failed', error)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -122,7 +123,7 @@ async function registerWorker() {
|
||||
observeRegistration(registration)
|
||||
} catch (error) {
|
||||
// A failed PWA registration must never stop the online control console.
|
||||
console.warn('控制台 PWA 注册失败', error)
|
||||
console.warn('Console PWA registration failed', error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,19 +212,18 @@ function applyUpdate() {
|
||||
const reasons = [...new Set(updateBlockers.values())]
|
||||
if (reasons.length > 0) {
|
||||
window.alert(
|
||||
`暂时不能更新,请先处理以下内容:\n\n${reasons.map(reason => `• ${reason}`).join('\n')}`,
|
||||
translate('pwa.blocked', { reasons: reasons.map(reason => `• ${reason}`).join('\n') }),
|
||||
)
|
||||
return
|
||||
}
|
||||
const confirmed = window.confirm(
|
||||
'更新会刷新控制台。请先保存设置、邀请码、恢复码或刚轮换的 OBS 令牌,确定现在更新吗?',
|
||||
)
|
||||
const confirmed = window.confirm(translate('pwa.confirm_update'))
|
||||
if (!confirmed) return
|
||||
reloadForUpdate = true
|
||||
worker.postMessage({ type: 'SKIP_WAITING' })
|
||||
}
|
||||
|
||||
export function PwaControls() {
|
||||
const { t } = useI18n()
|
||||
const state = useSyncExternalStore(subscribe, getSnapshot, getSnapshot)
|
||||
const canInstall = Boolean(state.installPrompt) && !state.standalone
|
||||
if (state.online && !state.waitingWorker && !canInstall) return null
|
||||
@@ -233,17 +233,17 @@ export function PwaControls() {
|
||||
{!state.online && (
|
||||
<span className="pwa-state offline">
|
||||
<i aria-hidden="true" />
|
||||
离线
|
||||
{t('pwa.offline')}
|
||||
</span>
|
||||
)}
|
||||
{state.waitingWorker && (
|
||||
<button type="button" className="pwa-action update" onClick={applyUpdate}>
|
||||
更新可用
|
||||
{t('pwa.update')}
|
||||
</button>
|
||||
)}
|
||||
{canInstall && (
|
||||
<button type="button" className="pwa-action install" onClick={() => void requestInstall()}>
|
||||
安装到设备
|
||||
{t('pwa.install')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user