Skip to content

Commit

Permalink
Deck folders [wip]
Browse files Browse the repository at this point in the history
  • Loading branch information
kubk committed Dec 30, 2023
1 parent 52c95fe commit 22ea7d8
Show file tree
Hide file tree
Showing 22 changed files with 628 additions and 49 deletions.
110 changes: 110 additions & 0 deletions functions/db/databaseTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,65 @@ export interface Database {
}
Relationships: []
}
deck_folder: {
Row: {
created_at: string
deck_id: number
folder_id: number
}
Insert: {
created_at?: string
deck_id: number
folder_id: number
}
Update: {
created_at?: string
deck_id?: number
folder_id?: number
}
Relationships: [
{
foreignKeyName: "deck_folder_deck_id_fkey"
columns: ["deck_id"]
referencedRelation: "deck"
referencedColumns: ["id"]
},
{
foreignKeyName: "deck_folder_folder_id_fkey"
columns: ["folder_id"]
referencedRelation: "folder"
referencedColumns: ["id"]
}
]
}
folder: {
Row: {
author_id: number
created_at: string
id: number
title: string
}
Insert: {
author_id: number
created_at?: string
id?: number
title: string
}
Update: {
author_id?: number
created_at?: string
id?: number
title?: string
}
Relationships: [
{
foreignKeyName: "folder_author_id_fkey"
columns: ["author_id"]
referencedRelation: "user"
referencedColumns: ["id"]
}
]
}
notification: {
Row: {
created_at: string
Expand Down Expand Up @@ -259,6 +318,37 @@ export interface Database {
}
]
}
user_folder: {
Row: {
created_at: string
folder_id: number
user_id: number
}
Insert: {
created_at?: string
folder_id: number
user_id: number
}
Update: {
created_at?: string
folder_id?: number
user_id?: number
}
Relationships: [
{
foreignKeyName: "user_folder_folder_id_fkey"
columns: ["folder_id"]
referencedRelation: "folder"
referencedColumns: ["id"]
},
{
foreignKeyName: "user_folder_user_id_fkey"
columns: ["user_id"]
referencedRelation: "user"
referencedColumns: ["id"]
}
]
}
}
Views: {
[_ in never]: never
Expand Down Expand Up @@ -292,6 +382,16 @@ export interface Database {
type: string
}[]
}
get_folder_with_decks: {
Args: {
usr_id: number
}
Returns: {
folder_id: number
folder_title: string
deck_id: number
}[]
}
get_unadded_public_decks: {
Args: {
user_id: number
Expand Down Expand Up @@ -347,6 +447,16 @@ export interface Database {
is_admin: boolean
}[]
}
get_users_with_review_to_notify2: {
Args: Record<PropertyKey, never>
Returns: {
user_id: number
review_count: number
last_reminded_date: string
is_admin: boolean
language_code: string
}[]
}
}
Enums: {
[_ in never]: never
Expand Down
27 changes: 27 additions & 0 deletions functions/db/deck/get-all-my-decks-flat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { EnvSafe } from "../../env/env-schema.ts";
import { getDatabase } from "../get-database.ts";
import { DatabaseException } from "../database-exception.ts";
import { z } from "zod";

export const schema = z.object({
id: z.string(),
name: z.string(),
});

export type MyDeckFlatDb = z.infer<typeof schema>;

export const getAllMyDecksFlatDb = async (env: EnvSafe, userId: number) => {
const db = getDatabase(env);

const { data, error } = await db
.from("deck")
.select("id")
.eq("author_id", userId)
.limit(500);

if (error) {
throw new DatabaseException(error);
}

return z.array(schema).parse(data);
};
26 changes: 26 additions & 0 deletions functions/db/folder/get-folders-with-decks-db.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { EnvSafe } from "../../env/env-schema.ts";
import { getDatabase } from "../get-database.ts";
import { DatabaseException } from "../database-exception.ts";
import { z } from "zod";

const userFoldersSchema = z.object({
folder_id: z.number(),
folder_title: z.string(),
deck_id: z.number().nullable(),
});

export type UserFoldersDbType = z.infer<typeof userFoldersSchema>;

export const getFoldersWithDecksDb = async (env: EnvSafe, userId: number) => {
const db = getDatabase(env);

const result = await db.rpc("get_folder_with_decks", {
usr_id: userId,
});

if (result.error) {
throw new DatabaseException(result.error);
}

return z.array(userFoldersSchema).parse(result.data);
};
9 changes: 8 additions & 1 deletion functions/my-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,36 @@ import {
getCardsToReviewDb,
} from "./db/deck/get-cards-to-review-db.ts";
import { getUnAddedPublicDecksDb } from "./db/deck/get-un-added-public-decks-db.ts";
import {
getFoldersWithDecksDb,
UserFoldersDbType,
} from "./db/folder/get-folders-with-decks-db.tsx";

export type MyInfoResponse = {
user: UserDbType;
myDecks: DeckWithCardsDbType[];
publicDecks: DeckWithCardsDbType[];
cardsToReview: CardToReviewDbType[];
folders: UserFoldersDbType[];
};

export const onRequest = handleError(async ({ request, env }) => {
const user = await getUser(request, env);
if (!user) return createAuthFailedResponse();
const envSafe = envSchema.parse(env);

const [publicDecks, myDecks, cardsToReview] = await Promise.all([
const [publicDecks, myDecks, cardsToReview, folders] = await Promise.all([
await getUnAddedPublicDecksDb(envSafe, user.id),
await getMyDecksWithCardsDb(envSafe, user.id),
await getCardsToReviewDb(envSafe, user.id),
await getFoldersWithDecksDb(envSafe, user.id),
]);

return createJsonResponse<MyInfoResponse>({
user,
publicDecks,
myDecks,
cardsToReview,
folders,
});
});
43 changes: 43 additions & 0 deletions functions/upsert-folder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { handleError } from "./lib/handle-error/handle-error.ts";
import { getUser } from "./services/get-user.ts";
import { createAuthFailedResponse } from "./lib/json-response/create-auth-failed-response.ts";
import { z } from "zod";
import { createBadRequestResponse } from "./lib/json-response/create-bad-request-response.ts";
import { getDatabase } from "./db/get-database.ts";
import { envSchema } from "./env/env-schema.ts";
import { DatabaseException } from "./db/database-exception.ts";
import { createJsonResponse } from "./lib/json-response/create-json-response.ts";

const requestSchema = z.object({
id: z.number().optional(),
title: z.string(),
});

export type AddFolderRequest = z.infer<typeof requestSchema>;
export type AddFolderResponse = null;

export const onRequestPost = handleError(async ({ request, env }) => {
const user = await getUser(request, env);
if (!user) return createAuthFailedResponse();

const input = requestSchema.safeParse(await request.json());
if (!input.success) {
return createBadRequestResponse();
}

const envSafe = envSchema.parse(env);
const db = getDatabase(envSafe);
const { data } = input;

const upsertFolderResult = await db.from("folder").upsert({
id: data.id,
title: data.title,
author_id: user.id,
});

if (upsertFolderResult.error) {
throw new DatabaseException(upsertFolderResult.error);
}

return createJsonResponse<AddFolderResponse>(null);
});
12 changes: 12 additions & 0 deletions src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ import { DeckCatalogResponse } from "../../functions/catalog-decks.ts";
import { DeckWithCardsResponse } from "../../functions/deck-with-cards.ts";
import { CopyDeckResponse } from "../../functions/duplicate-deck.ts";
import { DeckCategoryResponse } from "../../functions/deck-categories.ts";
import {
AddFolderRequest,
AddFolderResponse,
} from "../../functions/upsert-folder.ts";

export const healthRequest = () => {
return request<HealthResponse>("/health");
Expand Down Expand Up @@ -99,3 +103,11 @@ export const apiDeckWithCards = (deckId: number) => {
export const apiDeckCategories = () => {
return request<DeckCategoryResponse>("/deck-categories");
};

export const apiFolderUpsert = (body: AddFolderRequest) => {
return request<AddFolderResponse, AddFolderRequest>(
"/upsert-folder",
"POST",
body,
);
};
12 changes: 12 additions & 0 deletions src/screens/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
import { RepeatAllScreen } from "./deck-review/repeat-all-screen.tsx";
import { DeckCatalog } from "./deck-catalog/deck-catalog.tsx";
import { DeckCatalogStoreContextProvider } from "../store/deck-catalog-store-context.tsx";
import { DeckOrFolderChoose } from "./deck-or-folder-choose/deck-or-folder-choose.tsx";
import { FolderForm } from "./folder-form/folder-form.tsx";

export const App = observer(() => {
useRestoreFullScreenExpand();
Expand Down Expand Up @@ -53,6 +55,16 @@ export const App = observer(() => {
</ReviewStoreProvider>
</PreventTelegramSwipeDownClosingIos>
)}
{screenStore.screen.type === "deckOrFolderChoose" && (
<PreventTelegramSwipeDownClosingIos>
<DeckOrFolderChoose />
</PreventTelegramSwipeDownClosingIos>
)}
{screenStore.screen.type === "folderForm" && (
<PreventTelegramSwipeDownClosingIos>
<FolderForm />
</PreventTelegramSwipeDownClosingIos>
)}
{screenStore.screen.type === "deckForm" && (
<PreventTelegramSwipeDownClosingIos>
<DeckFormStoreProvider>
Expand Down
15 changes: 3 additions & 12 deletions src/screens/deck-form/card-form-view.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { observer } from "mobx-react-lite";
import { css } from "@emotion/css";
import { Label } from "../../ui/label.tsx";
import { Input } from "../../ui/input.tsx";
import React from "react";
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";

type Props = {
cardForm: CardFormType;
Expand All @@ -15,16 +15,7 @@ export const CardFormView = observer((props: Props) => {
const { cardForm } = props;

return (
<div
className={css({
display: "flex",
flexDirection: "column",
gap: 16,
marginBottom: 16,
position: "relative",
})}
>
<h3 className={css({ textAlign: "center" })}>{t("add_card")}</h3>
<Screen title={cardForm ? t("edit_card") : t("add_card")}>
<Label text={t("card_front_title")} isRequired>
<Input field={cardForm.front} rows={3} type={"textarea"} />
<HintTransparent>{t("card_front_side_hint")}</HintTransparent>
Expand All @@ -39,6 +30,6 @@ export const CardFormView = observer((props: Props) => {
<Input field={cardForm.example} rows={2} type={"textarea"} />
<HintTransparent>{t("card_field_example_hint")}</HintTransparent>
</Label>
</div>
</Screen>
);
});
16 changes: 3 additions & 13 deletions src/screens/deck-form/deck-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
import { DeckSpeakFieldEnum } from "../../../functions/db/deck/decks-with-cards-schema.ts";
import { theme } from "../../ui/theme.tsx";
import { t } from "../../translations/t.ts";
import { Screen } from "../shared/screen.tsx";

export const DeckForm = observer(() => {
const deckFormStore = useDeckFormStore();
Expand All @@ -49,18 +50,7 @@ export const DeckForm = observer(() => {
}

return (
<div
className={css({
display: "flex",
flexDirection: "column",
gap: 6,
position: "relative",
marginBottom: 16,
})}
>
<h3 className={css({ textAlign: "center" })}>
{screen.deckId ? t("edit_deck") : t("add_deck")}
</h3>
<Screen title={screen.deckId ? t("edit_deck") : t("add_deck")}>
<Label text={t("title")} isRequired>
<Input field={deckFormStore.form.title} />
</Label>
Expand Down Expand Up @@ -147,6 +137,6 @@ export const DeckForm = observer(() => {
>
{t("add_card")}
</Button>
</div>
</Screen>
);
});
Loading

0 comments on commit 22ea7d8

Please sign in to comment.