From a7bf2cb1ead6eb0397ad8f96c4d254b5a107c00e Mon Sep 17 00:00:00 2001 From: Gorbachev Egor <7gorbachevm@gmail.com> Date: Fri, 5 Jan 2024 17:45:48 +0700 Subject: [PATCH] f --- .../get-last-deck-accesses-for-deck-db.ts | 27 ++++++++++++++++--- functions/deck-accesses.ts | 24 ++++++----------- src/api/api.ts | 4 +-- src/screens/deck-form/deck-form.tsx | 26 +++++++++++++++++- .../share-deck/share-deck-one-time-links.tsx | 27 ++++++++++++++++--- .../share-deck/store/share-deck-store.ts | 4 +-- src/store/deck-list-store.ts | 16 +++++++++++ src/translations/t.ts | 12 ++++++--- 8 files changed, 108 insertions(+), 32 deletions(-) diff --git a/functions/db/deck-access/get-last-deck-accesses-for-deck-db.ts b/functions/db/deck-access/get-last-deck-accesses-for-deck-db.ts index d6960de4..c14bbfb1 100644 --- a/functions/db/deck-access/get-last-deck-accesses-for-deck-db.ts +++ b/functions/db/deck-access/get-last-deck-accesses-for-deck-db.ts @@ -1,17 +1,38 @@ import { DatabaseException } from "../database-exception.ts"; import { getDatabase } from "../get-database.ts"; import { EnvSafe } from "../../env/env-schema.ts"; +import { z } from "zod"; + +const responseSchema = z.array( + z.object({ + used_by: z.number().nullable(), + user: z + .object({ + id: z.number(), + username: z.string().nullable(), + first_name: z.string().nullable(), + last_name: z.string().nullable(), + }) + .nullable(), + share_id: z.string(), + id: z.number(), + created_at: z.string(), + duration_days: z.number().nullable(), + }), +); + +export type DeckAccessesForDeckTypeDb = z.infer; export const getLastDeckAccessesForDeckDb = async ( envSafe: EnvSafe, deckId: number, -) => { +): Promise => { const db = getDatabase(envSafe); const { data, error } = await db .from("deck_access") .select( - "deck_id, author_id, used_by, share_id, id, created_at, duration_days", + "deck_id, author_id, used_by, share_id, id, created_at, duration_days, user:used_by (id, username, first_name, last_name)", ) .eq("deck_id", deckId) .order("created_at", { ascending: false }) @@ -21,5 +42,5 @@ export const getLastDeckAccessesForDeckDb = async ( throw new DatabaseException(error); } - return data; + return responseSchema.parse(data); }; diff --git a/functions/deck-accesses.ts b/functions/deck-accesses.ts index f5090a0a..60b29a2e 100644 --- a/functions/deck-accesses.ts +++ b/functions/deck-accesses.ts @@ -4,22 +4,14 @@ import { createAuthFailedResponse } from "./lib/json-response/create-auth-failed import { handleError } from "./lib/handle-error/handle-error.ts"; import { envSchema } from "./env/env-schema.ts"; import { createBadRequestResponse } from "./lib/json-response/create-bad-request-response.ts"; -import { getLastDeckAccessesForDeckDb } from "./db/deck-access/get-last-deck-accesses-for-deck-db.ts"; -import { z } from "zod"; +import { + DeckAccessesForDeckTypeDb, + getLastDeckAccessesForDeckDb, +} from "./db/deck-access/get-last-deck-accesses-for-deck-db.ts"; -const responseSchema = z.object({ - accesses: z.array( - z.object({ - used_by: z.number().nullable(), - share_id: z.string(), - id: z.number(), - created_at: z.string(), - duration_days: z.number().nullable(), - }), - ), -}); - -export type DeckAccessesForDeckTypeDb = z.infer; +export type DeckAccessesResponse = { + accesses: DeckAccessesForDeckTypeDb; +}; export const onRequest = handleError(async ({ request, env }) => { const user = await getUser(request, env); @@ -35,7 +27,7 @@ export const onRequest = handleError(async ({ request, env }) => { const data = await getLastDeckAccessesForDeckDb(envSafe, Number(deckId)); - return createJsonResponse({ + return createJsonResponse({ accesses: data, }); }); diff --git a/src/api/api.ts b/src/api/api.ts index 560489ec..557eb240 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -27,7 +27,7 @@ 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 { DeckAccessesForDeckTypeDb } from "../../functions/deck-accesses.ts"; +import { DeckAccessesResponse } from "../../functions/deck-accesses.ts"; import { AddDeckAccessRequest, AddDeckAccessResponse, @@ -54,7 +54,7 @@ export const addDeckToMineRequest = (body: AddDeckToMineRequest) => { }; export const getDeckAccessesOfDeckRequest = (deckId: number) => { - return request(`/deck-accesses?deck_id=${deckId}`); + return request(`/deck-accesses?deck_id=${deckId}`); }; export const addDeckAccessRequest = (body: AddDeckAccessRequest) => { diff --git a/src/screens/deck-form/deck-form.tsx b/src/screens/deck-form/deck-form.tsx index e7b56d8d..be1a1578 100644 --- a/src/screens/deck-form/deck-form.tsx +++ b/src/screens/deck-form/deck-form.tsx @@ -1,5 +1,5 @@ import { observer } from "mobx-react-lite"; -import { css } from "@emotion/css"; +import { css, cx } from "@emotion/css"; import { Label } from "../../ui/label.tsx"; import { Input } from "../../ui/input.tsx"; import React from "react"; @@ -23,6 +23,8 @@ 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 { deckListStore } from "../../store/deck-list-store.ts"; +import { reset } from "../../ui/reset.ts"; export const DeckForm = observer(() => { const deckFormStore = useDeckFormStore(); @@ -141,12 +143,34 @@ export const DeckForm = observer(() => {
+ {deckFormStore.form.id ? ( + + ) : null}
); }); diff --git a/src/screens/share-deck/share-deck-one-time-links.tsx b/src/screens/share-deck/share-deck-one-time-links.tsx index f888cd4f..bbfa9601 100644 --- a/src/screens/share-deck/share-deck-one-time-links.tsx +++ b/src/screens/share-deck/share-deck-one-time-links.tsx @@ -11,6 +11,21 @@ import { theme } from "../../ui/theme.tsx"; import { DateTime } from "luxon"; import { useShareDeckStore } from "./store/share-deck-store-context.tsx"; +const formatAccessUser = (user: { + id: number; + username: string | null; + first_name: string | null; + last_name: string | null; +}) => { + if (user.username) { + return `@${user.username}`; + } + if (user.first_name || user.last_name) { + return `${user.first_name ?? ""} ${user.last_name ?? ""}`; + } + return `#${user.id}`; +}; + export const ShareDeckOneTimeLinks = observer(() => { const store = useShareDeckStore(); @@ -64,7 +79,8 @@ export const ShareDeckOneTimeLinks = observer(() => { className={css({ paddingTop: 6, marginLeft: 12, - borderTop: i !== 0 ? "1px solid #ccc" : undefined, + borderTop: + i !== 0 ? `1px solid ${theme.dividerColor}` : undefined, })} >
@@ -73,8 +89,7 @@ export const ShareDeckOneTimeLinks = observer(() => { fontWeight: 500, })} > - #{access.id}:{" "} - {access.used_by ? t("share_used") : t("share_unused")} + #{access.id}{" "} { const link = getDeckLink(access.share_id); @@ -86,10 +101,14 @@ export const ShareDeckOneTimeLinks = observer(() => { cursor: "pointer", })} > - {" "} {t("share_copy_link")}
+
+ {access.used_by && access.user + ? `${t("share_used")} ${formatAccessUser(access.user)}` + : t("share_unused")} +
{t("share_access_duration_days")}:{" "} {access.duration_days ?? ( diff --git a/src/screens/share-deck/store/share-deck-store.ts b/src/screens/share-deck/store/share-deck-store.ts index 7c19ed0e..adfba20c 100644 --- a/src/screens/share-deck/store/share-deck-store.ts +++ b/src/screens/share-deck/store/share-deck-store.ts @@ -12,11 +12,11 @@ import { } from "../../../api/api.ts"; import { persistableField } from "../../../lib/mobx-form/persistable-field.ts"; import { fromPromise, IPromiseBasedObservable } from "mobx-utils"; -import { DeckAccessesForDeckTypeDb } from "../../../../functions/deck-accesses.ts"; +import { DeckAccessesResponse } from "../../../../functions/deck-accesses.ts"; export class ShareDeckStore { isSending = false; - deckAccesses?: IPromiseBasedObservable; + deckAccesses?: IPromiseBasedObservable; isDeckAccessesOpen = new BooleanToggle(false); form = { diff --git a/src/store/deck-list-store.ts b/src/store/deck-list-store.ts index 782a5958..e855c30b 100644 --- a/src/store/deck-list-store.ts +++ b/src/store/deck-list-store.ts @@ -232,6 +232,22 @@ export class DeckListStore { ); } + goDeckById(deckId: number) { + if (!this.myInfo) { + return null; + } + const myDeck = this.myInfo.myDecks.find((deck) => deck.id === deckId); + if (myDeck) { + screenStore.go({ type: "deckMine", deckId }); + return; + } + const publicDeck = this.publicDecks.find((deck) => deck.id === deckId); + if (publicDeck) { + screenStore.go({ type: "deckPublic", deckId }); + return; + } + } + searchDeckById(deckId: number) { if (!this.myInfo) { return null; diff --git a/src/translations/t.ts b/src/translations/t.ts index bb031c57..4a762544 100644 --- a/src/translations/t.ts +++ b/src/translations/t.ts @@ -31,6 +31,7 @@ const en = { category_History: "History", save: "Save", add_card: "Add card", + deck_preview: 'Deck preview', add_card_short: "Add card", card_front_title: "Front side", card_back_title: "Back side", @@ -98,7 +99,7 @@ const en = { share_days_description: "How long the deck will be available after the first use", share_one_time_links_usage: "One-time links", - share_used: "Link have been used ✅", + share_used: "Link have been used by", share_unused: "Haven't been used", share_link_copied: "The link has been copied to your clipboard", share_copy_link: "Copy link", @@ -112,6 +113,7 @@ type Translation = typeof en; const ru: Translation = { my_decks: "Мои колоды", + deck_preview: 'Предпросмотр колоды', show_all_decks: "Показать", hide_all_decks: "Скрыть", no_personal_decks_start: "У вас еще нет персональных колод. Вы можете", @@ -196,7 +198,7 @@ const ru: Translation = { validation_number: "Это поле должно быть числом", validation_positive_number: "Это поле должно быть положительным числом", share_access_duration_days: "Длительность доступа в днях", - share_used: "Использована ✅", + share_used: "Ссылка была использована", share_one_time_access_link: "Одноразовая ссылка", share_deck_settings: "Настройки шеринга колоды", share_access_duration: "Длительность доступа", @@ -219,6 +221,7 @@ const ru: Translation = { const es: Translation = { my_decks: "Mis mazos", show_all_decks: "Mostrar todos", + deck_preview: 'Vista previa del mazo', hide_all_decks: "Ocultar", no_personal_decks_start: "Todavía no tienes ningún mazo personal. Siéntete libre de", @@ -319,7 +322,7 @@ const es: Translation = { "Cuánto tiempo estará disponible el mazo después del primer uso", share_access_duration_no_limit: "Sin límite", share_access_duration: "Duración del acceso", - share_used: "El enlace ha sido utilizado ✅", + share_used: "El enlace ha sido utilizado por", share_one_time_access_link: "Enlace de acceso de un solo uso", share_access_duration_days: "Duración del acceso en días", share_deck_settings: "Compartir un mazo", @@ -330,6 +333,7 @@ const ptBr: Translation = { my_decks: "Meus baralhos", show_all_decks: "Mostrar todos", hide_all_decks: "Ocultar", + deck_preview: 'Visualização do baralho', no_personal_decks_start: "Você ainda não tem nenhum baralho pessoal. Sinta-se à vontade para", no_personal_decks_create: "criar um", @@ -418,7 +422,7 @@ const ptBr: Translation = { share_perpetual_link: "Compartilhar link perpétuo", share_deck_settings: "Compartilhar um baralho", share_access_duration_days: "Duração do acesso em dias", - share_used: "O link foi usado ✅", + share_used: "O link foi usado por", share_one_time_access_link: "Link de acesso único", share_access_duration: "Duração do acesso", share_access_duration_no_limit: "Sem limite",