Skip to content

Commit

Permalink
Review all feature (#16)
Browse files Browse the repository at this point in the history
* Review all feature
  • Loading branch information
kubk authored Nov 27, 2023
1 parent 818230e commit 444c6c9
Show file tree
Hide file tree
Showing 10 changed files with 180 additions and 66 deletions.
2 changes: 1 addition & 1 deletion src/lib/telegram/prevent-telegram-swipe-down-closing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export const PreventTelegramSwipeDownClosingIos = (props: {
// A hacky way to force expand app back whenever user pull app down
export const useRestoreFullScreenExpand = () => {
useEffect(() => {
if (WebApp.platform !== "android" && WebApp.platform !== 'ios') {
if (WebApp.platform !== "android" && WebApp.platform !== "ios") {
return;
}
const onViewPortChanged = () => {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/throttle/throttle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

export function throttle(func, limit) {
let inThrottle;
return function() {
return function () {
// eslint-disable-next-line
const args = arguments;
// eslint-disable-next-line
Expand Down
16 changes: 14 additions & 2 deletions src/screens/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,18 @@ import { deckListStore } from "../store/deck-list-store.ts";
import { FullScreenLoader } from "./deck-list/full-screen-loader.tsx";
import {
PreventTelegramSwipeDownClosingIos,
useRestoreFullScreenExpand
useRestoreFullScreenExpand,
} from "../lib/telegram/prevent-telegram-swipe-down-closing.tsx";
import { RepeatAllScreen } from "./deck-review/repeat-all-screen.tsx";

export const App = observer(() => {
useRestoreFullScreenExpand();

if (deckListStore.isSharedDeckLoading || deckListStore.isDeckRemoving) {
if (
deckListStore.isSharedDeckLoading ||
deckListStore.isDeckRemoving ||
deckListStore.isReviewAllLoading
) {
return <FullScreenLoader />;
}

Expand All @@ -39,6 +44,13 @@ export const App = observer(() => {
</ReviewStoreProvider>
</PreventTelegramSwipeDownClosingIos>
)}
{screenStore.screen.type === "reviewAll" && (
<PreventTelegramSwipeDownClosingIos>
<ReviewStoreProvider>
<RepeatAllScreen />
</ReviewStoreProvider>
</PreventTelegramSwipeDownClosingIos>
)}
{screenStore.screen.type === "deckForm" && (
<PreventTelegramSwipeDownClosingIos>
<DeckFormStoreProvider>
Expand Down
13 changes: 11 additions & 2 deletions src/screens/deck-review/deck-finished.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ const encouragingMessages = [
"Just think of the compounded knowledge you're getting with every review. Your future self thanks you!",
];

export const DeckFinished = observer(() => {
type Props = {
type: "deck" | "repeat_all";
};

export const DeckFinished = observer((props: Props) => {
const { type } = props;
const reviewStore = useReviewStore();

useMount(() => {
Expand All @@ -46,7 +51,11 @@ export const DeckFinished = observer(() => {
alignItems: "center",
})}
>
<p>You have finished this deck for now 🎉</p>
<p>
{type === "deck"
? `You have finished this deck for now 🎉`
: `You have repeated all the cards for today 🎉`}
</p>

<p>{random(encouragingMessages)} 😊</p>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/screens/deck-review/deck-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const DeckPreview = observer(() => {
useMainButton(
"Review deck",
() => {
deckListStore.startReview(reviewStore);
deckListStore.startDeckReview(reviewStore);
},
() => deckListStore.canReview,
);
Expand Down
2 changes: 1 addition & 1 deletion src/screens/deck-review/deck-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const DeckScreen = observer(() => {
const reviewStore = useReviewStore();

if (reviewStore.isFinished) {
return <DeckFinished />;
return <DeckFinished type={'deck'} />;
} else if (reviewStore.currentCardId) {
return <Review />;
}
Expand Down
23 changes: 23 additions & 0 deletions src/screens/deck-review/repeat-all-screen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { observer } from "mobx-react-lite";
import { useReviewStore } from "../../store/review-store-context.tsx";
import { useMount } from "../../lib/react/use-mount.ts";
import { deckListStore } from "../../store/deck-list-store.ts";
import { DeckFinished } from "./deck-finished.tsx";
import { Review } from "./review.tsx";
import React from "react";

export const RepeatAllScreen = observer(() => {
const reviewStore = useReviewStore();

useMount(() => {
reviewStore.startAllRepeatReview(deckListStore.myDecks);
});

if (reviewStore.isFinished) {
return <DeckFinished type={"repeat_all"} />;
} else if (reviewStore.currentCardId) {
return <Review />;
}

return null;
});
152 changes: 95 additions & 57 deletions src/store/deck-list-store.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { action, makeAutoObservable, runInAction, when } from "mobx";
import { action, makeAutoObservable, when } from "mobx";
import { fromPromise, IPromiseBasedObservable } from "mobx-utils";
import {
addDeckToMineRequest,
Expand All @@ -18,14 +18,23 @@ import { ReviewStore } from "./review-store.ts";
import { reportHandledError } from "../lib/rollbar/rollbar.tsx";
import { UserDbType } from "../../functions/db/user/upsert-user-db.ts";

export enum StartParamType {
RepeatAll = "repeat_all",
}

export type DeckWithCardsWithReviewType = DeckWithCardsDbType & {
cardsToReview: Array<DeckCardDbType & { type: "new" | "repeat" }>;
};

export class DeckListStore {
myInfo?: IPromiseBasedObservable<MyInfoResponse>;

isSharedDeckLoading = false;
isSharedDeckLoaded = false;

isReviewAllLoading = false;
isReviewAllLoaded = false;

skeletonLoaderData = { publicCount: 3, myDecksCount: 3 };

isDeckRemoving = false;
Expand All @@ -34,9 +43,9 @@ export class DeckListStore {
makeAutoObservable(this, {}, { autoBind: true });
}

loadFirstTime(shareId?: string) {
loadFirstTime(startParam?: string) {
this.load();
this.loadSharedDeck(shareId);
this.handleStartParam(startParam);
}

load() {
Expand All @@ -52,51 +61,76 @@ export class DeckListStore {
}
}

async loadSharedDeck(shareId?: string) {
if (!shareId || this.isSharedDeckLoaded) {
async handleStartParam(startParam?: string) {
if (!startParam) {
return;
}

this.isSharedDeckLoading = true;
await when(() => this.myInfo?.state === "fulfilled");

getSharedDeckRequest(shareId)
.then(
action((sharedDeck) => {
assert(this.myInfo?.state === "fulfilled");
if (
this.myInfo.value.myDecks.find(
(myDeck) => myDeck.id === sharedDeck.deck.id,
)
) {
screenStore.go({ type: "deckMine", deckId: sharedDeck.deck.id });
return;
}

if (
this.publicDecks.find(
(publicDeck) => publicDeck.id === sharedDeck.deck.id,
)
) {
if (startParam === StartParamType.RepeatAll) {
if (this.isReviewAllLoaded) {
return;
}

this.isReviewAllLoading = true;
when(() => this.myInfo?.state === "fulfilled")
.then(() => {
screenStore.go({ type: "reviewAll" });
})
.finally(
action(() => {
this.isReviewAllLoading = false;
this.isReviewAllLoaded = true;
}),
);
} else {
if (this.isSharedDeckLoaded) {
return;
}

this.isSharedDeckLoading = true;
await when(() => this.myInfo?.state === "fulfilled");

getSharedDeckRequest(startParam)
.then(
action((sharedDeck) => {
assert(this.myInfo?.state === "fulfilled");
if (
this.myInfo.value.myDecks.find(
(myDeck) => myDeck.id === sharedDeck.deck.id,
)
) {
screenStore.go({ type: "deckMine", deckId: sharedDeck.deck.id });
return;
}

if (
this.publicDecks.find(
(publicDeck) => publicDeck.id === sharedDeck.deck.id,
)
) {
screenStore.go({
type: "deckPublic",
deckId: sharedDeck.deck.id,
});
return;
}

this.myInfo.value.publicDecks.push(sharedDeck.deck);
screenStore.go({ type: "deckPublic", deckId: sharedDeck.deck.id });
return;
}

this.myInfo.value.publicDecks.push(sharedDeck.deck);
screenStore.go({ type: "deckPublic", deckId: sharedDeck.deck.id });
}),
)
.catch((e) => {
reportHandledError("Error while retrieving shared deck", e, {
shareId,
});
})
.finally(
action(() => {
this.isSharedDeckLoading = false;
this.isSharedDeckLoaded = true;
}),
);
}),
)
.catch((e) => {
reportHandledError("Error while retrieving shared deck", e, {
shareId: startParam,
});
})
.finally(
action(() => {
this.isSharedDeckLoading = false;
this.isSharedDeckLoaded = true;
}),
);
}
}

get canReview() {
Expand All @@ -108,7 +142,7 @@ export class DeckListStore {
);
}

startReview(reviewStore: ReviewStore) {
startDeckReview(reviewStore: ReviewStore) {
if (!this.canReview) {
return;
}
Expand Down Expand Up @@ -198,25 +232,29 @@ export class DeckListStore {
);
}

async removeDeck() {
removeDeck() {
const deck = this.selectedDeck;
if (!deck) {
return;
}

this.isDeckRemoving = true;

try {
await removeDeckFromMine({ deckId: deck.id });
screenStore.go({ type: "main" });
this.myInfo = fromPromise(myInfoRequest());
} catch (e) {
reportHandledError(`Unable to remove deck ${deck.id}`, e);
} finally {
runInAction(() => {
this.isDeckRemoving = false;
});
}
removeDeckFromMine({ deckId: deck.id })
.then(
action(() => {
screenStore.go({ type: "main" });
this.myInfo = fromPromise(myInfoRequest());
}),
)
.catch((e) => {
reportHandledError(`Unable to remove deck ${deck.id}`, e);
})
.finally(
action(() => {
this.isDeckRemoving = false;
}),
);
}

optimisticUpdateSettings(
Expand Down
33 changes: 32 additions & 1 deletion src/store/review-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import { assert } from "../lib/typescript/assert.ts";
import { reviewCardsRequest } from "../api/api.ts";
import { ReviewOutcome } from "../../functions/services/review-card.ts";
import { screenStore } from "./screen-store.ts";
import { deckListStore } from "./deck-list-store.ts";
import {
deckListStore,
DeckWithCardsWithReviewType,
} from "./deck-list-store.ts";

type ReviewResult = {
forgotIds: number[];
Expand Down Expand Up @@ -49,6 +52,34 @@ export class ReviewStore {
}
}

startAllRepeatReview(myDecks: DeckWithCardsWithReviewType[]) {
if (!myDecks.length) {
return;
}

myDecks.forEach((deck) => {
deck.cardsToReview
.filter((card) => card.type === "repeat")
.forEach((card) => {
this.cardsToReview.push(
new CardFormStore(
card.id,
card.front,
card.back,
card.example,
deck.name,
),
);
});
});

this.initialCardCount = this.cardsToReview.length;
this.currentCardId = this.cardsToReview[0].id;
if (this.cardsToReview.length > 1) {
this.nextCardId = this.cardsToReview[1].id;
}
}

get currentCard() {
if (!this.currentCardId) {
return null;
Expand Down
1 change: 1 addition & 0 deletions src/store/screen-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type Route =
| { type: "deckMine"; deckId: number }
| { type: "deckPublic"; deckId: number }
| { type: "deckForm"; deckId?: number }
| { type: "reviewAll" }
| { type: "cardQuickAddForm"; deckId: number }
| { type: "userSettings" };

Expand Down

0 comments on commit 444c6c9

Please sign in to comment.