style: trim narration comments from recent changes
This commit is contained in:
@@ -140,8 +140,6 @@ export const Route = createRootRouteWithContext<{
|
||||
);
|
||||
const locale = resolveLocale(auth?.metadata?.locale);
|
||||
await ensureMessages(locale);
|
||||
// Dehydrate the catalog with the page so the client hydrates in the
|
||||
// active locale instead of falling back to en until a chunk loads.
|
||||
if (typeof window === "undefined" && locale !== DEFAULT_LOCALE) {
|
||||
context.queryClient.setQueryData(
|
||||
i18nMessagesQueryKey(locale),
|
||||
|
||||
@@ -5,7 +5,6 @@ import { Trans, useLingui } from '@lingui/react/macro'
|
||||
import { useAuth } from '@/contexts/auth-context'
|
||||
import { useIsMobile } from '@/hooks/use-is-mobile'
|
||||
|
||||
// Navbar geometry: 4rem height + 0.5rem margin + its safe-area bottom offset.
|
||||
const ABOVE_MOBILE_NAV_OFFSET = 'calc(4.5rem + env(safe-area-inset-bottom, 0px))'
|
||||
|
||||
export function IOSInstallPrompt() {
|
||||
@@ -43,8 +42,6 @@ export function IOSInstallPrompt() {
|
||||
? t`Tap Share → Add to Home Screen`
|
||||
: t`Tap Menu (⋮) → Add to Home screen`
|
||||
|
||||
// Navbar renders only under _authed, so a signed-in mobile user on a public
|
||||
// route has no nav to clear.
|
||||
const aboveBottomNav = Boolean(user) && isMobile
|
||||
const bottomStyle = aboveBottomNav
|
||||
? { bottom: ABOVE_MOBILE_NAV_OFFSET, paddingBottom: '8px' }
|
||||
|
||||
@@ -5,9 +5,6 @@ const eventListerOptions = {
|
||||
};
|
||||
|
||||
const useVisualViewportSize = () => {
|
||||
// Starts at zero on server and client alike, filling in after mount — the
|
||||
// same contract as Mantine's useViewportSize. Reading window during render
|
||||
// makes the client's first render disagree with the SSR markup.
|
||||
const [windowSize, setWindowSize] = useState({
|
||||
width: 0,
|
||||
height: 0,
|
||||
|
||||
@@ -41,7 +41,6 @@ export async function ensureMessages(locale: AppLocale): Promise<Messages> {
|
||||
const loader = catalogLoaders[`/src/locales/${locale}/messages.po`];
|
||||
if (!loader) return messageCache[DEFAULT_LOCALE]!;
|
||||
|
||||
// Dedupe concurrent loads; a rejected load is dropped so a retry can succeed.
|
||||
const pending = (inflightLoads[locale] ??= loader()
|
||||
.then(({ messages }) => {
|
||||
messageCache[locale] = messages;
|
||||
@@ -57,8 +56,6 @@ export function primeMessages(locale: AppLocale, messages: Messages) {
|
||||
messageCache[locale] ??= messages;
|
||||
}
|
||||
|
||||
// Seeded into the SSR-dehydrated query cache so hydration renders the active
|
||||
// locale without depending on a client-side catalog chunk load.
|
||||
export const i18nMessagesQueryKey = (locale: AppLocale) =>
|
||||
["i18n-messages", locale] as const;
|
||||
|
||||
|
||||
@@ -36,11 +36,6 @@ export const LinguiProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const locale = resolveLocale(data?.metadata?.locale);
|
||||
const [catalogVersion, setCatalogVersion] = useState(0);
|
||||
|
||||
// Fresh instance per locale (and per SSR request tree). __root beforeLoad
|
||||
// dehydrates the active locale's catalog with the page, so hydration primes
|
||||
// it from the query cache and never regresses to en for the SSR'd locale.
|
||||
// Public routes (login) may still lack it — createI18n falls back to en
|
||||
// until the effect below loads it and bumps catalogVersion.
|
||||
const i18n = useMemo(
|
||||
() => {
|
||||
if (!hasCachedMessages(locale)) {
|
||||
@@ -59,8 +54,6 @@ export const LinguiProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
if (hasCachedMessages(locale)) return;
|
||||
let cancelled = false;
|
||||
let retryTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
// A failed chunk load must not strand the UI in en — retry until the
|
||||
// catalog arrives or the locale changes.
|
||||
const load = () => {
|
||||
ensureMessages(locale).then(
|
||||
() => {
|
||||
|
||||
@@ -19,17 +19,13 @@ class PocketBaseAdminClient {
|
||||
this.pb = new PocketBase(process.env.POCKETBASE_URL);
|
||||
|
||||
this.pb.beforeSend = async (url, options) => {
|
||||
// The auth requests themselves must skip the gate below: gating them on
|
||||
// authPromise would make them await their own completion (deadlock).
|
||||
// Auth requests skip the gate: awaiting authPromise here would deadlock them.
|
||||
if (!url.includes("/collections/_superusers/auth-")) {
|
||||
try {
|
||||
await this.authPromise;
|
||||
} catch {
|
||||
// Swallow: fall through to self-heal so a rejected authPromise can't strand every request.
|
||||
}
|
||||
} catch {}
|
||||
|
||||
if (!this.pb.authStore.isValid) {
|
||||
// Self-heal: re-auth once PocketBase is reachable again, no restart.
|
||||
this.authPromise = this.authenticate();
|
||||
await this.authPromise;
|
||||
this.startTokenRefresh();
|
||||
@@ -43,8 +39,6 @@ class PocketBaseAdminClient {
|
||||
}
|
||||
}
|
||||
|
||||
// The SDK stamps Authorization before this hook runs, so a request
|
||||
// built while auth was still in flight carries no (or a stale) token.
|
||||
options.headers = {
|
||||
...options.headers,
|
||||
Authorization: this.pb.authStore.token,
|
||||
@@ -75,8 +69,6 @@ class PocketBaseAdminClient {
|
||||
Object.assign(this, createPushService(this.pb));
|
||||
|
||||
this.authPromise = this.authenticate();
|
||||
// Fail soft at boot: an unreachable PocketBase must not crash the process;
|
||||
// beforeSend self-heals on the next request.
|
||||
this.authPromise
|
||||
.then(() => {
|
||||
this.startTokenRefresh();
|
||||
|
||||
@@ -2,17 +2,9 @@ import type PocketBase from "pocketbase";
|
||||
|
||||
export type FilterParam = string | number | boolean | Date | null;
|
||||
|
||||
// Any key the SDK's own `replaceAll("{:" + key + "}", …)` loop would substitute,
|
||||
// so which keys resolve does not depend on the characters they are spelled with.
|
||||
const PLACEHOLDER = /\{:([^}]+)\}/g;
|
||||
|
||||
// `pb.filter` substitutes with String.replaceAll and a *string* replacement, so
|
||||
// `$&`, `` $` ``, `$'` and `$$` inside a value are expanded as replacement
|
||||
// patterns: the value's own text, or a slice of the surrounding expression,
|
||||
// gets spliced into the quoted literal. Doubling every `$` first collapses back
|
||||
// to the exact literal inside that same replaceAll. The FilterParam union is
|
||||
// load-bearing — it keeps objects and arrays out of the SDK's JSON.stringify
|
||||
// branch, which would reintroduce an undoubled `$`.
|
||||
// pb.filter $-expands string replacements; doubling `$` keeps the value literal.
|
||||
const quote = (pb: PocketBase, value: FilterParam) =>
|
||||
pb.filter("{:v}", {
|
||||
v: typeof value === "string" ? value.replaceAll("$", () => "$$") : value,
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
// `pb.filter()` escapes single quotes and nothing else, so a term ending in a
|
||||
// backslash escapes the closing quote of the literal it is substituted into and
|
||||
// PocketBase rejects the whole expression with 400 validation_invalid_filter.
|
||||
// Escaping `\ % _` and appending the wildcards here keeps the operand's last
|
||||
// character a literal `%`, and makes `~` an unconditional substring match:
|
||||
// PocketBase only auto-wraps (and only auto-escapes) operands that contain no
|
||||
// `%` of their own, so a term carrying one would otherwise silently become a
|
||||
// prefix match, and a bare `_` would match every row.
|
||||
// Escapes LIKE metacharacters and wraps in `%` for a literal substring match.
|
||||
export const likePattern = (term: string) =>
|
||||
`%${term.replace(/[\\%_]/g, (char) => `\\${char}`)}%`;
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
// Shared redaction for logs + audit rows so the two never drift.
|
||||
export const SENSITIVE_KEY = /token|secret|password|phone|otp|code|auth|key/i;
|
||||
|
||||
const REDACTED = "[redacted]";
|
||||
|
||||
// Deep-redact: keys matching SENSITIVE_KEY become "[redacted]"; primitives pass through.
|
||||
export const redactValue = (value: unknown): unknown => {
|
||||
if (Array.isArray(value)) return value.map(redactValue);
|
||||
if (value && typeof value === "object") {
|
||||
// Keep Error serializable.
|
||||
if (value instanceof Error) {
|
||||
return { name: value.name, message: value.message, stack: value.stack };
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@ export const createServerError = (
|
||||
context,
|
||||
});
|
||||
|
||||
// Audit rows are written by serverFnLoggingMiddleware, which reads the
|
||||
// returned envelope's success flag — never write them here.
|
||||
export const toServerResult = async <T>(
|
||||
serverFn: () => Promise<T>
|
||||
): Promise<ServerResult<T>> => {
|
||||
|
||||
@@ -392,7 +392,7 @@ msgstr "Aktivitäten"
|
||||
msgid "Activity Details"
|
||||
msgstr "Aktivitätsdetails"
|
||||
|
||||
#: src/components/ios-install-prompt.tsx:61
|
||||
#: src/components/ios-install-prompt.tsx:58
|
||||
msgid "Add FLXN to your home screen for a smoother experience"
|
||||
msgstr "Leg FLXN auf deinen Homescreen für ein flüssigeres Erlebnis"
|
||||
|
||||
@@ -845,7 +845,7 @@ msgstr "deviceId ist für die Übertragungsaktion erforderlich"
|
||||
msgid "Disconnect Spotify"
|
||||
msgstr "Spotify trennen"
|
||||
|
||||
#: src/components/ios-install-prompt.tsx:68
|
||||
#: src/components/ios-install-prompt.tsx:65
|
||||
msgid "Dismiss"
|
||||
msgstr "Schließen"
|
||||
|
||||
@@ -2324,11 +2324,11 @@ msgstr "T"
|
||||
msgid "Tap an opponent below to compare"
|
||||
msgstr "Tippe unten auf einen Gegner zum Vergleichen"
|
||||
|
||||
#: src/components/ios-install-prompt.tsx:44
|
||||
#: src/components/ios-install-prompt.tsx:43
|
||||
msgid "Tap Menu (⋮) → Add to Home screen"
|
||||
msgstr "Tippe auf Menü (⋮) → Zum Home-Bildschirm hinzufügen"
|
||||
|
||||
#: src/components/ios-install-prompt.tsx:43
|
||||
#: src/components/ios-install-prompt.tsx:42
|
||||
msgid "Tap Share → Add to Home Screen"
|
||||
msgstr "Tippe auf Teilen → Zum Home-Bildschirm hinzufügen"
|
||||
|
||||
|
||||
@@ -392,7 +392,7 @@ msgstr "Activities"
|
||||
msgid "Activity Details"
|
||||
msgstr "Activity Details"
|
||||
|
||||
#: src/components/ios-install-prompt.tsx:61
|
||||
#: src/components/ios-install-prompt.tsx:58
|
||||
msgid "Add FLXN to your home screen for a smoother experience"
|
||||
msgstr "Add FLXN to your home screen for a smoother experience"
|
||||
|
||||
@@ -845,7 +845,7 @@ msgstr "deviceId is required for transfer action"
|
||||
msgid "Disconnect Spotify"
|
||||
msgstr "Disconnect Spotify"
|
||||
|
||||
#: src/components/ios-install-prompt.tsx:68
|
||||
#: src/components/ios-install-prompt.tsx:65
|
||||
msgid "Dismiss"
|
||||
msgstr "Dismiss"
|
||||
|
||||
@@ -2324,11 +2324,11 @@ msgstr "T"
|
||||
msgid "Tap an opponent below to compare"
|
||||
msgstr "Tap an opponent below to compare"
|
||||
|
||||
#: src/components/ios-install-prompt.tsx:44
|
||||
#: src/components/ios-install-prompt.tsx:43
|
||||
msgid "Tap Menu (⋮) → Add to Home screen"
|
||||
msgstr "Tap Menu (⋮) → Add to Home screen"
|
||||
|
||||
#: src/components/ios-install-prompt.tsx:43
|
||||
#: src/components/ios-install-prompt.tsx:42
|
||||
msgid "Tap Share → Add to Home Screen"
|
||||
msgstr "Tap Share → Add to Home Screen"
|
||||
|
||||
|
||||
@@ -392,7 +392,7 @@ msgstr "Actividades"
|
||||
msgid "Activity Details"
|
||||
msgstr "Detalles de la actividad"
|
||||
|
||||
#: src/components/ios-install-prompt.tsx:61
|
||||
#: src/components/ios-install-prompt.tsx:58
|
||||
msgid "Add FLXN to your home screen for a smoother experience"
|
||||
msgstr "Agrega FLXN a tu pantalla de inicio para una experiencia más fluida"
|
||||
|
||||
@@ -845,7 +845,7 @@ msgstr "Se requiere deviceId para la acción de transferencia"
|
||||
msgid "Disconnect Spotify"
|
||||
msgstr "Desconectar Spotify"
|
||||
|
||||
#: src/components/ios-install-prompt.tsx:68
|
||||
#: src/components/ios-install-prompt.tsx:65
|
||||
msgid "Dismiss"
|
||||
msgstr "Cerrar"
|
||||
|
||||
@@ -2324,11 +2324,11 @@ msgstr "T"
|
||||
msgid "Tap an opponent below to compare"
|
||||
msgstr "Toca un oponente abajo para comparar"
|
||||
|
||||
#: src/components/ios-install-prompt.tsx:44
|
||||
#: src/components/ios-install-prompt.tsx:43
|
||||
msgid "Tap Menu (⋮) → Add to Home screen"
|
||||
msgstr "Toca Menú (⋮) → Agregar a pantalla de inicio"
|
||||
|
||||
#: src/components/ios-install-prompt.tsx:43
|
||||
#: src/components/ios-install-prompt.tsx:42
|
||||
msgid "Tap Share → Add to Home Screen"
|
||||
msgstr "Toca Compartir → Agregar a pantalla de inicio"
|
||||
|
||||
|
||||
@@ -392,7 +392,7 @@ msgstr "アクティビティ"
|
||||
msgid "Activity Details"
|
||||
msgstr "アクティビティの詳細"
|
||||
|
||||
#: src/components/ios-install-prompt.tsx:61
|
||||
#: src/components/ios-install-prompt.tsx:58
|
||||
msgid "Add FLXN to your home screen for a smoother experience"
|
||||
msgstr "FLXNをホーム画面に追加すると、もっと快適に使えます"
|
||||
|
||||
@@ -845,7 +845,7 @@ msgstr "転送操作にはdeviceIdが必要です"
|
||||
msgid "Disconnect Spotify"
|
||||
msgstr "Spotifyの連携を解除"
|
||||
|
||||
#: src/components/ios-install-prompt.tsx:68
|
||||
#: src/components/ios-install-prompt.tsx:65
|
||||
msgid "Dismiss"
|
||||
msgstr "閉じる"
|
||||
|
||||
@@ -2324,11 +2324,11 @@ msgstr "T"
|
||||
msgid "Tap an opponent below to compare"
|
||||
msgstr "下の対戦相手をタップして比較"
|
||||
|
||||
#: src/components/ios-install-prompt.tsx:44
|
||||
#: src/components/ios-install-prompt.tsx:43
|
||||
msgid "Tap Menu (⋮) → Add to Home screen"
|
||||
msgstr "メニュー (⋮) をタップ →「ホーム画面に追加」"
|
||||
|
||||
#: src/components/ios-install-prompt.tsx:43
|
||||
#: src/components/ios-install-prompt.tsx:42
|
||||
msgid "Tap Share → Add to Home Screen"
|
||||
msgstr "共有をタップ →「ホーム画面に追加」"
|
||||
|
||||
|
||||
@@ -41,19 +41,13 @@ type Actor = { player_id?: string };
|
||||
|
||||
type ServerFnMeta = { name?: string };
|
||||
|
||||
// Start's default generateFunctionId hashes the entry id, so the URL segment is
|
||||
// a sha256 and the compile-time meta is the only readable name. The path stays
|
||||
// as the fallback for requests that carry no meta.
|
||||
// The URL segment is a hash of the fn id; serverFnMeta carries the readable name.
|
||||
const serverFnName = (request: Request, meta?: ServerFnMeta): string => {
|
||||
if (meta?.name) return meta.name;
|
||||
const pathParts = new URL(request.url).pathname.split("/").filter(Boolean);
|
||||
return pathParts[pathParts.length - 1] || "unknown";
|
||||
};
|
||||
|
||||
// The admin middleware refuses before serverFnLoggingMiddleware ever runs, so
|
||||
// a denial has to write its own row — otherwise an operator reviewing the log
|
||||
// during a probing attempt sees a clean history. The rejected payload is left
|
||||
// out: it never reached a validator, so it is unbounded attacker input.
|
||||
export const recordDeniedServerFn = (
|
||||
request: Request,
|
||||
actor?: Actor,
|
||||
@@ -85,7 +79,6 @@ export const serverFnLoggingMiddleware = createMiddleware({
|
||||
const result = await next();
|
||||
const duration = Date.now() - startTime;
|
||||
|
||||
// Single audit writer; toServerResult resolves { success:false } instead of throwing, so read the flag.
|
||||
const envelope = (result as { result?: unknown })?.result;
|
||||
const failed =
|
||||
!!envelope &&
|
||||
|
||||
@@ -62,7 +62,6 @@ export const superTokensAdminFunctionMiddleware = createMiddleware({
|
||||
return next({ context });
|
||||
}
|
||||
|
||||
// Identifiers only — the full context carries phone + metadata.
|
||||
logger.error("Unauthorized user in admin function.", {
|
||||
userAuthId: context.userAuthId,
|
||||
roles: context.roles,
|
||||
|
||||
Reference in New Issue
Block a user