diff --git a/functions/db/deck/decks-with-cards-schema.ts b/functions/db/deck/decks-with-cards-schema.ts index 2d8f2597..ee112463 100644 --- a/functions/db/deck/decks-with-cards-schema.ts +++ b/functions/db/deck/decks-with-cards-schema.ts @@ -1,6 +1,6 @@ import { z } from "zod"; -const deckCard = z.object({ +export const deckCardSchema = z.object({ id: z.number(), created_at: z.string(), deck_id: z.number(), @@ -21,11 +21,11 @@ export const deckSchema = z.object({ export const deckWithCardsSchema = deckSchema.merge( z.object({ - deck_card: z.array(deckCard), + deck_card: z.array(deckCardSchema), }), ); export const decksWithCardsSchema = z.array(deckWithCardsSchema); export type DeckWithCardsDbType = z.infer; -export type DeckCardDbType = z.infer; +export type DeckCardDbType = z.infer; diff --git a/index.build.html b/index.build.html new file mode 100644 index 00000000..41180b7b --- /dev/null +++ b/index.build.html @@ -0,0 +1,30 @@ + + + + + + + MemoCard + + + + +
+ + + diff --git a/index.html b/index.html index 2ce03f53..f1bce433 100644 --- a/index.html +++ b/index.html @@ -4,31 +4,15 @@ - Vite + React + TS - + MemoCard
- - - - + + diff --git a/package.json b/package.json index 198ff02b..a6ea9ab3 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "dev:frontend:start": "vite", "dev:api:start": "npx wrangler pages dev /functions --compatibility-date=2023-09-22", "dev:tunnel": "../ngrok http --domain=causal-magpie-closing.ngrok-free.app 5173", - "build": "vite build", + "build": "cp index.build.html index.html && vite build", "typecheck": "npx tsc", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "test:api": "npx vitest --dir functions/", diff --git a/src/lib/keyboard/useMacTabNavigationFix.tsx b/src/lib/keyboard/useMacTabNavigationFix.tsx new file mode 100644 index 00000000..56a0057d --- /dev/null +++ b/src/lib/keyboard/useMacTabNavigationFix.tsx @@ -0,0 +1,50 @@ +import { useEffect } from "react"; + +const isMacOS = /Mac OS X/i.test(navigator.userAgent); + +// Custom hook for handling tab navigation in Safari on macOS +// It's either a bug of Telegram for Mac or some issues with Safari WebView +export const useMacTabNavigationFix = () => { + useEffect(() => { + if (!isMacOS) { + return; + } + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key === "Tab") { + event.preventDefault(); + + const inputs = Array.from(document.querySelectorAll("input, textarea")); + const activeElement = document.activeElement; + // @ts-expect-error + const currentIndex = inputs.indexOf(activeElement); + if (currentIndex === -1) { + // No input is currently focused, focus the first input + if (inputs.length > 0) { + // @ts-expect-error + inputs[0].focus(); + } + } else { + if (event.shiftKey && currentIndex > 0) { + // Shift + Tab was pressed + // @ts-expect-error + inputs[currentIndex - 1].focus(); + } else if ( + !event.shiftKey && + currentIndex >= 0 && + currentIndex < inputs.length - 1 + ) { + // Only Tab was pressed + // @ts-expect-error + inputs[currentIndex + 1].focus(); + } + } + } + }; + + // Only keyup works here, events like keydown and keypress don't + document.addEventListener("keyup", handleKeyDown); + return () => { + document.removeEventListener("keyup", handleKeyDown); + }; + }, []); +}; diff --git a/src/screens/app.tsx b/src/screens/app.tsx index b918e1e3..2dd285eb 100644 --- a/src/screens/app.tsx +++ b/src/screens/app.tsx @@ -2,7 +2,7 @@ import { observer } from "mobx-react-lite"; import { MainScreen } from "./deck-list/main-screen.tsx"; import { DeckScreen } from "./deck-review/deck-screen.tsx"; import { ReviewStoreProvider } from "../store/review-store-context.tsx"; -import { Screen, screenStore } from "../store/screen-store.ts"; +import { screenStore } from "../store/screen-store.ts"; import { DeckFormScreen } from "./deck-form/deck-form-screen.tsx"; import { DeckFormStoreProvider } from "../store/deck-form-store-context.tsx"; import { QuickAddCardForm } from "./deck-form/quick-add-card-form.tsx"; @@ -15,19 +15,19 @@ export const App = observer(() => { return (
- {screenStore.screen === Screen.Main && } + {screenStore.screen.type === "main" && } {screenStore.isDeckPreviewScreen && ( )} - {screenStore.screen === Screen.DeckForm && ( + {screenStore.screen.type === "deckForm" && ( )} - {screenStore.screen === Screen.CardQuickAddForm && } - {screenStore.screen === Screen.UserSettings && ( + {screenStore.screen.type === "cardQuickAddForm" && } + {screenStore.screen.type === "userSettings" && ( diff --git a/src/screens/deck-form/card-form-view.tsx b/src/screens/deck-form/card-form-view.tsx index d1447b1f..ff1ef0c1 100644 --- a/src/screens/deck-form/card-form-view.tsx +++ b/src/screens/deck-form/card-form-view.tsx @@ -24,15 +24,15 @@ export const CardFormView = observer((props: Props) => { >

Add card

); diff --git a/src/screens/deck-form/deck-form.tsx b/src/screens/deck-form/deck-form.tsx index 401fea78..a32c90f0 100644 --- a/src/screens/deck-form/deck-form.tsx +++ b/src/screens/deck-form/deck-form.tsx @@ -11,9 +11,13 @@ import { screenStore } from "../../store/screen-store.ts"; import { useMount } from "../../lib/react/use-mount.ts"; import { useBackButton } from "../../lib/telegram/use-back-button.tsx"; import { useTelegramProgress } from "../../lib/telegram/use-telegram-progress.tsx"; +import { assert } from "../../lib/typescript/assert.ts"; +import { useMacTabNavigationFix } from "../../lib/keyboard/useMacTabNavigationFix.tsx"; export const DeckForm = observer(() => { const deckFormStore = useDeckFormStore(); + const screen = screenStore.screen; + assert(screen.type === "deckForm"); useMount(() => { deckFormStore.loadForm(); @@ -25,6 +29,7 @@ export const DeckForm = observer(() => { deckFormStore.onDeckBack(); }); useTelegramProgress(() => deckFormStore.isSending); + useMacTabNavigationFix(); if (!deckFormStore.form) { return null; @@ -41,15 +46,15 @@ export const DeckForm = observer(() => { })} >

- {screenStore.deckFormId ? "Edit deck" : "Add deck"} + {screen.deckId ? "Edit deck" : "Add deck"}