diff --git a/src/screens/app.tsx b/src/screens/app.tsx index ff10722a..a72f382b 100644 --- a/src/screens/app.tsx +++ b/src/screens/app.tsx @@ -5,7 +5,7 @@ import { ReviewStoreProvider } from "./deck-review/store/review-store-context.ts import { screenStore } from "../store/screen-store.ts"; import { DeckFormScreen } from "./deck-form/deck-form-screen.tsx"; import { DeckFormStoreProvider } from "./deck-form/store/deck-form-store-context.tsx"; -import { QuickAddCardForm } from "./deck-form/quick-add-card-form.tsx"; +import { QuickAddCardFormPage } from "./deck-form/quick-add-card-form-page.tsx"; import { VersionWarning } from "./shared/version-warning.tsx"; import React from "react"; import { UserSettingsStoreProvider } from "./user-settings/store/user-settings-store-context.tsx"; @@ -90,7 +90,7 @@ export const App = observer(() => { )} {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 c3b385a7..1094026b 100644 --- a/src/screens/deck-form/card-form-view.tsx +++ b/src/screens/deck-form/card-form-view.tsx @@ -6,13 +6,16 @@ import { CardFormType } from "./store/deck-form-store.ts"; import { HintTransparent } from "../../ui/hint-transparent.tsx"; import { t } from "../../translations/t.ts"; import { Screen } from "../shared/screen.tsx"; +import { CenteredUnstyledButton } from "../../ui/centered-unstyled-button.tsx"; +import { isFormValid } from "../../lib/mobx-form/form-has-error.ts"; type Props = { cardForm: CardFormType; + onPreviewClick: () => void; }; export const CardFormView = observer((props: Props) => { - const { cardForm } = props; + const { cardForm, onPreviewClick } = props; return ( @@ -30,6 +33,12 @@ export const CardFormView = observer((props: Props) => { {t("card_field_example_hint")} + + {isFormValid(cardForm) && ( + + {t("card_preview")} + + )} ); }); diff --git a/src/screens/deck-form/card-form.tsx b/src/screens/deck-form/card-form.tsx index ae8d1fd3..754ea1da 100644 --- a/src/screens/deck-form/card-form.tsx +++ b/src/screens/deck-form/card-form.tsx @@ -27,5 +27,12 @@ export const CardForm = observer(() => { deckFormStore.onCardBack(); }); - return ; + return ( + { + deckFormStore.isCardPreviewSelected.setTrue(); + }} + /> + ); }); diff --git a/src/screens/deck-form/card-preview.tsx b/src/screens/deck-form/card-preview.tsx new file mode 100644 index 00000000..4aab75a0 --- /dev/null +++ b/src/screens/deck-form/card-preview.tsx @@ -0,0 +1,40 @@ +import { observer } from "mobx-react-lite"; +import { Screen } from "../shared/screen.tsx"; +import { useBackButton } from "../../lib/telegram/use-back-button.tsx"; +import { Card } from "../deck-review/card.tsx"; +import { css } from "@emotion/css"; +import { t } from "../../translations/t.ts"; +import { CardFormType } from "./store/deck-form-store.ts"; + +type Props = { + form: CardFormType; + onBack: () => void; +}; + +export const CardPreview = observer((props: Props) => { + const { form, onBack } = props; + + useBackButton(() => { + onBack(); + }); + + return ( + +
+ {}, + deckSpeakField: "front", + isSpeakingCardsEnabledSettings: false, + }} + animate={{}} + style={{}} + /> +
+
+ ); +}); diff --git a/src/screens/deck-form/deck-form-screen.tsx b/src/screens/deck-form/deck-form-screen.tsx index f56632f6..1f7bf6f5 100644 --- a/src/screens/deck-form/deck-form-screen.tsx +++ b/src/screens/deck-form/deck-form-screen.tsx @@ -4,6 +4,8 @@ import { DeckForm } from "./deck-form.tsx"; import { CardForm } from "./card-form.tsx"; import { useDeckFormStore } from "./store/deck-form-store-context.tsx"; import { CardList } from "./card-list.tsx"; +import { CardPreview } from "./card-preview.tsx"; +import { assert } from "../../lib/typescript/assert.ts"; export const DeckFormScreen = observer(() => { const deckFormStore = useDeckFormStore(); @@ -11,9 +13,20 @@ export const DeckFormScreen = observer(() => { if (deckFormStore.deckFormScreen === "cardList") { return ; } + if (deckFormStore.deckFormScreen === "cardForm") { return ; } + if (deckFormStore.deckFormScreen === "cardPreview") { + assert(deckFormStore.cardForm, "Card should not be empty before preview"); + return ( + + ); + } + return ; }); diff --git a/src/screens/deck-form/deck-form.tsx b/src/screens/deck-form/deck-form.tsx index 2d9a6f47..37a55f51 100644 --- a/src/screens/deck-form/deck-form.tsx +++ b/src/screens/deck-form/deck-form.tsx @@ -26,6 +26,7 @@ import { t } from "../../translations/t.ts"; import { deckListStore } from "../../store/deck-list-store.ts"; import { reset } from "../../ui/reset.ts"; import { Screen } from "../shared/screen.tsx"; +import { CenteredUnstyledButton } from "../../ui/centered-unstyled-button.tsx"; export const DeckForm = observer(() => { const deckFormStore = useDeckFormStore(); @@ -165,17 +166,7 @@ export const DeckForm = observer(() => { {t("add_card")} {deckFormStore.form.id ? ( - + ) : null} ); diff --git a/src/screens/deck-form/quick-add-card-form-page.tsx b/src/screens/deck-form/quick-add-card-form-page.tsx new file mode 100644 index 00000000..d437cfc2 --- /dev/null +++ b/src/screens/deck-form/quick-add-card-form-page.tsx @@ -0,0 +1,20 @@ +import { observer } from "mobx-react-lite"; +import React, { useState } from "react"; +import { QuickAddCardFormStore } from "./store/quick-add-card-form-store.ts"; +import { CardPreview } from "./card-preview.tsx"; +import { QuickAddForm } from "./quick-add-form.tsx"; + +export const QuickAddCardFormPage = observer(() => { + const [quickAddCardStore] = useState(() => new QuickAddCardFormStore()); + + if (quickAddCardStore.isCardPreviewSelected.value) { + return ( + + ); + } + + return ; +}); diff --git a/src/screens/deck-form/quick-add-card-form.tsx b/src/screens/deck-form/quick-add-form.tsx similarity index 65% rename from src/screens/deck-form/quick-add-card-form.tsx rename to src/screens/deck-form/quick-add-form.tsx index 401982c0..c4399255 100644 --- a/src/screens/deck-form/quick-add-card-form.tsx +++ b/src/screens/deck-form/quick-add-form.tsx @@ -1,15 +1,16 @@ import { observer } from "mobx-react-lite"; -import React, { useState } from "react"; -import { CardFormView } from "./card-form-view.tsx"; +import { QuickAddCardFormStore } from "./store/quick-add-card-form-store.ts"; import { useMainButton } from "../../lib/telegram/use-main-button.tsx"; +import { t } from "../../translations/t.ts"; import { useBackButton } from "../../lib/telegram/use-back-button.tsx"; -import { QuickAddCardFormStore } from "./store/quick-add-card-form-store.ts"; import { useTelegramProgress } from "../../lib/telegram/use-telegram-progress.tsx"; -import { t } from "../../translations/t.ts"; +import { CardFormView } from "./card-form-view.tsx"; +import React from "react"; -export const QuickAddCardForm = observer(() => { - const [quickAddCardStore] = useState(() => new QuickAddCardFormStore()); +type Props = { quickAddCardStore: QuickAddCardFormStore }; +export const QuickAddForm = observer((props: Props) => { + const { quickAddCardStore } = props; useMainButton(t("save"), () => { quickAddCardStore.onSave(); }); @@ -18,5 +19,10 @@ export const QuickAddCardForm = observer(() => { }); useTelegramProgress(() => quickAddCardStore.isSending); - return ; + return ( + + ); }); diff --git a/src/screens/deck-form/store/deck-form-store.ts b/src/screens/deck-form/store/deck-form-store.ts index afd97181..315986ea 100644 --- a/src/screens/deck-form/store/deck-form-store.ts +++ b/src/screens/deck-form/store/deck-form-store.ts @@ -19,6 +19,7 @@ import { } from "../../../../functions/db/deck/decks-with-cards-schema.ts"; import { SpeakLanguageEnum } from "../../../lib/voice-playback/speak.ts"; import { t } from "../../../translations/t.ts"; +import { BooleanToggle } from "../../../lib/mobx-form/boolean-toggle.ts"; export type CardFormType = { front: TextField; @@ -79,6 +80,7 @@ export type CardFilterDirection = "desc" | "asc"; export class DeckFormStore { cardFormIndex?: number; cardFormType?: "new" | "edit"; + isCardPreviewSelected = new BooleanToggle(false); form?: DeckFormType; isSending = false; isCardList = false; @@ -94,6 +96,9 @@ export class DeckFormStore { get deckFormScreen() { if (this.cardFormIndex !== undefined) { + if (this.isCardPreviewSelected.value) { + return "cardPreview"; + } return "cardForm"; } if (this.isCardList) { diff --git a/src/screens/deck-form/store/quick-add-card-form-store.ts b/src/screens/deck-form/store/quick-add-card-form-store.ts index c6bdfa90..d03805e7 100644 --- a/src/screens/deck-form/store/quick-add-card-form-store.ts +++ b/src/screens/deck-form/store/quick-add-card-form-store.ts @@ -13,6 +13,7 @@ import { TextField } from "../../../lib/mobx-form/text-field.ts"; import { AddCardRequest } from "../../../../functions/add-card.ts"; import { deckListStore } from "../../../store/deck-list-store.ts"; import { t } from "../../../translations/t.ts"; +import { BooleanToggle } from "../../../lib/mobx-form/boolean-toggle.ts"; export class QuickAddCardFormStore { form: CardFormType = { @@ -21,6 +22,7 @@ export class QuickAddCardFormStore { example: new TextField(""), }; isSending = false; + isCardPreviewSelected = new BooleanToggle(false); constructor() { makeAutoObservable(this, {}, { autoBind: true }); diff --git a/src/screens/deck-review/card-field-view.tsx b/src/screens/deck-review/card-field-view.tsx index fdaa470f..9288fac2 100644 --- a/src/screens/deck-review/card-field-view.tsx +++ b/src/screens/deck-review/card-field-view.tsx @@ -6,8 +6,8 @@ export const CardFieldView = (props: { text: string }) => { return ( + /> ); }; diff --git a/src/screens/deck-review/card-speaker.tsx b/src/screens/deck-review/card-speaker.tsx index cca77a03..32fe24b5 100644 --- a/src/screens/deck-review/card-speaker.tsx +++ b/src/screens/deck-review/card-speaker.tsx @@ -1,13 +1,13 @@ -import { CardUnderReviewStore } from "./store/card-under-review-store.ts"; import { isSpeechSynthesisSupported } from "../../lib/voice-playback/speak.ts"; import { throttle } from "../../lib/throttle/throttle.ts"; import { css, cx } from "@emotion/css"; import { theme } from "../../ui/theme.tsx"; import React from "react"; import { observer } from "mobx-react-lite"; +import { LimitedCardUnderReviewStore } from "./card.tsx"; type Props = { - card: CardUnderReviewStore; + card: LimitedCardUnderReviewStore; type: "front" | "back"; }; diff --git a/src/screens/deck-review/card.tsx b/src/screens/deck-review/card.tsx index 757514d0..d97b3536 100644 --- a/src/screens/deck-review/card.tsx +++ b/src/screens/deck-review/card.tsx @@ -12,8 +12,19 @@ export const cardSize = 310; type FramerMotionProps = Pick; +export type LimitedCardUnderReviewStore = Pick< + CardUnderReviewStore, + | "isOpened" + | "deckSpeakField" + | "isSpeakingCardsEnabledSettings" + | "speak" + | "front" + | "back" + | "example" +>; + type Props = { - card: CardUnderReviewStore; + card: LimitedCardUnderReviewStore; } & FramerMotionProps; export const Card = observer(({ card, style, animate }: Props) => { diff --git a/src/store/screen-store.ts b/src/store/screen-store.ts index a08a2f30..0eea959d 100644 --- a/src/store/screen-store.ts +++ b/src/store/screen-store.ts @@ -1,10 +1,12 @@ import { makeAutoObservable } from "mobx"; +import { CardFormType } from "../screens/deck-form/store/deck-form-store.ts"; type Route = | { type: "main" } | { type: "deckMine"; deckId: number; backScreen?: RouteType } | { type: "deckPublic"; deckId: number; backScreen?: RouteType } | { type: "deckForm"; deckId?: number; folder?: { id: number; name: string } } + | { type: "cardPreview"; form: CardFormType } | { type: "folderForm"; folderId?: number } | { type: "folderPreview"; folderId: number } | { type: "deckOrFolderChoose" } diff --git a/src/translations/t.ts b/src/translations/t.ts index 94be4e73..78fe541c 100644 --- a/src/translations/t.ts +++ b/src/translations/t.ts @@ -44,6 +44,7 @@ const en = { add_card: "Add card", edit_card: "Edit card", deck_preview: "Deck preview", + card_preview: "Card preview", add_card_short: "Add card", add_deck_short: "Deck", card_front_title: "Front side", @@ -130,6 +131,7 @@ type Translation = typeof en; const ru: Translation = { choose_what_to_create: "Выберите что создать", + card_preview: "Предпросмотр карточки", deck: "Колода", add_deck_short: "Колода", deck_description: "Коллекция карточек", @@ -253,6 +255,7 @@ const ru: Translation = { }; const es: Translation = { + card_preview: "Vista previa de la tarjeta", review_folder: "Repasar carpeta", folder_description: "Una colección de mazos", add_deck_short: "Mazo", @@ -381,6 +384,7 @@ const es: Translation = { }; const ptBr: Translation = { + card_preview: "Visualização do cartão", review_folder: "Revisar pasta", add_deck_short: "Baralho", choose_what_to_create: "Escolha o que criar", diff --git a/src/ui/centered-unstyled-button.tsx b/src/ui/centered-unstyled-button.tsx new file mode 100644 index 00000000..a14ce701 --- /dev/null +++ b/src/ui/centered-unstyled-button.tsx @@ -0,0 +1,31 @@ +import { css, cx } from "@emotion/css"; +import { reset } from "./reset.ts"; +import { theme } from "./theme.tsx"; +import React, { ReactNode } from "react"; + +type Props = { + children: ReactNode; + onClick: () => void; +}; + +export const CenteredUnstyledButton = (props: Props) => { + const { children, onClick } = props; + + return ( + + ); +};