Skip to content

Commit

Permalink
Add multiple cards in one go (#31)
Browse files Browse the repository at this point in the history
* Add multiple cards in one go
  • Loading branch information
kubk authored Dec 28, 2023
1 parent 82ae10a commit 1e17fe1
Show file tree
Hide file tree
Showing 13 changed files with 395 additions and 96 deletions.
27 changes: 26 additions & 1 deletion functions/db/user/user-set-server-bot-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,33 @@ import { DatabaseException } from "../database-exception.ts";

export type ServerBotState =
| null
| { type: "cardAdded"; cardFront: string; cardBack: string }
| {
// better name - cardGiven
type: "cardAdded";
cardFront: string;
cardBack: string;
cardExample: string | null;
}
| {
// better name - manyCardsGiven
type: "manyCardsAdded";
cards: Array<{
cardFront: string;
cardBack: string;
cardExample: string | null;
}>;
}
| {
type: "deckWithManyCardsSelected";
deckId: number;
cards: Array<{
cardFront: string;
cardBack: string;
cardExample: string | null;
}>;
}
| {
// deck with only 1 card
type: "deckSelected";
cardFront: string;
cardBack: string;
Expand Down
1 change: 1 addition & 0 deletions functions/server-bot/callback-query-type.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export enum CallbackQueryType {
Deck = "deck",
ConfirmCreateCard = "confirm",
ConfirmCreateManyCards = "confirm-many-cards",
EditFront = "edit-front",
EditExample = "edit-example",
EditBack = "edit-back",
Expand Down
68 changes: 56 additions & 12 deletions functions/server-bot/on-callback-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { sendCardCreateConfirmMessage } from "./send-card-create-confirm-message
import { DatabaseException } from "../db/database-exception.ts";
import { createUserAwareTranslator } from "../translations/create-user-aware-translator.ts";
import { MemoCardTranslator } from "../translations/create-translator.ts";
import { sendMultipleCardsCreateConfirmMessage } from "./send-multiple-cards-create-confirm-message.ts";

type CallbackQueryEdit =
| CallbackQueryType.EditFront
Expand Down Expand Up @@ -51,32 +52,49 @@ export const onCallbackQuery = (envSafe: EnvSafe) => async (ctx: Context) => {
assert(ctx.from);

const data = ctx.callbackQuery.data;
const db = getDatabase(envSafe);
if (!data) {
await ctx.answerCallbackQuery();
return;
}

const db = getDatabase(envSafe);
const translator = await createUserAwareTranslator(envSafe, ctx);

if (data.startsWith(CallbackQueryType.Deck)) {
if (data.startsWith(`${CallbackQueryType.Deck}:`)) {
const deckId = Number(data.split(":")[1]);
if (!deckId) {
throw new Error(`Deck id ${deckId} is not valid`);
}
const state = await userGetServerBotState(envSafe, ctx.from.id);
if (state?.type !== "cardAdded") {
if (
!state ||
(state.type !== "cardAdded" && state.type !== "manyCardsAdded")
) {
return;
}
await userSetServerBotState(envSafe, ctx.from.id, {
type: "deckSelected",
cardBack: state.cardBack,
cardFront: state.cardFront,
cardExample: null,
deckId,
});

await sendCardCreateConfirmMessage(envSafe, ctx);
if (state.type === "cardAdded") {
await userSetServerBotState(envSafe, ctx.from.id, {
type: "deckSelected",
cardBack: state.cardBack,
cardFront: state.cardFront,
cardExample: state.cardExample,
deckId,
});

await sendCardCreateConfirmMessage(envSafe, ctx, translator);
}

if (state.type === "manyCardsAdded") {
await userSetServerBotState(envSafe, ctx.from.id, {
type: "deckWithManyCardsSelected",
cards: state.cards,
deckId,
});

await sendMultipleCardsCreateConfirmMessage(envSafe, ctx, translator);
}

await ctx.answerCallbackQuery();
return;
}
Expand Down Expand Up @@ -121,7 +139,33 @@ export const onCallbackQuery = (envSafe: EnvSafe) => async (ctx: Context) => {
throw new DatabaseException(createCardsResult.error);
}

await ctx.reply(translator.translate("card_created"));
await ctx.reply(`${translator.translate("card_created")}`);
await ctx.deleteMessage();
await userSetServerBotState(envSafe, ctx.from.id, null);
return;
}

if (data === CallbackQueryType.ConfirmCreateManyCards) {
const state = await userGetServerBotState(envSafe, ctx.from.id);
assert(
state?.type === "deckWithManyCardsSelected",
"State is not deckWithManyCardsSelected",
);

const createCardsResult = await db.from("deck_card").insert(
state.cards.map((card) => ({
deck_id: state.deckId,
front: card.cardFront,
back: card.cardBack,
example: card.cardExample,
})),
);

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

await ctx.reply(translator.translate("many_cards_created"));
await ctx.deleteMessage();
await userSetServerBotState(envSafe, ctx.from.id, null);
return;
Expand Down
39 changes: 27 additions & 12 deletions functions/server-bot/on-message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
userSetServerBotState,
} from "../db/user/user-set-server-bot-state.ts";
import { sendCardCreateConfirmMessage } from "./send-card-create-confirm-message.ts";
import { parseDeckFromText } from "./parse-deck-from-text.ts";
import { parseCardsFromText } from "./parse-cards-from-text.ts";
import { getDecksCreatedByMe } from "../db/deck/get-decks-created-by-me.ts";
import { CallbackQueryType } from "./callback-query-type.ts";
import { createUserAwareTranslator } from "../translations/create-user-aware-translator.ts";
Expand All @@ -28,21 +28,20 @@ export const onMessage = (envSafe: EnvSafe) => async (ctx: Context) => {
editingField: undefined,
});

await sendCardCreateConfirmMessage(envSafe, ctx);

await sendCardCreateConfirmMessage(envSafe, ctx, translator);
return;
}

const cardAsText = parseDeckFromText(ctx.message.text);
if (!cardAsText) {
const cardsParsed = parseCardsFromText(ctx.message.text);
if (!cardsParsed.length) {
await ctx.reply(translator.translate("invalid_card_format"), {
parse_mode: "MarkdownV2",
});
return;
}

const decks = await getDecksCreatedByMe(envSafe, ctx.from.id);
if (decks.length === 0) {
if (!decks.length) {
await ctx.reply(translator.translate("no_decks_created"), {
reply_markup: new InlineKeyboard().url(
translator.translate("create_deck"),
Expand All @@ -52,13 +51,29 @@ export const onMessage = (envSafe: EnvSafe) => async (ctx: Context) => {
return;
}

await userSetServerBotState(envSafe, ctx.from.id, {
type: "cardAdded",
cardFront: cardAsText.front,
cardBack: cardAsText.back,
});
let message = "";
if (cardsParsed.length === 1) {
const cardParsed = cardsParsed[0];
await userSetServerBotState(envSafe, ctx.from.id, {
type: "cardAdded",
cardFront: cardParsed.front,
cardBack: cardParsed.back,
cardExample: cardParsed.example || null,
});
message = translator.translate("create_card_from_deck_message");
} else {
await userSetServerBotState(envSafe, ctx.from.id, {
type: "manyCardsAdded",
cards: cardsParsed.map((cardParsed) => ({
cardFront: cardParsed.front,
cardBack: cardParsed.back,
cardExample: cardParsed.example || null,
})),
});
message = translator.translate("create_many_cards_message");
}

await ctx.reply(translator.translate("create_card_from_deck_message"), {
await ctx.reply(message, {
reply_markup: InlineKeyboard.from(
decks
.map((deck) => [
Expand Down
31 changes: 31 additions & 0 deletions functions/server-bot/parse-cards-from-text.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const fieldSeparator = " - ";
const deckSeparator = "\n";
// deckSeparator as regex one or many new lines
const deckSeparatorRegex = /\n+/g;

type CardParsed = {
front: string;
back: string;
example?: string;
};

export const parseCardsFromText = (text: string): Array<CardParsed> => {
if (!text.includes(deckSeparator)) {
return [extractDeckFromRow(text)];
}

const rows = text.split(deckSeparatorRegex);

return rows.map(extractDeckFromRow);
};

const extractDeckFromRow = (row: string): CardParsed => {
const [front, back, example] = row.split(fieldSeparator);
if (!front || !back) {
return {
front: row,
back: row,
};
}
return { front, back, example };
};
99 changes: 86 additions & 13 deletions functions/server-bot/parse-deck-from-text.test.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,92 @@
import { expect, test } from "vitest";
import { parseDeckFromText } from "./parse-deck-from-text.ts";
import { parseCardsFromText } from "./parse-cards-from-text.ts";

test("parse deck from text", () => {
expect(parseDeckFromText("front - back")).toEqual({
front: "front",
back: "back",
});
expect(parseCardsFromText("front - back")).toEqual([
{
front: "front",
back: "back",
example: undefined,
},
]);

expect(parseDeckFromText("front - back - test")).toEqual({
front: "front",
back: "back",
});
expect(parseCardsFromText("front - back - test")).toEqual([
{
front: "front",
back: "back",
example: "test",
},
]);

expect(parseDeckFromText("one side")).toEqual({
front: "one side",
back: "one side",
});
expect(parseCardsFromText("one side")).toEqual([
{
front: "one side",
back: "one side",
example: undefined,
},
]);

expect(parseCardsFromText("one side\na\nb")).toEqual([
{
front: "one side",
back: "one side",
example: undefined,
},
{
front: "a",
back: "a",
},
{
front: "b",
back: "b",
},
]);

expect(
parseCardsFromText("front - back\na - b - c\nd - e\nk - n - m"),
).toEqual([
{
front: "front",
back: "back",
example: undefined,
},
{
front: "a",
back: "b",
example: "c",
},
{
front: "d",
back: "e",
},
{
front: "k",
back: "n",
example: "m",
},
]);

expect(
parseCardsFromText("front - back\n\n\na - b - c\n\nd - e\nk - n - m"),
).toEqual([
{
front: "front",
back: "back",
example: undefined,
},
{
front: "a",
back: "b",
example: "c",
},
{
front: "d",
back: "e",
},
{
front: "k",
back: "n",
example: "m",
},
]);
});
21 changes: 0 additions & 21 deletions functions/server-bot/parse-deck-from-text.ts

This file was deleted.

9 changes: 9 additions & 0 deletions functions/server-bot/render-field-value.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { escapeMarkdown } from "./escape-markdown.ts";

export const renderFieldValue = (value: string | null) => {
if (!value) {
return "_None_";
}

return escapeMarkdown(value);
};
Loading

0 comments on commit 1e17fe1

Please sign in to comment.