mirror of
https://github.com/wu736139669/hapi.git
synced 2026-10-09 19:29:41 +00:00
* fix codex session import merge (#1123) 修复 Codex 会话导入合并后列表为空的问题。 Fix Codex session import merge so a session is not detected as a duplicate of itself and deleted during merge. Co-authored-by: LIUZHIRU <ryuu@fine-net.co.jp> * fix(web): keep overlays below PWA status bar Account for env(safe-area-inset-top) on fixed top banners, image preview chrome, and session action menus so installed PWA mode no longer draws controls under the OS status bar. Fixes #1066 --------- Co-authored-by: Himehane <36065996+Himehane@users.noreply.github.com> Co-authored-by: LIUZHIRU <ryuu@fine-net.co.jp>
80 lines
2.8 KiB
TypeScript
80 lines
2.8 KiB
TypeScript
import { useOnlineStatus } from '@/hooks/useOnlineStatus'
|
|
import { usePlatform } from '@/hooks/usePlatform'
|
|
import { usePwaUpdateContext } from '@/lib/pwa-update-context'
|
|
import { useTranslation } from '@/lib/use-translation'
|
|
import { useVoiceOptional } from '@/lib/voice-context'
|
|
|
|
export function PwaUpdateBanner({ topClassName }: { topClassName?: string } = {}) {
|
|
const { t } = useTranslation()
|
|
const { needRefresh, reload } = usePwaUpdateContext()
|
|
const isOnline = useOnlineStatus()
|
|
const { haptic } = usePlatform()
|
|
|
|
if (!needRefresh) {
|
|
return null
|
|
}
|
|
|
|
const topClass = topClassName ?? (isOnline
|
|
? 'top-[calc(env(safe-area-inset-top)+0.5rem)]'
|
|
: 'top-[calc(env(safe-area-inset-top)+2.5rem)]')
|
|
|
|
return (
|
|
<div
|
|
data-testid="pwa-update-banner"
|
|
className={`fixed left-4 right-4 bg-[var(--app-secondary-bg)] border border-[var(--app-border)] rounded-lg p-4 shadow-lg z-50 ${topClass}`}
|
|
>
|
|
<div className="flex items-center justify-between gap-3">
|
|
<div className="flex-1 min-w-0">
|
|
<p className="text-sm font-medium text-[var(--app-fg)]">
|
|
{t('pwa.update.title')}
|
|
</p>
|
|
<p className="text-xs text-[var(--app-hint)] mt-0.5">
|
|
{t('pwa.update.body')}
|
|
</p>
|
|
</div>
|
|
<button
|
|
type="button"
|
|
onClick={() => {
|
|
haptic.impact('light')
|
|
reload()
|
|
}}
|
|
className="shrink-0 px-4 py-2 bg-[var(--app-fg)] text-[var(--app-bg)] rounded-lg text-sm font-medium active:opacity-80"
|
|
>
|
|
{t('pwa.update.reload')}
|
|
</button>
|
|
</div>
|
|
|
|
<details className="mt-3 border-t border-[var(--app-border)] pt-2">
|
|
<summary className="cursor-pointer text-xs text-[var(--app-link)] active:opacity-60 list-none [&::-webkit-details-marker]:hidden">
|
|
{t('pwa.update.whyToggle')}
|
|
</summary>
|
|
<p className="mt-2 text-xs text-[var(--app-hint)] leading-relaxed">
|
|
{t('pwa.update.whyBody')}
|
|
</p>
|
|
</details>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export function PwaUpdateBannerWithStatusOffset({
|
|
isSyncing,
|
|
isReconnecting,
|
|
}: {
|
|
isSyncing: boolean
|
|
isReconnecting: boolean
|
|
}) {
|
|
const voice = useVoiceOptional()
|
|
const hasTopStatusBanner =
|
|
isSyncing ||
|
|
isReconnecting ||
|
|
Boolean(voice && voice.status === 'error' && voice.errorMessage)
|
|
|
|
return (
|
|
<PwaUpdateBanner
|
|
topClassName={hasTopStatusBanner
|
|
? 'top-[calc(env(safe-area-inset-top)+3rem)]'
|
|
: undefined}
|
|
/>
|
|
)
|
|
}
|