Skip to content

Commit 8f1fe98

Browse files
committed
Deck catalog
1 parent 4b5606f commit 8f1fe98

34 files changed

+590
-238
lines changed

functions/catalog-decks.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { createJsonResponse } from "./lib/json-response/create-json-response.ts";
2+
import { getUser } from "./services/get-user.ts";
3+
import { createAuthFailedResponse } from "./lib/json-response/create-auth-failed-response.ts";
4+
import { handleError } from "./lib/handle-error/handle-error.ts";
5+
import { envSchema } from "./env/env-schema.ts";
6+
import { getCatalogDecksDb } from "./db/deck/get-catalog-decks-db.ts";
7+
import { DeckWithCardsDbType } from "./db/deck/decks-with-cards-schema.ts";
8+
9+
export type DeckCatalogResponse = {
10+
decks: DeckWithCardsDbType[];
11+
};
12+
13+
export const onRequest = handleError(async ({ request, env }) => {
14+
const user = await getUser(request, env);
15+
if (!user) return createAuthFailedResponse();
16+
const envSafe = envSchema.parse(env);
17+
18+
const decks = await getCatalogDecksDb(envSafe);
19+
20+
return createJsonResponse<DeckCatalogResponse>({
21+
decks,
22+
});
23+
});

functions/db/databaseTypes.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export interface Database {
5252
deck: {
5353
Row: {
5454
author_id: number | null
55+
available_in: string | null
5556
created_at: string
5657
description: string | null
5758
id: number
@@ -63,6 +64,7 @@ export interface Database {
6364
}
6465
Insert: {
6566
author_id?: number | null
67+
available_in?: string | null
6668
created_at?: string
6769
description?: string | null
6870
id?: number
@@ -74,6 +76,7 @@ export interface Database {
7476
}
7577
Update: {
7678
author_id?: number | null
79+
available_in?: string | null
7780
created_at?: string
7881
description?: string | null
7982
id?: number
@@ -238,6 +241,23 @@ export interface Database {
238241
type: string
239242
}[]
240243
}
244+
get_unadded_public_decks: {
245+
Args: {
246+
user_id: number
247+
}
248+
Returns: {
249+
author_id: number | null
250+
available_in: string | null
251+
created_at: string
252+
description: string | null
253+
id: number
254+
is_public: boolean
255+
name: string
256+
share_id: string
257+
speak_field: string | null
258+
speak_locale: string | null
259+
}[]
260+
}
241261
get_user_decks_deck_id: {
242262
Args: {
243263
usr_id: number

functions/db/deck/decks-with-cards-schema.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export const deckSchema = z.object({
2626
export const deckWithCardsSchema = deckSchema.merge(
2727
z.object({
2828
deck_card: z.array(deckCardSchema),
29+
available_in: z.string().nullable(),
2930
}),
3031
);
3132

functions/db/deck/get-public-decks-db.ts renamed to functions/db/deck/get-catalog-decks-db.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,23 @@ import {
66
DeckWithCardsDbType,
77
} from "./decks-with-cards-schema.ts";
88

9-
export const getPublicDecksDb = async (
9+
export const getCatalogDecksDb = async (
1010
env: EnvType,
1111
): Promise<DeckWithCardsDbType[]> => {
1212
const db = getDatabase(env);
1313

1414
const { data, error } = await db
1515
.from("deck")
16-
.select("*,deck_card!deck_card_deck_id_fkey(*)")
16+
.select("*")
1717
.eq("is_public", true)
1818
.order("id", { ascending: false })
19-
.limit(20);
19+
.limit(100);
2020

2121
if (error) {
2222
throw new DatabaseException(error);
2323
}
2424

25-
return decksWithCardsSchema.parse(data);
25+
return decksWithCardsSchema.parse(
26+
data.map((item) => ({ ...item, deck_card: [] })),
27+
);
2628
};
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { EnvType } from "../../env/env-schema.ts";
2+
import { getDatabase } from "../get-database.ts";
3+
import { DatabaseException } from "../database-exception.ts";
4+
import { decksWithCardsSchema } from "./decks-with-cards-schema.ts";
5+
6+
export const getUnAddedPublicDecksDb = async (env: EnvType, userId: number) => {
7+
const db = getDatabase(env);
8+
9+
const { data, error } = await db.rpc("get_unadded_public_decks", {
10+
user_id: userId,
11+
});
12+
13+
if (error) {
14+
throw new DatabaseException(error);
15+
}
16+
17+
return decksWithCardsSchema.parse(
18+
data.map((item) => ({ ...item, deck_card: [] })),
19+
);
20+
};

functions/deck-with-cards.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { createJsonResponse } from "./lib/json-response/create-json-response.ts";
2+
import { getUser } from "./services/get-user.ts";
3+
import { createAuthFailedResponse } from "./lib/json-response/create-auth-failed-response.ts";
4+
import { handleError } from "./lib/handle-error/handle-error.ts";
5+
import { envSchema } from "./env/env-schema.ts";
6+
import { getDeckWithCardsById } from "./db/deck/get-deck-with-cards-by-id-db.ts";
7+
import { createBadRequestResponse } from "./lib/json-response/create-bad-request-response.ts";
8+
import { z } from "zod";
9+
import { deckWithCardsSchema } from "./db/deck/decks-with-cards-schema.ts";
10+
11+
export type DeckWithCardsResponse = z.infer<typeof deckWithCardsSchema>;
12+
13+
export const onRequest = handleError(async ({ request, env }) => {
14+
const user = await getUser(request, env);
15+
if (!user) return createAuthFailedResponse();
16+
const envSafe = envSchema.parse(env);
17+
18+
const url = new URL(request.url);
19+
const deckId = url.searchParams.get("deck_id");
20+
if (!deckId) {
21+
return createBadRequestResponse();
22+
}
23+
24+
const deck = await getDeckWithCardsById(envSafe, parseInt(deckId));
25+
26+
return createJsonResponse<DeckWithCardsResponse>(deck);
27+
});

functions/my-info.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import { createAuthFailedResponse } from "./lib/json-response/create-auth-failed
44
import { handleError } from "./lib/handle-error/handle-error.ts";
55
import { UserDbType } from "./db/user/upsert-user-db.ts";
66
import { DeckWithCardsDbType } from "./db/deck/decks-with-cards-schema.ts";
7-
import { getPublicDecksDb } from "./db/deck/get-public-decks-db.ts";
87
import { envSchema } from "./env/env-schema.ts";
98
import { getMyDecksDb } from "./db/deck/get-my-decks-db.ts";
109
import {
1110
CardToReviewDbType,
1211
getCardsToReviewDb,
1312
} from "./db/deck/get-cards-to-review-db.ts";
13+
import { getUnAddedPublicDecksDb } from "./db/deck/get-un-added-public-decks-db.ts";
1414

1515
export type MyInfoResponse = {
1616
user: UserDbType;
@@ -25,7 +25,7 @@ export const onRequest = handleError(async ({ request, env }) => {
2525
const envSafe = envSchema.parse(env);
2626

2727
const [publicDecks, myDecks, cardsToReview] = await Promise.all([
28-
await getPublicDecksDb(envSafe),
28+
await getUnAddedPublicDecksDb(envSafe, user.id),
2929
await getMyDecksDb(envSafe, user.id),
3030
await getCardsToReviewDb(envSafe, user.id),
3131
]);

functions/review-cards.ts

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -53,27 +53,29 @@ export const onRequestPost = handleError(async ({ env, request }) => {
5353
const upsertReviewsResult = await db
5454
.from("card_review")
5555
.upsert(
56-
input.data.cards.map((card): Database['public']['Tables']['card_review']['Insert'] => {
57-
const previousReview = existingReviews.find(
58-
(review) => review.card_id === card.id,
59-
);
56+
input.data.cards.map(
57+
(card): Database["public"]["Tables"]["card_review"]["Insert"] => {
58+
const previousReview = existingReviews.find(
59+
(review) => review.card_id === card.id,
60+
);
6061

61-
const reviewResult = reviewCard(
62-
now,
63-
previousReview?.interval,
64-
card.outcome,
65-
previousReview?.ease_factor,
66-
input.data.isInterrupted,
67-
);
62+
const reviewResult = reviewCard(
63+
now,
64+
previousReview?.interval,
65+
card.outcome,
66+
previousReview?.ease_factor,
67+
input.data.isInterrupted,
68+
);
6869

69-
return {
70-
user_id: user.id,
71-
card_id: card.id,
72-
last_review_date: now.toJSDate().toISOString(),
73-
ease_factor: reviewResult.easeFactor,
74-
interval: reviewResult.interval,
75-
};
76-
}),
70+
return {
71+
user_id: user.id,
72+
card_id: card.id,
73+
last_review_date: now.toJSDate().toISOString(),
74+
ease_factor: reviewResult.easeFactor,
75+
interval: reviewResult.interval,
76+
};
77+
},
78+
),
7779
)
7880
.select();
7981

functions/services/review-card.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ export const reviewCard = (
2323
easeFactor: number | undefined = startEaseFactor,
2424
isInterrupted = false,
2525
): Result => {
26-
2726
if (reviewOutcome === "correct") {
2827
interval = interval === 0 ? startInterval : interval * easeFactor;
2928
easeFactor = Math.min(easeFactor + easeFactorIncrement, startEaseFactor);

src/api/api.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import {
2323
RemoveDeckFromMineRequest,
2424
RemoveDeckFromMineResponse,
2525
} from "../../functions/remove-deck-from-mine.ts";
26+
import { DeckCatalogResponse } from "../../functions/catalog-decks.ts";
27+
import { DeckWithCardsResponse } from "../../functions/deck-with-cards.ts";
2628

2729
export const healthRequest = () => {
2830
return request<HealthResponse>("/health");
@@ -79,3 +81,11 @@ export const removeDeckFromMine = (body: RemoveDeckFromMineRequest) => {
7981
body,
8082
);
8183
};
84+
85+
export const apiDeckCatalog = () => {
86+
return request<DeckCatalogResponse>("/catalog-decks");
87+
};
88+
89+
export const apiDeckWithCards = (deckId: number) => {
90+
return request<DeckWithCardsResponse>(`/deck-with-cards?deck_id=${deckId}`);
91+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// ISO 639-1 two-letter language codes
2+
export enum LanguageCode {
3+
Russian = "ru",
4+
English = "en",
5+
Spanish = "es",
6+
}

src/lib/string/camel-case-to-human.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const camelCaseToHuman = (str: string) => {
2+
return str.replace(/([A-Z])/g, " $1");
3+
};

src/lib/telegram/cloud-storage.ts

Lines changed: 0 additions & 25 deletions
This file was deleted.

src/lib/voice-playback/speak.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { camelCaseToHuman } from "../string/camel-case-to-human.ts";
2+
13
export enum SpeakLanguageEnum {
24
USEnglish = "en-US",
35
Italian = "it-IT",
@@ -41,7 +43,7 @@ export const languageKeyToHuman = (str: string): string => {
4143
if (str === "USEnglish") {
4244
return "US English";
4345
}
44-
return str.replace(/([A-Z])/g, " $1").trim();
46+
return camelCaseToHuman(str);
4547
};
4648

4749
export const isSpeechSynthesisSupported =

src/screens/app.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import {
1717
useRestoreFullScreenExpand,
1818
} from "../lib/telegram/prevent-telegram-swipe-down-closing.tsx";
1919
import { RepeatAllScreen } from "./deck-review/repeat-all-screen.tsx";
20+
import { DeckCatalog } from "./deck-catalog/deck-catalog.tsx";
21+
import { DeckCatalogStoreContextProvider } from "../store/deck-catalog-store-context.tsx";
2022

2123
export const App = observer(() => {
2224
useRestoreFullScreenExpand();
@@ -70,6 +72,13 @@ export const App = observer(() => {
7072
</UserSettingsStoreProvider>
7173
</PreventTelegramSwipeDownClosingIos>
7274
)}
75+
{screenStore.screen.type === "deckCatalog" && (
76+
<PreventTelegramSwipeDownClosingIos>
77+
<DeckCatalogStoreContextProvider>
78+
<DeckCatalog />
79+
</DeckCatalogStoreContextProvider>
80+
</PreventTelegramSwipeDownClosingIos>
81+
)}
7382
</div>
7483
);
7584
});

0 commit comments

Comments
 (0)