diff --git a/functions/db/user/user-set-server-bot-state.ts b/functions/db/user/user-set-server-bot-state.ts index 7b6d61e5..4984580e 100644 --- a/functions/db/user/user-set-server-bot-state.ts +++ b/functions/db/user/user-set-server-bot-state.ts @@ -9,8 +9,9 @@ export type ServerBotState = type: "deckSelected"; cardFront: string; cardBack: string; + cardExample: string | null; deckId: number; - editingField?: "cardFront" | "cardBack"; + editingField?: "cardFront" | "cardBack" | "cardExample"; }; export const userSetServerBotState = async ( diff --git a/functions/server-bot/callback-query-type.ts b/functions/server-bot/callback-query-type.ts index fae30942..79b50daf 100644 --- a/functions/server-bot/callback-query-type.ts +++ b/functions/server-bot/callback-query-type.ts @@ -2,6 +2,7 @@ export enum CallbackQueryType { Deck = "deck", ConfirmCreateCard = "confirm", EditFront = "edit-front", + EditExample = "edit-example", EditBack = "edit-back", Cancel = "cancel", } diff --git a/functions/server-bot/escape-markdown.ts b/functions/server-bot/escape-markdown.ts index 29e8fb8e..a02a0980 100644 --- a/functions/server-bot/escape-markdown.ts +++ b/functions/server-bot/escape-markdown.ts @@ -18,13 +18,13 @@ const SPECIAL_CHARS = [ "{", "}", ".", - "!" + "!", ]; // https://core.telegram.org/bots/api#markdownv2-style export const escapeMarkdown = (text: string) => { - SPECIAL_CHARS.forEach(char => { - const regex = new RegExp(`\\${char}`, 'g'); + SPECIAL_CHARS.forEach((char) => { + const regex = new RegExp(`\\${char}`, "g"); text = text.replace(regex, `\\${char}`); }); return text; diff --git a/functions/server-bot/on-callback-query.ts b/functions/server-bot/on-callback-query.ts index 22a47e98..fedd9a4a 100644 --- a/functions/server-bot/on-callback-query.ts +++ b/functions/server-bot/on-callback-query.ts @@ -10,6 +10,37 @@ import { import { sendCardCreateConfirmMessage } from "./send-card-create-confirm-message.ts"; import { DatabaseException } from "../db/database-exception.ts"; +type CallbackQueryEdit = + | CallbackQueryType.EditFront + | CallbackQueryType.EditBack + | CallbackQueryType.EditExample; + +const callbackQueryEditTypeToField = (data: CallbackQueryEdit) => { + switch (data) { + case CallbackQueryType.EditFront: + return "cardFront"; + case CallbackQueryType.EditBack: + return "cardBack"; + case CallbackQueryType.EditExample: + return "cardExample"; + default: + return data satisfies never; + } +}; + +const callbackQueryToHumanReadable = (data: CallbackQueryEdit) => { + switch (data) { + case CallbackQueryType.EditFront: + return "front"; + case CallbackQueryType.EditBack: + return "back"; + case CallbackQueryType.EditExample: + return "example"; + default: + return data satisfies never; + } +}; + export const onCallbackQuery = (envSafe: EnvSafe) => async (ctx: Context) => { assert(ctx.callbackQuery); assert(ctx.from); @@ -34,6 +65,7 @@ export const onCallbackQuery = (envSafe: EnvSafe) => async (ctx: Context) => { type: "deckSelected", cardBack: state.cardBack, cardFront: state.cardFront, + cardExample: null, deckId, }); @@ -44,19 +76,19 @@ export const onCallbackQuery = (envSafe: EnvSafe) => async (ctx: Context) => { if ( data === CallbackQueryType.EditFront || - data === CallbackQueryType.EditBack + data === CallbackQueryType.EditBack || + data === CallbackQueryType.EditExample ) { - const isFront = data === CallbackQueryType.EditFront; const state = await userGetServerBotState(envSafe, ctx.from.id); assert(state?.type === "deckSelected", "State is not deckSelected"); + const editingField = callbackQueryEditTypeToField(data); + const editingFieldHuman = callbackQueryToHumanReadable(data); await userSetServerBotState(envSafe, ctx.from.id, { ...state, - editingField: isFront ? "cardFront" : "cardBack", + editingField, }); await ctx.deleteMessage(); - await ctx.reply( - `Send a message with the new ${isFront ? "front" : "back"}:`, - ); + await ctx.reply(`Send a message with the new ${editingFieldHuman}:`); return; } @@ -75,13 +107,14 @@ export const onCallbackQuery = (envSafe: EnvSafe) => async (ctx: Context) => { deck_id: state.deckId, front: state.cardFront, back: state.cardBack, + example: state.cardExample, }); if (createCardsResult.error) { throw new DatabaseException(createCardsResult.error); } - await ctx.reply('Card has been created'); + await ctx.reply("Card has been created"); await ctx.deleteMessage(); await userSetServerBotState(envSafe, ctx.from.id, null); return; diff --git a/functions/server-bot/send-card-create-confirm-message.ts b/functions/server-bot/send-card-create-confirm-message.ts index b76a571e..058e78f1 100644 --- a/functions/server-bot/send-card-create-confirm-message.ts +++ b/functions/server-bot/send-card-create-confirm-message.ts @@ -8,6 +8,14 @@ import { import { CallbackQueryType } from "./callback-query-type.ts"; import { escapeMarkdown } from "./escape-markdown.ts"; +const renderFieldValue = (value: string | null) => { + if (!value) { + return "_None_"; + } + + return escapeMarkdown(value); +}; + export const sendCardCreateConfirmMessage = async ( envSafe: EnvSafe, ctx: Context, @@ -21,14 +29,17 @@ export const sendCardCreateConfirmMessage = async ( cardBack: state.cardBack, cardFront: state.cardFront, deckId: state.deckId, + cardExample: state.cardExample, }); await ctx.deleteMessage(); await ctx.reply( - `Confirm card creation:\n\n*Front:*\n${escapeMarkdown( + `Confirm card creation:\n\n*Front:* ${renderFieldValue( state.cardFront, - )}\n\n*Back:*\n${escapeMarkdown(state.cardBack)}`, + )}\n\n*Back:* ${renderFieldValue( + state.cardBack, + )}\n\n*Example:* ${renderFieldValue(state.cardExample)}`, { parse_mode: "MarkdownV2", reply_markup: InlineKeyboard.from([ @@ -41,6 +52,7 @@ export const sendCardCreateConfirmMessage = async ( [ InlineKeyboard.text(`✏️ Edit front`, CallbackQueryType.EditFront), InlineKeyboard.text(`✏️ Edit back`, CallbackQueryType.EditBack), + InlineKeyboard.text(`✏️ Edit example`, CallbackQueryType.EditExample), ], [InlineKeyboard.text(`❌ Cancel`, CallbackQueryType.Cancel)], ]),