fix(web): fix push notification click 404 on GitHub Pages (#322)

This commit is contained in:
pppobear
2026-03-20 08:46:21 +08:00
committed by GitHub
parent d76b1a6ac0
commit f539f10507
4 changed files with 111 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>HAPI</title>
<script>
sessionStorage.setItem('spaRedirect', window.location.pathname + window.location.search + window.location.hash)
window.location.replace('/')
</script>
</head>
<body></body>
</html>
+69
View File
@@ -0,0 +1,69 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { restoreSpaRedirect, storeSpaRedirect } from './spaRedirect'
describe('spaRedirect', () => {
beforeEach(() => {
sessionStorage.clear()
vi.spyOn(window.history, 'replaceState')
})
afterEach(() => {
vi.restoreAllMocks()
})
describe('restoreSpaRedirect', () => {
it('restores stored path via replaceState', () => {
sessionStorage.setItem('spaRedirect', '/sessions/abc123')
restoreSpaRedirect()
expect(window.history.replaceState).toHaveBeenCalledWith(null, '', '/sessions/abc123')
})
it('removes spaRedirect from sessionStorage after restoring', () => {
sessionStorage.setItem('spaRedirect', '/sessions/abc123')
restoreSpaRedirect()
expect(sessionStorage.getItem('spaRedirect')).toBeNull()
})
it('does nothing when spaRedirect is not set', () => {
restoreSpaRedirect()
expect(window.history.replaceState).not.toHaveBeenCalled()
})
it('preserves query string and hash in restored path', () => {
sessionStorage.setItem('spaRedirect', '/sessions/abc123?foo=bar#section')
restoreSpaRedirect()
expect(window.history.replaceState).toHaveBeenCalledWith(null, '', '/sessions/abc123?foo=bar#section')
})
})
describe('storeSpaRedirect', () => {
it('stores the current pathname in sessionStorage', () => {
Object.defineProperty(window, 'location', {
value: { pathname: '/sessions/abc123', search: '', hash: '' },
configurable: true,
})
storeSpaRedirect()
expect(sessionStorage.getItem('spaRedirect')).toBe('/sessions/abc123')
})
it('stores pathname with search and hash', () => {
Object.defineProperty(window, 'location', {
value: { pathname: '/sessions/abc123', search: '?foo=bar', hash: '#section' },
configurable: true,
})
storeSpaRedirect()
expect(sessionStorage.getItem('spaRedirect')).toBe('/sessions/abc123?foo=bar#section')
})
})
})
+22
View File
@@ -0,0 +1,22 @@
const SPA_REDIRECT_KEY = 'spaRedirect'
/**
* Stores the current path in sessionStorage before GitHub Pages redirects to /.
* Called from public/404.html when GitHub Pages serves a 404 for SPA routes.
*/
export function storeSpaRedirect(): void {
const path = window.location.pathname + window.location.search + window.location.hash
sessionStorage.setItem(SPA_REDIRECT_KEY, path)
}
/**
* Restores the path stored by storeSpaRedirect() using replaceState,
* so TanStack Router initializes at the correct URL without a server round-trip.
*/
export function restoreSpaRedirect(): void {
const redirect = sessionStorage.getItem(SPA_REDIRECT_KEY)
if (redirect) {
sessionStorage.removeItem(SPA_REDIRECT_KEY)
window.history.replaceState(null, '', redirect)
}
}
+8
View File
@@ -10,6 +10,7 @@ import { getTelegramWebApp, isTelegramEnvironment, loadTelegramSdk } from './hoo
import { queryClient } from './lib/query-client'
import { createAppRouter } from './router'
import { I18nProvider } from './lib/i18n-context'
import { restoreSpaRedirect } from './lib/spaRedirect'
function getStartParam(): string | null {
const query = new URLSearchParams(window.location.search)
@@ -41,6 +42,13 @@ async function bootstrap() {
await loadTelegramSdk()
}
// Handle GitHub Pages 404 redirect for SPA routing
// When GitHub Pages can't find a path (e.g. /sessions/xxx), it serves 404.html
// which stores the path in sessionStorage and redirects to /
if (!isTelegram) {
restoreSpaRedirect()
}
const updateSW = registerSW({
onNeedRefresh() {
if (confirm('New version available! Reload to update?')) {