Skip to content

Commit

Permalink
Hide card forever (#15)
Browse files Browse the repository at this point in the history
* Hide card forever
  • Loading branch information
kubk authored Feb 7, 2024
1 parent 6be3f74 commit 0155209
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 17 deletions.
1 change: 1 addition & 0 deletions src/screens/deck-form/card-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const CardPreview = observer((props: Props) => {
}}
card={cardPreviewStore}
onReviewCardWithAnswers={() => {}}
onHideCardForever={() => {}}
/>
</div>
);
Expand Down
13 changes: 10 additions & 3 deletions src/screens/deck-review/card-review-with-controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,18 @@ type Props = {
onCorrect: () => void;
onShowAnswer: () => void;
onReviewCardWithAnswers: () => void;
onHideCardForever: () => void;
};

export const CardReviewWithControls = observer((props: Props) => {
const { card, onWrong, onCorrect, onShowAnswer, onReviewCardWithAnswers } =
props;
const {
card,
onWrong,
onCorrect,
onShowAnswer,
onReviewCardWithAnswers,
onHideCardForever,
} = props;

return (
<>
Expand All @@ -31,7 +38,7 @@ export const CardReviewWithControls = observer((props: Props) => {
width: "100%",
})}
>
{card && <Card card={card} />}
{card && <Card card={card} onHideCardForever={onHideCardForever} />}
</div>
{card && card.answerType === "remember" && (
<div
Expand Down
5 changes: 2 additions & 3 deletions src/screens/deck-review/review.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,7 @@ export const Review = observer(() => {
>
{reviewStore.initialCardCount && (
<ProgressBar
value={
reviewStore.initialCardCount - reviewStore.cardsToReview.length
}
value={reviewStore.reviewedCardsCount}
max={reviewStore.initialCardCount}
/>
)}
Expand All @@ -89,6 +87,7 @@ export const Review = observer(() => {
onCorrect={onCorrect}
onShowAnswer={reviewStore.open}
onReviewCardWithAnswers={reviewStore.onReviewCardWithAnswers}
onHideCardForever={reviewStore.onHideCardForever}
/>
</div>
);
Expand Down
1 change: 1 addition & 0 deletions src/screens/deck-review/store/card-under-review-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { CardReviewType } from "../../../../functions/db/deck/get-cards-to-revie
export enum CardState {
Remember = "remember",
Forget = "forget",
Never = "never",
}

export class CardUnderReviewStore {
Expand Down
51 changes: 51 additions & 0 deletions src/screens/deck-review/store/review-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ vi.mock("mobx-persist-store", () => {
};
});

vi.mock("../../../lib/telegram/show-confirm.ts", () => {
return {
showAlert: () => {},
};
});

vi.mock("../../../translations/t.ts", () => {
return {
t: (val: string) => val,
};
});

const createDeckWithCards = (cards: DeckCardDbTypeWithType[]) => {
const deckMock: DeckWithCardsWithReviewType = {
id: 1,
Expand Down Expand Up @@ -413,6 +425,45 @@ describe("card form store", () => {
expect(reviewStore.currentCard?.id).toEqual(4);
});

it("use 'never show' option", () => {
const reviewStore = new ReviewStore();
reviewStore.startDeckReview(createDeckWithCards(repeatCardsMock));
expect(reviewStore.isFinished).toBeFalsy();
expect(reviewStore.currentCard?.id).toEqual(3);

reviewStore.open();
reviewStore.changeState(CardState.Forget);
expect(reviewStore.currentCard?.id).toEqual(4);

reviewStore.open();
reviewStore.changeState(CardState.Forget);
expect(reviewStore.currentCard?.id).toEqual(5);

reviewStore.open();
reviewStore.changeState(CardState.Forget);
expect(reviewStore.currentCard?.id).toEqual(6);

reviewStore.open();
reviewStore.changeState(CardState.Never);
expect(reviewStore.currentCard?.id).toEqual(3);

reviewStore.open();
reviewStore.changeState(CardState.Remember);
expect(reviewStore.currentCard?.id).toEqual(4);

reviewStore.open();
reviewStore.changeState(CardState.Remember);
expect(reviewStore.currentCard?.id).toEqual(5);

reviewStore.open();
reviewStore.changeState(CardState.Remember);
expect(reviewStore.isFinished).toBeTruthy();

expect(reviewStore.result.forgotIds).toHaveLength(3);
expect(reviewStore.result.rememberIds).toHaveLength(0);
expect(reviewStore.result.neverIds).toHaveLength(1);
});

it("hit wrong many times - prioritize forgotten new cards", () => {
const reviewStore = new ReviewStore();
reviewStore.startDeckReview(createDeckWithCards(newCardsMock));
Expand Down
38 changes: 36 additions & 2 deletions src/screens/deck-review/store/review-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,20 @@ import {
hapticImpact,
hapticNotification,
} from "../../../lib/telegram/haptics.ts";
import { showConfirm } from "../../../lib/telegram/show-confirm.ts";
import { t } from "../../../translations/t.ts";

type ReviewResult = {
forgotIds: number[];
rememberIds: number[];
neverIds: number[];
};

export class ReviewStore {
cardsToReview: CardUnderReviewStore[] = [];
currentCardId?: number;

result: ReviewResult = { forgotIds: [], rememberIds: [] };
result: ReviewResult = { forgotIds: [], rememberIds: [], neverIds: [] };
initialCardCount?: number;

isReviewSending = false;
Expand All @@ -29,6 +32,11 @@ export class ReviewStore {
makeAutoObservable(this, {}, { autoBind: true });
}

get reviewedCardsCount() {
assert(this.initialCardCount, "initialCardCount is empty");
return this.initialCardCount - this.cardsToReview.length;
}

startDeckReview(deck: DeckWithCardsWithReviewType) {
if (!deck.cardsToReview.length) {
return;
Expand Down Expand Up @@ -130,6 +138,14 @@ export class ReviewStore {
this.changeState(newState);
}

async onHideCardForever() {
const isConfirmed = await showConfirm(t("hide_card_forever_confirm_title"));
if (!isConfirmed) {
return;
}
this.changeState(CardState.Never);
}

changeState(cardState: CardState) {
const currentCard = this.currentCard;
assert(
Expand Down Expand Up @@ -171,6 +187,16 @@ export class ReviewStore {
this.result.rememberIds.push(currentCard.id);
}

if (currentCard.state === CardState.Never) {
this.result.forgotIds = this.result.forgotIds.filter(
(id) => id !== currentCard.id,
);
this.result.rememberIds = this.result.rememberIds.filter(
(id) => id !== currentCard.id,
);
this.result.neverIds.push(currentCard.id);
}

if (this.cardsToReview.length !== 0) {
// Go to next card
this.currentCardId = this.cardsToReview[0].id;
Expand All @@ -182,7 +208,11 @@ export class ReviewStore {
}

get hasResult() {
return this.result.forgotIds.length || this.result.rememberIds.length;
return (
this.result.forgotIds.length ||
this.result.rememberIds.length ||
this.result.neverIds.length
);
}

submitUnfinished() {
Expand All @@ -209,6 +239,10 @@ export class ReviewStore {
id: rememberId,
outcome: "correct" as const,
})),
...this.result.neverIds.map((neverId) => ({
id: neverId,
outcome: "never" as const,
})),
];
}

Expand Down
26 changes: 18 additions & 8 deletions src/screens/shared/card/card.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { css, cx } from "@emotion/css";
import { css } from "@emotion/css";
import React from "react";
import { theme } from "../../../ui/theme.tsx";
import { observer } from "mobx-react-lite";
Expand All @@ -8,6 +8,8 @@ import { CardSpeaker } from "./card-speaker.tsx";
import { CardFieldView } from "./card-field-view.tsx";
import { assert } from "../../../lib/typescript/assert.ts";
import { useIsOverflowing } from "../../../lib/react/use-is-overflowing.ts";
import { Dropdown } from "../../../ui/dropdown.tsx";
import { t } from "../../../translations/t.ts";

export const cardSize = 310;

Expand All @@ -30,10 +32,11 @@ export type LimitedCardUnderReviewStore = Pick<

type Props = {
card: LimitedCardUnderReviewStore;
onHideCardForever?: () => void;
};

export const Card = observer((props: Props) => {
const { card } = props;
const { card, onHideCardForever } = props;
const { ref: cardRef } = useIsOverflowing(
card.isOpened,
(is) => card?.isOverflowing.setValue(is),
Expand Down Expand Up @@ -65,20 +68,27 @@ export const Card = observer((props: Props) => {
})
}
>
{false && (
{
<div
className={css({
position: "absolute",
top: 0,
right: 12,
transform: "scale(0.8)",
right: 30,
cursor: "pointer",
})}
onClick={() => {}}
>
<i className={cx("mdi mdi-dots-horizontal mdi-24px")} />
<Dropdown
items={[
{
text: t("hide_card_forever"),
onClick: () => {
onHideCardForever?.();
},
},
]}
/>
</div>
)}
}
<span
className={css({
textAlign: "center",
Expand Down
12 changes: 12 additions & 0 deletions src/translations/t.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { Translator } from "../lib/translator/translator.ts";
import { getUserLanguage } from "./get-user-language.ts";

const en = {
hide_card_forever: "Hide forever",
hide_card_forever_confirm_title:
"Are you sure you want to hide this card forever? You will never see it again",
wysiwyg_big_header: "Big header",
next: "Next",
wysiwyg_small_header: "Small header",
Expand Down Expand Up @@ -166,6 +169,9 @@ const en = {
type Translation = typeof en;

const ru: Translation = {
hide_card_forever_confirm_title:
"Вы уверены, что хотите скрыть эту карточку навсегда? Вы больше не увидите её",
hide_card_forever: "Скрыть навсегда",
wysiwyg_italic: "Курсив",
wysiwyg_red: "Красный",
next: "Далее",
Expand Down Expand Up @@ -325,6 +331,9 @@ const ru: Translation = {
};

const es: Translation = {
hide_card_forever: "Ocultar para siempre",
hide_card_forever_confirm_title:
"¿Estás seguro de que quieres ocultar esta tarjeta para siempre? No la volverás a ver",
next: "Siguiente",
wysiwyg_red: "Rojo",
wysiwyg_bold: "Negrita",
Expand Down Expand Up @@ -490,6 +499,9 @@ const es: Translation = {
};

const ptBr: Translation = {
hide_card_forever: "Ocultar para sempre",
hide_card_forever_confirm_title:
"Tem certeza de que deseja ocultar este cartão para sempre? Você nunca mais o verá",
next: "Próximo",
wysiwyg_red: "Vermelho",
wysiwyg_bold: "Negrito",
Expand Down
Loading

0 comments on commit 0155209

Please sign in to comment.