このコミットが含まれているのは:
+22
-1
@@ -26,7 +26,10 @@ const withUserCode = (opt?: Opt): AxiosRequestConfig => {
|
||||
&& { baseURL: currentRenderContext ()!.apiBaseURL, timeout: 10_000 }),
|
||||
...(locale != null && { params: { locale, ...config.params } }),
|
||||
headers: { 'X-Transfer-Code': typeof window === 'undefined'
|
||||
? '' : localStorage.getItem ('user_code') ?? '',
|
||||
? currentRenderContext ()?.transferCode ?? ''
|
||||
: localStorage.getItem ('user_code') ?? '',
|
||||
...(currentRenderContext ()?.forwardedFor != null && {
|
||||
'X-Forwarded-For': currentRenderContext ()!.forwardedFor }),
|
||||
...(config.headers ?? { }) } }
|
||||
}
|
||||
|
||||
@@ -37,6 +40,24 @@ const apiP = async <T> (
|
||||
body?: unknown,
|
||||
opt?: Opt): Promise<T> => {
|
||||
const res = await client[method] (path, body ?? { }, withUserCode (opt))
|
||||
if (typeof window !== 'undefined' && method === 'post'
|
||||
&& ['/users', '/users/verify', '/users/code/renew'].includes (path))
|
||||
{
|
||||
const code = res.data?.code ?? (res.data?.valid ? (body as { code?: string })?.code : null)
|
||||
if (typeof code === 'string')
|
||||
{
|
||||
try
|
||||
{
|
||||
await fetch ('/_ssr/session', {
|
||||
method: 'POST', credentials: 'same-origin',
|
||||
headers: { 'X-Transfer-Code': code } })
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Existing CSR authentication remains usable if the SSR endpoint is unavailable.
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opt?.responseType === 'blob')
|
||||
return res.data as T
|
||||
return toCamel (res.data as Record<string, unknown>, { deep: true }) as T
|
||||
|
||||
@@ -123,6 +123,8 @@ export const materialsKeys = {
|
||||
|
||||
export const wikiKeys = {
|
||||
root: ['wiki'] as const,
|
||||
diff: (id: string, p: { from: string | null; to: string | null }, locale: LocaleCode) =>
|
||||
['wiki', locale, 'diff', id, p] as const,
|
||||
autolinkCandidates: (language: string, locale: LocaleCode) =>
|
||||
['wiki', 'autolink', locale, language] as const,
|
||||
index: (
|
||||
@@ -147,3 +149,13 @@ export const helpKeys = {
|
||||
diff: (identifier: string, language: string, from: string, to: string) =>
|
||||
['help', 'diff', identifier, language, from, to] as const,
|
||||
changes: (language: string) => ['help', 'changes', language] as const }
|
||||
|
||||
export const sessionKeys = {
|
||||
current: ['session', 'current'] as const,
|
||||
settings: (id: number) => ['session', id, 'settings'] as const,
|
||||
themeSlots: (id: number) => ['session', id, 'themeSlots'] as const }
|
||||
|
||||
export const theatresKeys = {
|
||||
show: (id: string, locale: LocaleCode) => ['theatres', locale, id] as const,
|
||||
programmes: (id: string, locale: LocaleCode) =>
|
||||
['theatres', locale, id, 'programmes'] as const }
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
export type RenderContext = {
|
||||
pathname: string
|
||||
origin: string
|
||||
apiBaseURL?: string }
|
||||
apiBaseURL?: string
|
||||
transferCode?: string
|
||||
forwardedFor?: string
|
||||
status?: number }
|
||||
|
||||
|
||||
// The server build replaces this module with the AsyncLocalStorage implementation.
|
||||
|
||||
@@ -10,8 +10,11 @@ export type PagePreparation = {
|
||||
searchParams: URLSearchParams }
|
||||
|
||||
export type PageRouteHandle = {
|
||||
ssr?: (request: PagePreparation) => Promise<number>
|
||||
prepare?: (request: PagePreparation) => Promise<number>
|
||||
locale?: LocaleCode
|
||||
robots?: 'noindex'
|
||||
authentication?: 'user'
|
||||
redirectTo?: string
|
||||
notFound?: boolean }
|
||||
|
||||
export type PageRoute = { handle?: PageRouteHandle } & (
|
||||
|
||||
@@ -791,7 +791,8 @@ export const seedClientThemeMode = (theme: UserSettings['theme']): void => {
|
||||
|
||||
|
||||
const prefersDarkTheme = (): boolean =>
|
||||
window.matchMedia ('(prefers-color-scheme: dark)').matches
|
||||
typeof window !== 'undefined'
|
||||
&& window.matchMedia ('(prefers-color-scheme: dark)').matches
|
||||
|
||||
|
||||
export const resolveThemeMode = (
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { useSyncExternalStore } from 'react'
|
||||
|
||||
const subscribe = () => () => { }
|
||||
const clientSnapshot = () => true
|
||||
const serverSnapshot = () => false
|
||||
|
||||
|
||||
export const useHydrated = (): boolean =>
|
||||
useSyncExternalStore (subscribe, clientSnapshot, serverSnapshot)
|
||||
@@ -99,12 +99,16 @@ export const KeyboardShortcutsProvider = ({ children }: PropsWithChildren) => {
|
||||
const navigate = useLocaleNavigate ()
|
||||
|
||||
const [keyboardSettings, setKeyboardSettingsState] =
|
||||
useState<ClientKeyboardSettings> (() => getClientKeyboardSettings ())
|
||||
useState<ClientKeyboardSettings> ({ enabled: true, bindings: { } })
|
||||
const [registeredHandlers, setRegisteredHandlers] =
|
||||
useState<Record<number, ShortcutHandlers>> ({ })
|
||||
const [shortcutHelpOpen, setShortcutHelpOpen] = useState (false)
|
||||
const registrationIdRef = useRef (0)
|
||||
|
||||
useEffect (() => {
|
||||
setKeyboardSettingsState (getClientKeyboardSettings ())
|
||||
}, [])
|
||||
|
||||
const activeScope = useMemo<ShortcutScope> (
|
||||
() => resolveShortcutScope (location.pathname),
|
||||
[location.pathname],
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする