Skip to content

Commit

Permalink
Rework quick add card + refine card preview & review UI (#11)
Browse files Browse the repository at this point in the history
* Rework quick add card + refine card preview & review UI
  • Loading branch information
kubk authored Nov 17, 2023
1 parent b12c369 commit 8cbc8d1
Show file tree
Hide file tree
Showing 12 changed files with 130 additions and 81 deletions.
52 changes: 28 additions & 24 deletions src/screens/deck-list/main-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,31 +136,35 @@ export const MainScreen = observer(() => {
</div>
</div>

<div>
<ListHeader text={"News and updates"} />
<Button
icon={"mdi-call-made"}
onClick={() => {
const channelLink = import.meta.env.VITE_CHANNEL_LINK;
assert(channelLink, "Channel link env variable is empty");
{deckListStore.myInfo?.state === "fulfilled" && (
<>
<div>
<ListHeader text={"News and updates"} />
<Button
icon={"mdi-call-made"}
onClick={() => {
const channelLink = import.meta.env.VITE_CHANNEL_LINK;
assert(channelLink, "Channel link env variable is empty");

WebApp.openTelegramLink(channelLink);
}}
>
Telegram channel
</Button>
</div>
<div>
<Button
icon={"mdi-cog"}
disabled={deckListStore.myInfo?.state !== "fulfilled"}
onClick={() => {
screenStore.navigateToUserSettings();
}}
>
Settings
</Button>
</div>
WebApp.openTelegramLink(channelLink);
}}
>
Telegram channel
</Button>
</div>
<div>
<Button
icon={"mdi-cog"}
disabled={deckListStore.myInfo?.state !== "fulfilled"}
onClick={() => {
screenStore.navigateToUserSettings();
}}
>
Settings
</Button>
</div>
</>
)}
</div>
);
});
30 changes: 4 additions & 26 deletions src/screens/deck-list/my-deck.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import React from "react";
import { motion } from "framer-motion";
import { whileTap } from "../../ui/animations.ts";
import { screenStore } from "../../store/screen-store.ts";
import {
deckListStore,
DeckWithCardsWithReviewType,
} from "../../store/deck-list-store.ts";
import { DeckWithCardsWithReviewType } from "../../store/deck-list-store.ts";

type Props = { deck: DeckWithCardsWithReviewType };

Expand Down Expand Up @@ -42,31 +39,12 @@ export const MyDeck = observer((props: Props) => {
{deck.name}
</div>
<div
onClick={(event) => {
if (deckListStore.myId && deck.author_id === deckListStore.myId) {
event.stopPropagation();
screenStore.navigateToQuickCardAdd(deck.id);
}
}}
className={css({
display: "flex",
paddingLeft: 8,
gap: 8,
alignItems: "center",
color: theme.success,
fontWeight: 600,
})}
>
{deckListStore.myId && deck.author_id === deckListStore.myId ? (
<span className={css({ position: "relative", top: -1 })}>+</span>
) : null}

<span
className={css({
color: theme.success,
fontWeight: 600,
})}
>
{deck.cardsToReview.length}
</span>
{deck.cardsToReview.length}
</div>
</motion.div>
);
Expand Down
50 changes: 41 additions & 9 deletions src/screens/deck-review/deck-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,45 @@ export const DeckPreview = observer(() => {
<h4 className={css({ paddingBottom: 4 })}>Description</h4>
<div>{deck.description}</div>
</div>
<div className={css({ display: "flex", gap: 4 })}>
<h4>Cards available to review: </h4>
<span>{deck.cardsToReview.length}</span>
</div>
<div className={css({ display: "flex", gap: 4 })}>
<h4 className={css({ paddingBottom: 4 })}>Total cards: </h4>
<span>{deck.deck_card.length}</span>
<div
className={css({
display: "flex",
gap: 4,
flexDirection: "column",
borderTop: `1px solid ${theme.dividerColor}`,
paddingTop: 8,
})}
>
<div className={css({ display: "flex", gap: 4 })}>
<span>Cards to review: </span>
<h4 className={css({ color: theme.success })}>
{deck.cardsToReview.length}
</h4>
</div>
<div className={css({ display: "flex", gap: 4 })}>
<span>Total cards: </span>
<h4>{deck.deck_card.length}</h4>
</div>
</div>

<div className={css({ display: "flex", gap: 16 })}>
<ShareDeckButton deckId={deck.id} shareId={deck.share_id} />
{deckListStore.myId && deck.author_id === deckListStore.myId ? (
<Button
icon={"mdi-pencil"}
column
icon={"mdi-plus-circle mdi-24px"}
noPseudoClasses
outline
onClick={() => {
screenStore.navigateToQuickCardAdd(deck.id);
}}
>
Add card
</Button>
) : null}
{deckListStore.myId && deck.author_id === deckListStore.myId ? (
<Button
column
icon={"mdi-pencil-circle mdi-24px"}
noPseudoClasses
outline
onClick={() => {
Expand All @@ -84,6 +109,13 @@ export const DeckPreview = observer(() => {
Edit
</Button>
) : null}

<ShareDeckButton
column={
deckListStore.myId ? deck.author_id === deckListStore.myId : false
}
shareId={deck.share_id}
/>
</div>
</div>
{deck.cardsToReview.length === 0 && (
Expand Down
15 changes: 13 additions & 2 deletions src/screens/deck-review/review.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,11 @@ export const Review = observer(() => {
})}
>
<div
style={{
className={css({
width: cardSize,
position: "absolute",
top: 8,
}}
})}
>
{reviewStore.initialCardCount && (
<ProgressBar
Expand All @@ -114,6 +114,17 @@ export const Review = observer(() => {
/>
)}
</div>
<div
className={css({
position: "absolute",
top: 41,
fontSize: 14,
whiteSpace: "nowrap",
color: theme.hintColor,
})}
>
{reviewStore.currentCard?.deckName}
</div>
<div
className={css({
height: 350,
Expand Down
14 changes: 10 additions & 4 deletions src/screens/deck-review/share-deck-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import WebApp from "@twa-dev/sdk";
import { Button } from "../../ui/button.tsx";

type Props = {
deckId: number;
shareId?: string | null;
column?: boolean;
};

export const ShareDeckButton = (props: Props) => {
const { shareId } = props;
const { shareId, column } = props;

const onClick = async () => {
const botUrl = import.meta.env.VITE_BOT_APP_URL;
Expand All @@ -21,8 +21,14 @@ export const ShareDeckButton = (props: Props) => {
};

return (
<Button icon={"mdi-share"} noPseudoClasses outline onClick={onClick}>
Share deck
<Button
column={column}
icon={"mdi-share-circle mdi-24px"}
noPseudoClasses
outline
onClick={onClick}
>
Share
</Button>
);
};
1 change: 1 addition & 0 deletions src/store/card-form-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export class CardFormStore {
public front: string,
public back: string,
public example: string | null = null,
public deckName?: string,
) {
makeAutoObservable(this, {}, { autoBind: true });
}
Expand Down
12 changes: 6 additions & 6 deletions src/store/deck-list-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,6 @@ export class DeckListStore {
}
}

updateSettings(body: Pick<UserDbType, 'is_remind_enabled' | 'last_reminded_date'>) {
assert(this.myInfo?.state === 'fulfilled');
Object.assign(this.myInfo.value.user, body);
}

async loadSharedDeck(shareId?: string) {
if (!shareId || this.isSharedDeckLoaded) {
return;
Expand Down Expand Up @@ -112,7 +107,7 @@ export class DeckListStore {
deckListStore.addDeckToMine(deckListStore.selectedDeck.id);
}

reviewStore.startDeckReview(deckListStore.selectedDeck.cardsToReview);
reviewStore.startDeckReview(deckListStore.selectedDeck.cardsToReview, deckListStore.selectedDeck.name);
}

addDeckToMine(deckId: number) {
Expand Down Expand Up @@ -187,6 +182,11 @@ export class DeckListStore {
this.myDecks.every((deck) => deck.cardsToReview.length === 0)
);
}

updateSettings(body: Pick<UserDbType, 'is_remind_enabled' | 'last_reminded_date'>) {
assert(this.myInfo?.state === 'fulfilled');
Object.assign(this.myInfo.value.user, body);
}
}

const getCardsToReview = (
Expand Down
13 changes: 10 additions & 3 deletions src/store/review-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,13 @@ describe("card form store", () => {

it("basic", () => {
const reviewStore = new ReviewStore();
reviewStore.startDeckReview(deckCardsMock);
reviewStore.startDeckReview(deckCardsMock, '');
expect(reviewStore.isFinished).toBeFalsy();
expect(reviewStore.cardsToReview).toMatchInlineSnapshot(`
[
CardFormStore {
"back": "Время",
"deckName": "",
"example": null,
"front": "time",
"id": 3,
Expand All @@ -66,6 +67,7 @@ describe("card form store", () => {
},
CardFormStore {
"back": "Год",
"deckName": "",
"example": null,
"front": "year",
"id": 4,
Expand All @@ -74,6 +76,7 @@ describe("card form store", () => {
},
CardFormStore {
"back": "Дорога",
"deckName": "",
"example": null,
"front": "way",
"id": 5,
Expand All @@ -92,6 +95,7 @@ describe("card form store", () => {
[
CardFormStore {
"back": "Год",
"deckName": "",
"example": null,
"front": "year",
"id": 4,
Expand All @@ -100,6 +104,7 @@ describe("card form store", () => {
},
CardFormStore {
"back": "Дорога",
"deckName": "",
"example": null,
"front": "way",
"id": 5,
Expand All @@ -118,6 +123,7 @@ describe("card form store", () => {
[
CardFormStore {
"back": "Дорога",
"deckName": "",
"example": null,
"front": "way",
"id": 5,
Expand All @@ -126,6 +132,7 @@ describe("card form store", () => {
},
CardFormStore {
"back": "Год",
"deckName": "",
"example": null,
"front": "year",
"id": 4,
Expand All @@ -150,7 +157,7 @@ describe("card form store", () => {

it("current next", () => {
const reviewStore = new ReviewStore();
reviewStore.startDeckReview(deckCardsMock);
reviewStore.startDeckReview(deckCardsMock, '');
expect(reviewStore.isFinished).toBeFalsy();

expect(reviewStore.currentCard?.id).toEqual(3);
Expand Down Expand Up @@ -189,7 +196,7 @@ describe("card form store", () => {

it("hit wrong many times", () => {
const reviewStore = new ReviewStore();
reviewStore.startDeckReview(deckCardsMock);
reviewStore.startDeckReview(deckCardsMock, '');
expect(reviewStore.isFinished).toBeFalsy();

reviewStore.open();
Expand Down
14 changes: 10 additions & 4 deletions src/store/review-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,19 @@ export class ReviewStore {
makeAutoObservable(this, {}, { autoBind: true });
}

startDeckReview(decks: DeckCardDbType[]) {
if (!decks.length) {
startDeckReview(deckCards: DeckCardDbType[], deckName: string) {
if (!deckCards.length) {
return;
}
decks.forEach((card) => {
deckCards.forEach((card) => {
this.cardsToReview.push(
new CardFormStore(card.id, card.front, card.back, card.example),
new CardFormStore(
card.id,
card.front,
card.back,
card.example,
deckName,
),
);
});
this.initialCardCount = this.cardsToReview.length;
Expand Down
Loading

0 comments on commit 8cbc8d1

Please sign in to comment.