Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions frontend/src/ts/collections/quote-ratings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { Language } from "@monkeytype/schemas/languages";
import { QuoteRating } from "@monkeytype/schemas/quotes";
import {
parseLoadSubsetOptions,
queryCollectionOptions,
} from "@tanstack/query-db-collection";
import { createCollection, eq, useLiveQuery } from "@tanstack/solid-db";
import { Accessor } from "solid-js";
import { queryClient } from "../queries";
import { baseKey } from "../queries/utils/keys";
import Ape from "../ape";
import { getSnapshot } from "../states/snapshot";

type QuoteUserRating = QuoteRating & {
userRating?: number;
};

const queryKeys = {
root: () => [...baseKey("quoteRatings", { isUserSpecific: true })],
};

export const quoteRatingsCollection = createCollection(
queryCollectionOptions({
staleTime: Infinity,
queryKey: queryKeys.root(),
syncMode: "on-demand", // Enable predicate push-down
queryFn: async ({ meta }) => {
if (meta?.loadSubsetOptions === undefined) {
throw new Error("missing where clause in quoteRatingsCollection");
}
const { where } = meta.loadSubsetOptions;
const parsed = parseLoadSubsetOptions({ where });

const language = parsed.filters.find((it) => it.field?.[0] === "language")
?.value as Language;
const quoteId = parsed.filters.find((it) => it.field?.[0] === "quoteId")
?.value as number;

const response = await Ape.quotes.getRating({
query: { language, quoteId },
});
if (response.status !== 200) {
throw new Error(
"Error fetching quote ratings:" + response.body.message,
);
}

const userRating = getSnapshot()?.quoteRatings?.[language]?.[quoteId];

const existingData: QuoteUserRating[] =
queryClient.getQueryData(queryKeys.root()) ?? [];

if (response.body.data !== null) {
existingData.push({ ...response.body.data, userRating });
}

return existingData;
},
onInsert: async ({ transaction }) => {
const newItems = transaction.mutations.map((it) => it.modified);

quoteRatingsCollection.utils.writeBatch(() => {
newItems.forEach((item) => {
quoteRatingsCollection.utils.writeInsert(item);
});
});

newItems.forEach(async (it) => {
if (it.userRating !== undefined) {
const response = await Ape.quotes.addRating({
body: {
language: it.language,
quoteId: it.quoteId,
rating: it.userRating,
},
});
if (response.status !== 200) {
throw new Error(
"Cannot submit quote rating: " + response.body.message,
);
}
}
});
},
onUpdate: async ({ transaction }) => {
const newItems = transaction.mutations.map((it) => it.modified);

//TODO update rating average and total
quoteRatingsCollection.utils.writeBatch(() => {
newItems.forEach((item) => {
quoteRatingsCollection.utils.writeUpdate(item);
});
});

newItems.forEach(async (it) => {
if (it.userRating !== undefined) {
const response = await Ape.quotes.addRating({
body: {
language: it.language,
quoteId: it.quoteId,
rating: it.userRating,
},
});
if (response.status !== 200) {
throw new Error(
"Cannot submit quote rating: " + response.body.message,
);
}
}
});
},

queryClient,
getKey: (it) => it._id,
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use lang+quoteId instead

}),
);

// oxlint-disable-next-line typescript/explicit-function-return-type
export function useQuoteRatingsLiveQuery(
filterAccessor: Accessor<{
language: Language;
id: number;
} | null>,
) {
return useLiveQuery((q) => {
const filter = filterAccessor();
if (filter === null) return undefined;
return q
.from({ r: quoteRatingsCollection })
.where(({ r }) => eq(r.language, filter.language))
.where(({ r }) => eq(r.quoteId, filter.id))
.findOne();
});
}
10 changes: 5 additions & 5 deletions frontend/src/ts/commandline/lists/quote-favorites.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import {
} from "../../states/notifications";
import { isAuthenticated } from "../../firebase";
import { showLoaderBar, hideLoaderBar } from "../../states/loader-bar";
import * as TestWords from "../../test/test-words";
import { Command } from "../types";
import { getCurrentQuote } from "../../states/test";

const commands: Command[] = [
{
id: "addQuoteToFavorite",
display: "Add current quote to favorite",
icon: "fa-heart",
available: (): boolean => {
const quote = TestWords.currentQuote;
const quote = getCurrentQuote();
return (
isAuthenticated() &&
quote !== null &&
Expand All @@ -27,7 +27,7 @@ const commands: Command[] = [
try {
showLoaderBar();
await QuotesController.setQuoteFavorite(
TestWords.currentQuote as Quote,
getCurrentQuote() as Quote,
true,
);
hideLoaderBar();
Expand All @@ -43,7 +43,7 @@ const commands: Command[] = [
display: "Remove current quote from favorite",
icon: "fa-heart-broken",
available: (): boolean => {
const quote = TestWords.currentQuote;
const quote = getCurrentQuote();
return (
isAuthenticated() &&
quote !== null &&
Expand All @@ -55,7 +55,7 @@ const commands: Command[] = [
try {
showLoaderBar();
await QuotesController.setQuoteFavorite(
TestWords.currentQuote as Quote,
getCurrentQuote() as Quote,
false,
);
hideLoaderBar();
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/ts/components/core/DevTools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ if (import.meta.env.DEV) {
default: () => {
onMount(() => {
m.attachDevtoolsOverlay({
defaultOpen: true,
defaultOpen: false,
noPadding: true,
});
});
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/ts/components/layout/footer/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { JSXElement } from "solid-js";

import { useQuoteRatingsLiveQuery } from "../../../collections/quote-ratings";
import { getFocus, getIsScreenshotting } from "../../../states/core";
import { showModal } from "../../../states/modals";
import { getCurrentQuote } from "../../../states/test";
import { cn } from "../../../utils/cn";
import AsyncContent from "../../common/AsyncContent";
import { Button } from "../../common/Button";
import { Keytips } from "./Keytips";
import { ThemeIndicator } from "./ThemeIndicator";
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/ts/components/layout/overlays/LoaderBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { JSX } from "solid-js";

import { useRefWithUtils } from "../../../hooks/useRefWithUtils";
import { useVisibilityAnimation } from "../../../hooks/useVisibilityAnimation";
import { queryClient } from "../../../queries";
import { getLoaderBarSignal } from "../../../states/loader-bar";
import { applyReducedMotion } from "../../../utils/misc";

Expand All @@ -10,7 +11,8 @@ export function LoaderBar(): JSX.Element {

useVisibilityAnimation({
element: loaderEl,
isVisible: () => getLoaderBarSignal()?.visible === true,
isVisible: () =>
getLoaderBarSignal()?.visible === true || queryClient.isFetching() > 0,
showAnimationOptions: {
delay: applyReducedMotion(getLoaderBarSignal()?.instant ? 0 : 125),
},
Expand Down
Loading
Loading