From a593a36d14131a87bbc2fa7e542537b75ab26444 Mon Sep 17 00:00:00 2001 From: Gorbachev Egor <7gorbachevm@gmail.com> Date: Fri, 17 Nov 2023 12:30:44 +0700 Subject: [PATCH 1/6] Router allow go back --- index.build.html | 30 +++++++++++ index.html | 26 ++-------- package.json | 2 +- src/screens/app.tsx | 10 ++-- src/screens/deck-form/deck-form.tsx | 5 +- src/screens/deck-list/main-screen.tsx | 6 +-- src/screens/deck-list/my-deck.tsx | 2 +- src/screens/deck-list/public-deck.tsx | 2 +- src/screens/deck-review/deck-finished.tsx | 2 +- src/screens/deck-review/deck-preview.tsx | 7 +-- src/screens/user-settings/user-settings.tsx | 2 +- src/store/deck-form-store.test.ts | 5 +- src/store/deck-form-store.ts | 18 ++++--- src/store/deck-list-store.ts | 31 ++++++----- src/store/quick-add-card-form-store.ts | 14 +++-- src/store/review-store.ts | 4 +- src/store/screen-store.ts | 57 ++++++++------------- 17 files changed, 121 insertions(+), 102 deletions(-) create mode 100644 index.build.html 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/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/deck-form.tsx b/src/screens/deck-form/deck-form.tsx index 401fea78..c1d83d91 100644 --- a/src/screens/deck-form/deck-form.tsx +++ b/src/screens/deck-form/deck-form.tsx @@ -11,9 +11,12 @@ 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"; export const DeckForm = observer(() => { const deckFormStore = useDeckFormStore(); + const screen = screenStore.screen; + assert(screen.type === 'deckForm') useMount(() => { deckFormStore.loadForm(); @@ -41,7 +44,7 @@ export const DeckForm = observer(() => { })} >

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

); diff --git a/src/screens/useMacTabNavigationFix.tsx b/src/screens/useMacTabNavigationFix.tsx new file mode 100644 index 00000000..2e146795 --- /dev/null +++ b/src/screens/useMacTabNavigationFix.tsx @@ -0,0 +1,46 @@ +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/user-settings/settings-row.tsx b/src/screens/user-settings/settings-row.tsx index a7d8eb08..23b03a27 100644 --- a/src/screens/user-settings/settings-row.tsx +++ b/src/screens/user-settings/settings-row.tsx @@ -12,8 +12,9 @@ export const SettingsRow = observer((props: Props) => { className={css({ backgroundColor: theme.secondaryBgColor, borderRadius: theme.borderRadius, - height: 40, - padding: "0px 12px", + height: 48, + boxSizing: 'border-box', + padding: 12, display: "flex", justifyContent: "space-between", alignItems: "center", diff --git a/src/screens/user-settings/user-review-notification-settings.tsx b/src/screens/user-settings/user-review-notification-settings.tsx deleted file mode 100644 index 6dc51ea1..00000000 --- a/src/screens/user-settings/user-review-notification-settings.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { observer } from "mobx-react-lite"; -import { useBackButton } from "../../lib/telegram/use-back-button.tsx"; -import { ListHeader } from "../../ui/list-header.tsx"; -import { Hint } from "../../ui/hint.tsx"; -import React from "react"; -import { useUserSettingsStore } from "../../store/user-settings-store-context.tsx"; -import { css } from "@emotion/css"; -import { theme } from "../../ui/theme.tsx"; -import { RadioSwitcher } from "../../ui/radio-switcher.tsx"; -import { Select } from "../../ui/select.tsx"; -import { SettingsRow } from "./settings-row.tsx"; -import { generateTimeRange } from "./generate-time-range.tsx"; -import { useMainButton } from "../../lib/telegram/use-main-button.tsx"; -import { useTelegramProgress } from "../../lib/telegram/use-telegram-progress.tsx"; - -export const timeRanges = generateTimeRange(); - -export const UserReviewNotificationSettings = observer(() => { - const userSettingsStore = useUserSettingsStore(); - - useBackButton(() => { - userSettingsStore.goToMain(); - }); - - useMainButton( - "Save", - () => userSettingsStore.submit(), - () => userSettingsStore.isSaveVisible, - ); - - useTelegramProgress(() => userSettingsStore.isSending); - - if (!userSettingsStore.form) { - return null; - } - - const { isRemindNotifyEnabled, time } = userSettingsStore.form; - - return ( -
- - -
- - Notifications - - - - - {isRemindNotifyEnabled.value && ( - - Time -
- { + time.onChange(value); + }} + options={timeRanges.map((range) => ({ + value: range, + label: range, + }))} + /> +
+
+ )} + + + ⭐ Daily reminders help you keep up with your reviews, making it + easier for you to remember the information + +
+
+ ); }); diff --git a/src/screens/user-settings/user-settings.tsx b/src/screens/user-settings/user-settings.tsx deleted file mode 100644 index 9522601c..00000000 --- a/src/screens/user-settings/user-settings.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from "react"; -import { observer } from "mobx-react-lite"; -import { useBackButton } from "../../lib/telegram/use-back-button.tsx"; -import { screenStore } from "../../store/screen-store.ts"; -import { ListHeader } from "../../ui/list-header.tsx"; -import { css } from "@emotion/css"; -import { theme } from "../../ui/theme.tsx"; -import { useUserSettingsStore } from "../../store/user-settings-store-context.tsx"; - -export const UserSettings = observer(() => { - const userSettingsStore = useUserSettingsStore(); - - useBackButton(() => { - screenStore.back(); - }); - - return ( -
- -
{ - userSettingsStore.goToUserReviewSettings(); - }} - > - Review notification - - {userSettingsStore.notifyTimePreview} - -
-
- ); -}); diff --git a/src/store/deck-list-store.ts b/src/store/deck-list-store.ts index 553e9452..e73503a5 100644 --- a/src/store/deck-list-store.ts +++ b/src/store/deck-list-store.ts @@ -142,7 +142,6 @@ export class DeckListStore { } const decksToSearch = this.myInfo.value.myDecks.concat(this.publicDecks); - const deck = decksToSearch.find((deck) => deck.id === screen.deckId); if (!deck) { return null; @@ -188,7 +187,7 @@ export class DeckListStore { ); } - updateSettings( + optimisticUpdateSettings( body: Pick, ) { assert(this.myInfo?.state === "fulfilled"); diff --git a/src/store/quick-add-card-form-store.ts b/src/store/quick-add-card-form-store.ts index 26704461..bd62681a 100644 --- a/src/store/quick-add-card-form-store.ts +++ b/src/store/quick-add-card-form-store.ts @@ -11,6 +11,8 @@ import { showConfirm } from "../lib/telegram/show-confirm.ts"; import { addCardRequest } from "../api/api.ts"; import { assert } from "../lib/typescript/assert.ts"; import { TextField } from "../lib/mobx-form/mobx-form.ts"; +import { AddCardRequest } from "../../functions/add-card.ts"; +import { deckListStore } from "./deck-list-store.ts"; export class QuickAddCardFormStore { form: CardFormType = { @@ -35,16 +37,19 @@ export class QuickAddCardFormStore { this.isSending = true; - return addCardRequest({ + const body: AddCardRequest = { deckId: screen.cardQuickAddDeckId, card: { back: this.form.back.value, front: this.form.front.value, example: this.form.example.value, }, - }) + }; + + return addCardRequest(body) .then(() => { screenStore.back(); + deckListStore.load(); }) .finally( action(() => { diff --git a/src/store/user-settings-store.tsx b/src/store/user-settings-store.tsx index 8fe5fca7..592ecc42 100644 --- a/src/store/user-settings-store.tsx +++ b/src/store/user-settings-store.tsx @@ -6,15 +6,11 @@ import { DateTime } from "luxon"; import { formatTime } from "../screens/user-settings/generate-time-range.tsx"; import { isFormTouched } from "../lib/mobx-form/form-has-error.ts"; import { userSettingsRequest } from "../api/api.ts"; - -export enum UserSettingsScreen { - UserReviewSettings = "userReviewSettings", -} +import { screenStore } from "./screen-store.ts"; const DEFAULT_TIME = "12:00"; export class UserSettingsStore { - screen?: UserSettingsScreen; form?: { isRemindNotifyEnabled: BooleanField; time: TextField; @@ -43,30 +39,10 @@ export class UserSettingsStore { }; } - goToUserReviewSettings() { - this.screen = UserSettingsScreen.UserReviewSettings; - } - - goToMain() { - this.screen = undefined; - } - get isSaveVisible() { return !!this.form && isFormTouched(this.form); } - get notifyTimePreview() { - if (!this.form) { - return null; - } - - if (!this.form.isRemindNotifyEnabled.value) { - return "Off"; - } - - return this.form?.time?.value || "Not set"; - } - submit() { this.isSending = true; assert(this.form); @@ -87,11 +63,11 @@ export class UserSettingsStore { userSettingsRequest(body) .then(() => { - deckListStore.updateSettings({ + deckListStore.optimisticUpdateSettings({ is_remind_enabled: body.isRemindNotifyEnabled, last_reminded_date: body.remindNotificationTime, }); - this.goToMain(); + screenStore.go({ type: "main" }); }) .finally( action(() => { diff --git a/src/ui/radio-switcher.tsx b/src/ui/radio-switcher.tsx index 9fc02333..92aaf8b0 100644 --- a/src/ui/radio-switcher.tsx +++ b/src/ui/radio-switcher.tsx @@ -5,7 +5,6 @@ import { theme } from "./theme.tsx"; const config = { colors: { blueLight: theme.success, - grayLight: "#98a9bc", grayLightExtra: "#e8ecef", white: "#fff", }, @@ -59,7 +58,7 @@ export const RadioSwitcher = (props: Props) => { justifyContent: "center", alignItems: "center", position: "absolute", - backgroundColor: config.colors.grayLight, + backgroundColor: config.colors.white, boxShadow: "0 0 8px rgba(56, 0, 107, 0.16)", width: config.cursorSize, height: config.cursorSize, @@ -71,7 +70,6 @@ export const RadioSwitcher = (props: Props) => { }), isOn && css({ - backgroundColor: config.colors.white, left: `calc(100% - ${config.cursorSize}px - 4px)`, }), )} From 6028ab56de7dc69ba6ca484f86485a54f6c44764 Mon Sep 17 00:00:00 2001 From: Gorbachev Egor <7gorbachevm@gmail.com> Date: Fri, 17 Nov 2023 16:05:58 +0700 Subject: [PATCH 4/6] todo --- src/screens/deck-form/deck-form.tsx | 2 +- src/screens/deck-review/deck-preview.tsx | 4 ++-- src/store/deck-form-store.ts | 6 +++--- src/store/quick-add-card-form-store.ts | 2 +- src/store/screen-store.ts | 6 ++---- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/screens/deck-form/deck-form.tsx b/src/screens/deck-form/deck-form.tsx index 064ff60a..dc81b3f9 100644 --- a/src/screens/deck-form/deck-form.tsx +++ b/src/screens/deck-form/deck-form.tsx @@ -46,7 +46,7 @@ export const DeckForm = observer(() => { })} >

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