mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-10-29 02:07:55 +08:00
Add favorite quotes mode (#2945) bruception
* Add favorite quotes mode * Remove unnecessary repeated fn call * resetting quote length to -1 when no favorites found * fixed quote favoriting broken * starting hidden * showing/hiding button depending on login state * added command to commandline * removed comment Co-authored-by: Miodec <bartnikjack@gmail.com>
This commit is contained in:
parent
c9bfb00008
commit
9026893571
12 changed files with 123 additions and 44 deletions
|
|
@ -1000,7 +1000,7 @@ export function setQuoteLength(
|
|||
): boolean {
|
||||
if (
|
||||
!isConfigValueValid("quote length", len, [
|
||||
[-2, -1, 0, 1, 2, 3],
|
||||
[-3, -2, -1, 0, 1, 2, 3],
|
||||
"numberArray",
|
||||
])
|
||||
) {
|
||||
|
|
@ -1013,7 +1013,7 @@ export function setQuoteLength(
|
|||
config.quoteLength = len;
|
||||
} else {
|
||||
if (!Array.isArray(config.quoteLength)) config.quoteLength = [];
|
||||
if (len === null || isNaN(len) || len < -2 || len > 3) {
|
||||
if (len === null || isNaN(len) || len < -3 || len > 3) {
|
||||
len = 1;
|
||||
}
|
||||
len = parseInt(len.toString()) as MonkeyTypes.QuoteLength;
|
||||
|
|
|
|||
|
|
@ -47,6 +47,10 @@ import { Auth } from "../firebase";
|
|||
import differenceInDays from "date-fns/differenceInDays";
|
||||
import { defaultSnap } from "../constants/default-snapshot";
|
||||
import { dispatch as dispatchSignUpEvent } from "../observables/google-sign-up-event";
|
||||
import {
|
||||
hideFavoriteQuoteLength,
|
||||
showFavoriteQuoteLength,
|
||||
} from "../test/test-config";
|
||||
|
||||
export const gmailProvider = new GoogleAuthProvider();
|
||||
|
||||
|
|
@ -107,6 +111,7 @@ export async function getDataAndInit(): Promise<boolean> {
|
|||
LoadingPage.updateText("Applying settings...");
|
||||
const snapshot = DB.getSnapshot();
|
||||
$("#menu .text-button.account .text").text(snapshot.name);
|
||||
showFavoriteQuoteLength();
|
||||
|
||||
ResultFilters.loadTags(snapshot.tags);
|
||||
|
||||
|
|
@ -491,6 +496,7 @@ export function signOut(): void {
|
|||
DB.setSnapshot(defaultSnap);
|
||||
$(".pageLogin .button").removeClass("disabled");
|
||||
$(".pageLogin input").prop("disabled", false);
|
||||
hideFavoriteQuoteLength();
|
||||
})
|
||||
.catch(function (error) {
|
||||
Notifications.add(error.message, -1);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { shuffle } from "../utils/misc";
|
||||
import { randomElementFromArray, shuffle } from "../utils/misc";
|
||||
import { subscribe } from "../observables/config-event";
|
||||
import * as DB from "../db";
|
||||
|
||||
interface Quote {
|
||||
text: string;
|
||||
|
|
@ -28,6 +29,10 @@ const defaultQuoteCollection: QuoteCollection = {
|
|||
groups: [],
|
||||
};
|
||||
|
||||
function normalizeLanguage(language: string): string {
|
||||
return language.replace(/_\d*k$/g, "");
|
||||
}
|
||||
|
||||
class QuotesController {
|
||||
private quoteCollection: QuoteCollection = defaultQuoteCollection;
|
||||
|
||||
|
|
@ -38,7 +43,7 @@ class QuotesController {
|
|||
language: string,
|
||||
quoteLengths?: number[]
|
||||
): Promise<QuoteCollection> {
|
||||
const normalizedLanguage = language.replace(/_\d*k$/g, "");
|
||||
const normalizedLanguage = normalizeLanguage(language);
|
||||
|
||||
if (this.quoteCollection.language !== normalizedLanguage) {
|
||||
try {
|
||||
|
|
@ -146,6 +151,54 @@ class QuotesController {
|
|||
|
||||
return this.quoteQueue[this.queueIndex];
|
||||
}
|
||||
|
||||
getRandomFavoriteQuote(language: string): MonkeyTypes.Quote | null {
|
||||
const snapshot = DB.getSnapshot();
|
||||
if (!snapshot) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const normalizedLanguage = normalizeLanguage(language);
|
||||
const quoteIds: string[] = [];
|
||||
const { favoriteQuotes } = snapshot;
|
||||
|
||||
Object.keys(favoriteQuotes).forEach((language) => {
|
||||
if (normalizeLanguage(language) !== normalizedLanguage) {
|
||||
return;
|
||||
}
|
||||
|
||||
quoteIds.push(...favoriteQuotes[language]);
|
||||
});
|
||||
|
||||
if (quoteIds.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const randomQuoteId = randomElementFromArray(quoteIds);
|
||||
const randomQuote = this.getQuoteById(parseInt(randomQuoteId, 10));
|
||||
|
||||
return randomQuote ?? null;
|
||||
}
|
||||
|
||||
isQuoteFavorite({ language: quoteLanguage, id }: MonkeyTypes.Quote): boolean {
|
||||
const snapshot = DB.getSnapshot();
|
||||
if (!snapshot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { favoriteQuotes } = snapshot;
|
||||
|
||||
const normalizedQuoteLanguage = normalizeLanguage(quoteLanguage);
|
||||
|
||||
const matchedLanguage = Object.keys(favoriteQuotes).find((language) => {
|
||||
if (normalizedQuoteLanguage !== normalizeLanguage(language)) {
|
||||
return false;
|
||||
}
|
||||
return favoriteQuotes[language].includes(id.toString());
|
||||
});
|
||||
|
||||
return matchedLanguage !== undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const quoteController = new QuotesController();
|
||||
|
|
|
|||
|
|
@ -2059,6 +2059,20 @@ const commandsQuoteLengthConfig: MonkeyTypes.CommandsGroup = {
|
|||
TestLogic.restart();
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "changeQuoteLengthFavorite",
|
||||
display: "favorite",
|
||||
configValue: -3,
|
||||
configValueMode: "include",
|
||||
available: (): boolean => {
|
||||
return !!Auth.currentUser;
|
||||
},
|
||||
exec: (): void => {
|
||||
UpdateConfig.setMode("quote");
|
||||
UpdateConfig.setQuoteLength(-3);
|
||||
TestLogic.restart();
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import QuotesController from "../controllers/quotes-controller";
|
|||
import { Auth } from "../firebase";
|
||||
import { debounce } from "throttle-debounce";
|
||||
import Ape from "../ape";
|
||||
import { isQuoteFavorite } from "../utils/misc";
|
||||
import * as Loader from "../elements/loader";
|
||||
|
||||
export let selectedId = 1;
|
||||
|
|
@ -85,14 +84,10 @@ function applyQuoteFavFilter(quotes: MonkeyTypes.Quote[]): MonkeyTypes.Quote[] {
|
|||
|
||||
const filteredQuotes = quotes.filter((quote) => {
|
||||
if (showFavOnly) {
|
||||
return isQuoteFavorite(
|
||||
DB.getSnapshot(),
|
||||
quote.language,
|
||||
quote.id.toString()
|
||||
);
|
||||
} else {
|
||||
return true;
|
||||
return QuotesController.isQuoteFavorite(quote);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return filteredQuotes;
|
||||
|
|
@ -113,10 +108,8 @@ function buildQuoteSearchResult(
|
|||
lengthDesc = "thicc";
|
||||
}
|
||||
|
||||
const loggedIn = !Auth.currentUser;
|
||||
const isFav =
|
||||
!loggedIn &&
|
||||
isQuoteFavorite(DB.getSnapshot(), quote.language, quote.id.toString());
|
||||
const loggedOut = !Auth.currentUser;
|
||||
const isFav = !loggedOut && QuotesController.isQuoteFavorite(quote);
|
||||
|
||||
return `
|
||||
<div class="searchResult" id="${quote.id}">
|
||||
|
|
@ -143,13 +136,13 @@ function buildQuoteSearchResult(
|
|||
</div>
|
||||
|
||||
<div class="text-button report ${
|
||||
loggedIn && "hidden"
|
||||
loggedOut && "hidden"
|
||||
}" aria-label="Report quote" data-balloon-pos="left">
|
||||
<i class="fas fa-flag report"></i>
|
||||
</div>
|
||||
|
||||
<div class="text-button favorite ${
|
||||
loggedIn && "hidden"
|
||||
loggedOut && "hidden"
|
||||
}" aria-label="Favorite quote" data-balloon-pos="left">
|
||||
<i class="${isFav ? "fas" : "far"} fa-heart favorite"></i>
|
||||
</div>
|
||||
|
|
@ -171,10 +164,9 @@ async function updateResults(searchText: string): Promise<void> {
|
|||
const { results: matches, matchedQueryTerms } =
|
||||
quoteSearchService.query(searchText);
|
||||
|
||||
let quotesToShow = [];
|
||||
quotesToShow = applyQuoteLengthFilter(searchText === "" ? quotes : matches);
|
||||
|
||||
quotesToShow = applyQuoteFavFilter(quotesToShow);
|
||||
const quotesToShow = applyQuoteLengthFilter(
|
||||
applyQuoteFavFilter(searchText === "" ? quotes : matches)
|
||||
);
|
||||
|
||||
const resultsList = $("#quoteSearchResults");
|
||||
resultsList.empty();
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import * as GlarsesMode from "../states/glarses-mode";
|
|||
import * as TestInput from "./test-input";
|
||||
import * as Notifications from "../elements/notifications";
|
||||
import * as Loader from "../elements/loader";
|
||||
import QuotesController from "../controllers/quotes-controller";
|
||||
import { Chart } from "chart.js";
|
||||
import { Auth } from "../firebase";
|
||||
|
||||
|
|
@ -581,17 +582,15 @@ function updateQuoteFavorite(randomQuote: MonkeyTypes.Quote): void {
|
|||
quoteLang = Config.mode === "quote" ? randomQuote.language : "";
|
||||
quoteId = Config.mode === "quote" ? randomQuote.id.toString() : "";
|
||||
|
||||
const $icon = $(".pageTest #result #favoriteQuoteButton .icon");
|
||||
const icon = $(".pageTest #result #favoriteQuoteButton .icon");
|
||||
|
||||
if (Config.mode === "quote" && Auth.currentUser) {
|
||||
const userFav = Misc.isQuoteFavorite(DB.getSnapshot(), quoteLang, quoteId);
|
||||
const userFav = QuotesController.isQuoteFavorite(randomQuote);
|
||||
|
||||
$icon
|
||||
.removeClass(userFav ? "far" : "fas")
|
||||
.addClass(userFav ? "fas" : "far");
|
||||
$icon.parent().removeClass("hidden");
|
||||
icon.removeClass(userFav ? "far" : "fas").addClass(userFav ? "fas" : "far");
|
||||
icon.parent().removeClass("hidden");
|
||||
} else {
|
||||
$icon.parent().addClass("hidden");
|
||||
icon.parent().addClass("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -160,6 +160,14 @@ export function update(
|
|||
);
|
||||
}
|
||||
|
||||
export function showFavoriteQuoteLength(): void {
|
||||
$("#top .desktopConfig .group.quoteLength .favorite").removeClass("hidden");
|
||||
}
|
||||
|
||||
export function hideFavoriteQuoteLength(): void {
|
||||
$("#top .desktopConfig .group.quoteLength .favorite").addClass("hidden");
|
||||
}
|
||||
|
||||
ConfigEvent.subscribe((eventKey, eventValue, _nosave, eventPreviousValue) => {
|
||||
if (eventKey === "mode") {
|
||||
update(
|
||||
|
|
|
|||
|
|
@ -932,7 +932,7 @@ export async function init(): Promise<void> {
|
|||
}
|
||||
|
||||
let rq: MonkeyTypes.Quote | undefined = undefined;
|
||||
if (Config.quoteLength.includes(-2) && Config.quoteLength.length == 1) {
|
||||
if (Config.quoteLength.includes(-2) && Config.quoteLength.length === 1) {
|
||||
const targetQuote = QuotesController.getQuoteById(
|
||||
QuoteSearchPopup.selectedId
|
||||
);
|
||||
|
|
@ -942,6 +942,19 @@ export async function init(): Promise<void> {
|
|||
} else {
|
||||
rq = targetQuote;
|
||||
}
|
||||
} else if (Config.quoteLength.includes(-3)) {
|
||||
const randomQuote = QuotesController.getRandomFavoriteQuote(
|
||||
Config.language
|
||||
);
|
||||
|
||||
if (randomQuote === null) {
|
||||
Notifications.add("No favorite quotes found", 0);
|
||||
UpdateConfig.setQuoteLength(-1);
|
||||
restart();
|
||||
return;
|
||||
}
|
||||
|
||||
rq = randomQuote;
|
||||
} else {
|
||||
const randomQuote = QuotesController.getRandomQuote();
|
||||
if (randomQuote === null) {
|
||||
|
|
|
|||
2
frontend/src/ts/types/types.d.ts
vendored
2
frontend/src/ts/types/types.d.ts
vendored
|
|
@ -33,7 +33,7 @@ declare namespace MonkeyTypes {
|
|||
|
||||
type QuoteModes = "short" | "medium" | "long" | "thicc";
|
||||
|
||||
type QuoteLength = -2 | -1 | 0 | 1 | 2 | 3;
|
||||
type QuoteLength = -3 | -2 | -1 | 0 | 1 | 2 | 3;
|
||||
|
||||
type FontSize = "1" | "125" | "15" | "2" | "3" | "4";
|
||||
|
||||
|
|
|
|||
|
|
@ -1111,18 +1111,6 @@ export function createErrorMessage(error: unknown, message: string): string {
|
|||
return message;
|
||||
}
|
||||
|
||||
export function isQuoteFavorite(
|
||||
snapshot: MonkeyTypes.Snapshot,
|
||||
quoteLang: string,
|
||||
quoteId: string
|
||||
): boolean {
|
||||
if (!snapshot) return false;
|
||||
|
||||
if (!Object.keys(snapshot.favoriteQuotes).includes(quoteLang)) return false;
|
||||
|
||||
return snapshot.favoriteQuotes[quoteLang].includes(quoteId);
|
||||
}
|
||||
|
||||
export function isAnyPopupVisible(): boolean {
|
||||
const popups = document.querySelectorAll(".popupWrapper");
|
||||
let popupVisible = false;
|
||||
|
|
|
|||
|
|
@ -155,7 +155,6 @@
|
|||
style="display: inline-block"
|
||||
>
|
||||
<i class="icon far fa-heart"></i>
|
||||
<span class="favorite"></span>
|
||||
</span>
|
||||
<span
|
||||
id="rateQuoteButton"
|
||||
|
|
|
|||
|
|
@ -187,6 +187,13 @@
|
|||
</div>
|
||||
<div class="text-button" quoteLength="2" tabindex="2">long</div>
|
||||
<div class="text-button" quoteLength="3" tabindex="2">thicc</div>
|
||||
<div
|
||||
class="text-button hidden favorite"
|
||||
quoteLength="-3"
|
||||
tabindex="2"
|
||||
>
|
||||
<i class="fas fa-heart"></i>
|
||||
</div>
|
||||
<div class="text-button" quoteLength="-2" tabindex="2">
|
||||
<i class="fas fa-search"></i>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue