diff --git a/frontend/src/ts/commandline/lists.ts b/frontend/src/ts/commandline/lists.ts index 8219a9a5c..708943104 100644 --- a/frontend/src/ts/commandline/lists.ts +++ b/frontend/src/ts/commandline/lists.ts @@ -59,6 +59,7 @@ import KeymapShowTopRowCommands from "./lists/keymap-show-top-row"; import EnableAdsCommands from "./lists/enable-ads"; import MonkeyPowerLevelCommands from "./lists/monkey-power-level"; import BailOutCommands from "./lists/bail-out"; +import QuoteFavoriteCommands from "./lists/quote-favorites"; import ResultSavingCommands from "./lists/result-saving"; import NavigationCommands from "./lists/navigation"; import FontSizeCommands from "./lists/font-size"; @@ -205,6 +206,7 @@ export const commands: MonkeyTypes.CommandsSubgroup = { }, shouldFocusTestUI: false, }, + ...QuoteFavoriteCommands, ...BailOutCommands, { id: "shareTestSettings", diff --git a/frontend/src/ts/commandline/lists/quote-favorites.ts b/frontend/src/ts/commandline/lists/quote-favorites.ts new file mode 100644 index 000000000..2dba0e635 --- /dev/null +++ b/frontend/src/ts/commandline/lists/quote-favorites.ts @@ -0,0 +1,75 @@ +import Config from "../../config"; +import QuotesController from "../../controllers/quotes-controller"; +import * as Notifications from "../../elements/notifications"; +import { isAuthenticated } from "../../firebase"; +import { createErrorMessage } from "../../utils/misc"; +import * as Loader from "../../elements/loader"; + +const commands: MonkeyTypes.Command[] = [ + { + id: "addQuoteToFavorite", + display: "Add current quote to favorite", + icon: "fa-heart", + available: (): boolean => { + const currentQuote = QuotesController.getCurrentQuote(); + return ( + isAuthenticated() && + currentQuote !== null && + Config.mode === "quote" && + !QuotesController.isQuoteFavorite(currentQuote) + ); + }, + exec: async (): Promise => { + try { + Loader.show(); + await QuotesController.setQuoteFavorite( + QuotesController.getCurrentQuote() as MonkeyTypes.Quote, + true + ); + Loader.hide(); + Notifications.add("Quote added to favorites", 1); + } catch (e) { + Loader.hide(); + const message = createErrorMessage( + e, + "Failed to add quote to favorites" + ); + Notifications.add(message, -1); + } + }, + }, + { + id: "removeQuoteFromFavorite", + display: "Remove current quote from favorite", + icon: "fa-heart-broken", + available: (): boolean => { + const currentQuote = QuotesController.getCurrentQuote(); + return ( + isAuthenticated() && + currentQuote !== null && + Config.mode === "quote" && + QuotesController.isQuoteFavorite(currentQuote) + ); + }, + exec: async (): Promise => { + try { + Loader.show(); + await QuotesController.setQuoteFavorite( + QuotesController.getCurrentQuote() as MonkeyTypes.Quote, + false + ); + Loader.hide(); + Notifications.add("Quote removed from favorites", 1); + } catch (e) { + Loader.hide(); + const message = createErrorMessage( + e, + "Failed to remove quote from favorites" + ); + Notifications.add(message, -1); + } + }, + }, +]; + +export default commands; diff --git a/frontend/src/ts/controllers/quotes-controller.ts b/frontend/src/ts/controllers/quotes-controller.ts index b6e175e9b..57bb6ee16 100644 --- a/frontend/src/ts/controllers/quotes-controller.ts +++ b/frontend/src/ts/controllers/quotes-controller.ts @@ -3,6 +3,7 @@ import { randomElementFromArray, shuffle } from "../utils/arrays"; import { cachedFetchJson } from "../utils/json-data"; import { subscribe } from "../observables/config-event"; import * as DB from "../db"; +import Ape from "../ape"; type JsonQuote = { text: string; @@ -212,6 +213,51 @@ class QuotesController { return matchedLanguage !== undefined; } + + async setQuoteFavorite( + quote: MonkeyTypes.Quote, + isFavorite: boolean + ): Promise { + const snapshot = DB.getSnapshot(); + if (!snapshot) { + throw new Error("Snapshot is not available"); + } + + if (!isFavorite) { + // Remove from favorites + const response = await Ape.users.removeQuoteFromFavorites( + quote.language, + `${quote.id}` + ); + + if (response.status === 200) { + const quoteIndex = snapshot.favoriteQuotes?.[quote.language]?.indexOf( + `${quote.id}` + ) as number; + snapshot.favoriteQuotes?.[quote.language]?.splice(quoteIndex, 1); + } else { + throw new Error(response.message); + } + } else { + // Remove from favorites + const response = await Ape.users.addQuoteToFavorites( + quote.language, + `${quote.id}` + ); + + if (response.status === 200) { + if (snapshot.favoriteQuotes === undefined) { + snapshot.favoriteQuotes = {}; + } + if (!snapshot.favoriteQuotes[quote.language]) { + snapshot.favoriteQuotes[quote.language] = []; + } + snapshot.favoriteQuotes[quote.language]?.push(`${quote.id}`); + } else { + throw new Error(response.message); + } + } + } } const quoteController = new QuotesController(); diff --git a/frontend/src/ts/modals/quote-search.ts b/frontend/src/ts/modals/quote-search.ts index 2b5f617c5..4fdaf2e95 100644 --- a/frontend/src/ts/modals/quote-search.ts +++ b/frontend/src/ts/modals/quote-search.ts @@ -20,6 +20,7 @@ import SlimSelect from "slim-select"; import * as TestState from "../test/test-state"; import AnimatedModal, { ShowOptions } from "../utils/animated-modal"; import * as TestLogic from "../test/test-logic"; +import { createErrorMessage } from "../utils/misc"; const searchServiceCache: Record> = {}; @@ -352,47 +353,43 @@ async function toggleFavoriteForQuote(quoteId: string): Promise { return; } + const quote = { + language: quoteLang, + id: parseInt(quoteId, 10), + } as MonkeyTypes.Quote; + + const alreadyFavorited = QuotesController.isQuoteFavorite(quote); + const $button = $( `#quoteSearchModal .searchResult[data-quote-id=${quoteId}] .textButton.favorite i` ); const dbSnapshot = DB.getSnapshot(); if (!dbSnapshot) return; - if ($button.hasClass("fas")) { - // Remove from favorites - Loader.show(); - const response = await Ape.users.removeQuoteFromFavorites( - quoteLang, - quoteId - ); - Loader.hide(); - - Notifications.add(response.message, response.status === 200 ? 1 : -1); - - if (response.status === 200) { + if (alreadyFavorited) { + try { + Loader.show(); + await QuotesController.setQuoteFavorite(quote, false); + Loader.hide(); $button.removeClass("fas").addClass("far"); - const quoteIndex = dbSnapshot.favoriteQuotes?.[quoteLang]?.indexOf( - quoteId - ) as number; - dbSnapshot.favoriteQuotes?.[quoteLang]?.splice(quoteIndex, 1); + } catch (e) { + Loader.hide(); + const message = createErrorMessage( + e, + "Failed to remove quote from favorites" + ); + Notifications.add(message, -1); } } else { - // Add to favorites - Loader.show(); - const response = await Ape.users.addQuoteToFavorites(quoteLang, quoteId); - Loader.hide(); - - Notifications.add(response.message, response.status === 200 ? 1 : -1); - - if (response.status === 200) { + try { + Loader.show(); + await QuotesController.setQuoteFavorite(quote, true); + Loader.hide(); $button.removeClass("far").addClass("fas"); - if (dbSnapshot.favoriteQuotes === undefined) { - dbSnapshot.favoriteQuotes = {}; - } - if (!dbSnapshot.favoriteQuotes[quoteLang]) { - dbSnapshot.favoriteQuotes[quoteLang] = []; - } - dbSnapshot.favoriteQuotes[quoteLang]?.push(quoteId); + } catch (e) { + Loader.hide(); + const message = createErrorMessage(e, "Failed to add quote to favorites"); + Notifications.add(message, -1); } } }