Skip to content

Commit

Permalink
Deck one time access (#34)
Browse files Browse the repository at this point in the history
* Deck one time access
  • Loading branch information
kubk authored Jan 5, 2024
1 parent 706612d commit 0069c4b
Show file tree
Hide file tree
Showing 46 changed files with 669 additions and 108 deletions.
17 changes: 17 additions & 0 deletions src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ 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 { DeckAccessesResponse } from "../../functions/deck-accesses.ts";
import {
AddDeckAccessRequest,
AddDeckAccessResponse,
} from "../../functions/add-deck-access.ts";

export const healthRequest = () => {
return request<HealthResponse>("/health");
Expand All @@ -48,6 +53,18 @@ export const addDeckToMineRequest = (body: AddDeckToMineRequest) => {
);
};

export const getDeckAccessesOfDeckRequest = (deckId: number) => {
return request<DeckAccessesResponse>(`/deck-accesses?deck_id=${deckId}`);
};

export const addDeckAccessRequest = (body: AddDeckAccessRequest) => {
return request<AddDeckAccessResponse, AddDeckAccessRequest>(
"/add-deck-access",
"POST",
body,
);
};

export const apiDuplicateDeckRequest = (deckId: number) => {
return request<CopyDeckResponse>(`/duplicate-deck?deck_id=${deckId}`, "POST");
};
Expand Down
31 changes: 31 additions & 0 deletions src/lib/copy-to-clipboard/copy-to-clipboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export const copyToClipboard = async (text: string) => {
try {
await navigator.clipboard.writeText(text);
} catch (e) {
copyToClipboardOld(text);
}
};

// A hack for old android to get clipboard copy feature ready
function copyToClipboardOld(textToCopy: string) {
const textarea = document.createElement("textarea");
textarea.value = textToCopy;

// Move the textarea outside the viewport to make it invisible
textarea.style.position = "absolute";
textarea.style.left = "-99999999px";

// @ts-ignore
document.body.prepend(textarea);

// highlight the content of the textarea element
textarea.select();

try {
document.execCommand("copy");
} catch (err) {
console.log(err);
} finally {
textarea.remove();
}
}
3 changes: 2 additions & 1 deletion src/lib/mobx-form/boolean-toggle.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { makeAutoObservable } from "mobx";
import { FieldWithValue } from "./field-with-value.ts";

export class BooleanToggle {
export class BooleanToggle implements FieldWithValue<boolean> {
constructor(public value: boolean) {
makeAutoObservable(this, {}, { autoBind: true });
}
Expand Down
3 changes: 3 additions & 0 deletions src/lib/mobx-form/field-with-value.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type FieldWithValue<T> = {
value: T;
};
8 changes: 4 additions & 4 deletions src/lib/mobx-form/persistable-field.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { TextField } from "./text-field.ts";
import { makePersistable } from "mobx-persist-store";
import { FieldWithValue } from "./field-with-value.ts";

export const persistableField = <T>(
field: TextField<T>,
export const persistableField = <T extends FieldWithValue<any>>(
field: T,
storageKey: string,
expireIn?: number,
): TextField<T> => {
): T => {
makePersistable(field, {
name: storageKey,
properties: ["value"],
Expand Down
3 changes: 2 additions & 1 deletion src/lib/mobx-form/text-field.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { makeAutoObservable } from "mobx";
import { FieldWithValue } from "./field-with-value.ts";

export class TextField<T> {
export class TextField<T> implements FieldWithValue<T> {
isTouched = false;

constructor(
Expand Down
4 changes: 2 additions & 2 deletions src/lib/telegram/use-main-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useHotkeys } from "react-hotkeys-hook";
import { autorun } from "mobx";

export const useMainButton = (
text: string,
text: string | (() => string),
onClick: () => void,
condition?: () => boolean,
) => {
Expand All @@ -22,7 +22,7 @@ export const useMainButton = (
}

WebApp.MainButton.show();
WebApp.MainButton.setText(text);
WebApp.MainButton.setText(typeof text === "string" ? text : text());
WebApp.MainButton.onClick(onClick);
});

Expand Down
18 changes: 14 additions & 4 deletions src/screens/app.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { observer } from "mobx-react-lite";
import { MainScreen } from "./deck-list/main-screen.tsx";
import { DeckScreen } from "./deck-review/deck-screen.tsx";
import { ReviewStoreProvider } from "../store/review-store-context.tsx";
import { ReviewStoreProvider } from "./deck-review/store/review-store-context.tsx";
import { screenStore } from "../store/screen-store.ts";
import { DeckFormScreen } from "./deck-form/deck-form-screen.tsx";
import { DeckFormStoreProvider } from "../store/deck-form-store-context.tsx";
import { DeckFormStoreProvider } from "./deck-form/store/deck-form-store-context.tsx";
import { QuickAddCardForm } from "./deck-form/quick-add-card-form.tsx";
import { VersionWarning } from "./shared/version-warning.tsx";
import React from "react";
import { UserSettingsStoreProvider } from "../store/user-settings-store-context.tsx";
import { UserSettingsStoreProvider } from "./user-settings/store/user-settings-store-context.tsx";
import { UserSettingsMain } from "./user-settings/user-settings-main.tsx";
import { deckListStore } from "../store/deck-list-store.ts";
import { FullScreenLoader } from "./deck-list/full-screen-loader.tsx";
Expand All @@ -18,7 +18,9 @@ import {
} from "../lib/telegram/prevent-telegram-swipe-down-closing.tsx";
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 { DeckCatalogStoreContextProvider } from "./deck-catalog/store/deck-catalog-store-context.tsx";
import { ShareDeckScreen } from "./share-deck/share-deck-screen.tsx";
import { ShareDeckStoreProvider } from "./share-deck/store/share-deck-store-context.tsx";

export const App = observer(() => {
useRestoreFullScreenExpand();
Expand Down Expand Up @@ -60,6 +62,14 @@ export const App = observer(() => {
</DeckFormStoreProvider>
</PreventTelegramSwipeDownClosingIos>
)}
{screenStore.screen.type === "shareDeck" && (
<PreventTelegramSwipeDownClosingIos>
<ShareDeckStoreProvider>
<ShareDeckScreen />
</ShareDeckStoreProvider>
</PreventTelegramSwipeDownClosingIos>
)}

{screenStore.screen.type === "cardQuickAddForm" && (
<PreventTelegramSwipeDownClosingIos>
<QuickAddCardForm />
Expand Down
4 changes: 2 additions & 2 deletions src/screens/deck-catalog/deck-catalog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { useBackButton } from "../../lib/telegram/use-back-button.tsx";
import { screenStore } from "../../store/screen-store.ts";
import { css } from "@emotion/css";
import React from "react";
import { useDeckCatalogStore } from "../../store/deck-catalog-store-context.tsx";
import { useDeckCatalogStore } from "./store/deck-catalog-store-context.tsx";
import { useMount } from "../../lib/react/use-mount.ts";
import { theme } from "../../ui/theme.tsx";
import { Select } from "../../ui/select.tsx";
import {
DeckLanguage,
languageFilterToNativeName,
} from "../../store/deck-catalog-store.ts";
} from "./store/deck-catalog-store.ts";
import { DeckListItemWithDescription } from "../../ui/deck-list-item-with-description.tsx";
import { range } from "../../lib/array/range.ts";
import { DeckLoading } from "../deck-list/deck-loading.tsx";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { assert } from "../lib/typescript/assert.ts";
import { assert } from "../../../lib/typescript/assert.ts";
import { DeckCatalogStore } from "./deck-catalog-store.ts";
import { createContext, ReactNode, useContext } from "react";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { makeAutoObservable } from "mobx";
import { apiDeckCatalog, apiDeckCategories } from "../api/api.ts";
import { apiDeckCatalog, apiDeckCategories } from "../../../api/api.ts";
import { fromPromise, IPromiseBasedObservable } from "mobx-utils";
import { DeckCatalogResponse } from "../../functions/catalog-decks.ts";
import { TextField } from "../lib/mobx-form/text-field.ts";
import { cachePromise } from "../lib/cache/cache-promise.ts";
import { DeckCategoryResponse } from "../../functions/deck-categories.ts";
import { persistableField } from "../lib/mobx-form/persistable-field.ts";
import { t } from "../translations/t.ts";
import { DeckCatalogResponse } from "../../../../functions/catalog-decks.ts";
import { TextField } from "../../../lib/mobx-form/text-field.ts";
import { cachePromise } from "../../../lib/cache/cache-promise.ts";
import { DeckCategoryResponse } from "../../../../functions/deck-categories.ts";
import { persistableField } from "../../../lib/mobx-form/persistable-field.ts";
import { t } from "../../../translations/t.ts";

export enum DeckLanguage {
Any = "any",
Expand Down
2 changes: 1 addition & 1 deletion src/screens/deck-form/card-form-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ 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 { CardFormType } from "./store/deck-form-store.ts";
import { HintTransparent } from "../../ui/hint-transparent.tsx";
import { t } from "../../translations/t.ts";

Expand Down
2 changes: 1 addition & 1 deletion src/screens/deck-form/card-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { observer } from "mobx-react-lite";
import { assert } from "../../lib/typescript/assert.ts";
import React from "react";
import { useMainButton } from "../../lib/telegram/use-main-button.tsx";
import { useDeckFormStore } from "../../store/deck-form-store-context.tsx";
import { useDeckFormStore } from "./store/deck-form-store-context.tsx";
import { useBackButton } from "../../lib/telegram/use-back-button.tsx";
import { CardFormView } from "./card-form-view.tsx";
import { useTelegramProgress } from "../../lib/telegram/use-telegram-progress.tsx";
Expand Down
2 changes: 1 addition & 1 deletion src/screens/deck-form/card-list.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { observer } from "mobx-react-lite";
import { useDeckFormStore } from "../../store/deck-form-store-context.tsx";
import { useDeckFormStore } from "./store/deck-form-store-context.tsx";
import { screenStore } from "../../store/screen-store.ts";
import { assert } from "../../lib/typescript/assert.ts";
import { useBackButton } from "../../lib/telegram/use-back-button.tsx";
Expand Down
2 changes: 1 addition & 1 deletion src/screens/deck-form/deck-form-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from "react";
import { observer } from "mobx-react-lite";
import { DeckForm } from "./deck-form.tsx";
import { CardForm } from "./card-form.tsx";
import { useDeckFormStore } from "../../store/deck-form-store-context.tsx";
import { useDeckFormStore } from "./store/deck-form-store-context.tsx";
import { CardList } from "./card-list.tsx";

export const DeckFormScreen = observer(() => {
Expand Down
28 changes: 26 additions & 2 deletions src/screens/deck-form/deck-form.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
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";
import { useMainButton } from "../../lib/telegram/use-main-button.tsx";
import { useDeckFormStore } from "../../store/deck-form-store-context.tsx";
import { useDeckFormStore } from "./store/deck-form-store-context.tsx";
import { screenStore } from "../../store/screen-store.ts";
import { useMount } from "../../lib/react/use-mount.ts";
import { useBackButton } from "../../lib/telegram/use-back-button.tsx";
Expand All @@ -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();
Expand Down Expand Up @@ -141,12 +143,34 @@ export const DeckForm = observer(() => {
<div className={css({ marginTop: 18 })} />

<Button
icon={"mdi mdi-plus"}
onClick={() => {
deckFormStore.openNewCardForm();
}}
>
{t("add_card")}
</Button>
{deckFormStore.form.id ? (
<button
className={cx(
reset.button,
css({
width: "100%",
color: theme.linkColor,
fontSize: 14,
paddingTop: 6,
textTransform: "uppercase",
}),
)}
onClick={() => {
assert(deckFormStore.form);
assert(deckFormStore.form.id);
deckListStore.goDeckById(deckFormStore.form.id);
}}
>
{t("deck_preview")}
</button>
) : null}
</div>
);
});
2 changes: 1 addition & 1 deletion src/screens/deck-form/quick-add-card-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, { useState } from "react";
import { CardFormView } from "./card-form-view.tsx";
import { useMainButton } from "../../lib/telegram/use-main-button.tsx";
import { useBackButton } from "../../lib/telegram/use-back-button.tsx";
import { QuickAddCardFormStore } from "../../store/quick-add-card-form-store.ts";
import { QuickAddCardFormStore } from "./store/quick-add-card-form-store.ts";
import { useTelegramProgress } from "../../lib/telegram/use-telegram-progress.tsx";
import { t } from "../../translations/t.ts";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { createContext, ReactNode, useContext } from "react";
import { assert } from "../lib/typescript/assert.ts";
import { assert } from "../../../lib/typescript/assert.ts";
import { DeckFormStore } from "./deck-form-store.ts";

const Context = createContext<DeckFormStore | null>(null);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { CardFormType, DeckFormStore } from "./deck-form-store.ts";
import { DeckCardDbType } from "../../functions/db/deck/decks-with-cards-schema.ts";
import { type DeckWithCardsWithReviewType } from "./deck-list-store.ts";
import { assert } from "../lib/typescript/assert.ts";
import { DeckCardDbType } from "../../../../functions/db/deck/decks-with-cards-schema.ts";
import { type DeckWithCardsWithReviewType } from "../../../store/deck-list-store.ts";
import { assert } from "../../../lib/typescript/assert.ts";
import {
UpsertDeckRequest,
UpsertDeckResponse,
} from "../../functions/upsert-deck.ts";
import { isFormValid } from "../lib/mobx-form/form-has-error.ts";
} from "../../../../functions/upsert-deck.ts";
import { isFormValid } from "../../../lib/mobx-form/form-has-error.ts";

const mapUpsertDeckRequestToResponse = (
input: UpsertDeckRequest,
Expand Down Expand Up @@ -46,7 +46,7 @@ const mocks = vi.hoisted(() => {
};
});

vi.mock("./screen-store", () => {
vi.mock("./../../../store/screen-store", () => {
return {
screenStore: {
screen: {
Expand All @@ -57,7 +57,7 @@ vi.mock("./screen-store", () => {
};
});

vi.mock("./deck-list-store.ts", () => {
vi.mock("./../../../store/deck-list-store.ts", () => {
const deckCardsMock: DeckCardDbType[] = [
{
id: 3,
Expand Down Expand Up @@ -89,7 +89,7 @@ vi.mock("./deck-list-store.ts", () => {
{
id: 1,
cardsToReview: deckCardsMock.slice(0, 2),
share_id: null,
share_id: "share_id_mock",
deck_card: deckCardsMock,
name: "Test",
},
Expand All @@ -106,31 +106,31 @@ vi.mock("./deck-list-store.ts", () => {
};
});

vi.mock("../lib/telegram/show-confirm.ts", () => {
vi.mock("../../../lib/telegram/show-confirm.ts", () => {
return {
showConfirm: () => {},
};
});

vi.mock("../lib/telegram/show-alert.ts", () => {
vi.mock("../../../lib/telegram/show-alert.ts", () => {
return {
showAlert: () => {},
};
});

vi.mock("../translations/t.ts", () => {
vi.mock("../../../translations/t.ts", () => {
return {
t: (val: string) => val,
};
});

vi.mock("../api/api.ts", () => {
vi.mock("../../../api/api.ts", () => {
return {
upsertDeckRequest: mocks.upsertDeckRequest,
};
});

vi.mock("../lib/voice-playback/speak.ts", async () => {
vi.mock("../../../lib/voice-playback/speak.ts", async () => {
return {
speak: () => {},
};
Expand Down
Loading

0 comments on commit 0069c4b

Please sign in to comment.