mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-02-24 23:04:00 +08:00
Long Custom Text (#3543) rizwanmustafa miodec
* Create state for storing custom text name * Save custom text name on click * Add todo * add some utility functions to custom-text.ts * Set custom text name to empty upon modification * now update the custom text progress in localStorage * rework logic for updating progress in test-logic.ts * more logic changes * Keep progress in mind when starting next test after bailout * reset test once they complete it and minor refactor * Now set custom text progress to 0 when it is modified * Add UI for continuing and change var name * Reset progress if they start it again * Move functions * remove debug log * replaced simple popup with custom popup * fixed media query * also setting opacity to 1 * saving long custom text into a separate object * fixed incorrect saving function fixed get custom text names function * setting to empty object structure first * long list style fix * showing long texts handling delete and progress reset * renamed file tracking if custom text is long * unnecessary comment * showing a warning that editing will disable progress tracking * checking if text is long updating progress * added notifications * setting custom text * showing if progress tracking is working * showing if progress tracking was disabled * longer notification * corrected button text * joining with space * checking if name is taken added indicator Co-authored-by: Miodec <jack@monkeytype.com>
This commit is contained in:
parent
d2595bd1b1
commit
9bb778673a
11 changed files with 476 additions and 55 deletions
|
@ -21,6 +21,25 @@
|
|||
gap: 1rem;
|
||||
width: 60vw;
|
||||
|
||||
.longCustomTextWarning {
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
display: grid;
|
||||
place-items: center center;
|
||||
border-radius: var(--roundness);
|
||||
.textAndButton {
|
||||
width: 80%;
|
||||
max-width: 20rem;
|
||||
text-align: center;
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.buttonsTop {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
|
@ -99,12 +118,44 @@
|
|||
.savedText {
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
grid-template-columns: 1fr 3rem;
|
||||
grid-template-columns: 2fr 3rem;
|
||||
.button .fas {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.listLong {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
.savedText {
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
grid-template-columns: 2fr 1fr 3rem;
|
||||
.button .fas {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#saveCustomTextPopupWrapper {
|
||||
#saveCustomTextPopup {
|
||||
color: var(--sub-color);
|
||||
background: var(--bg-color);
|
||||
border-radius: var(--roundness);
|
||||
padding: 2rem;
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
width: 400px;
|
||||
max-height: 80vh;
|
||||
overflow: auto;
|
||||
|
||||
.title {
|
||||
font-size: 1.5rem;
|
||||
color: var(--sub-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,10 +85,6 @@
|
|||
}
|
||||
#customTextPopup {
|
||||
width: 80vw !important;
|
||||
|
||||
.wordfilter.button {
|
||||
width: 50% !important;
|
||||
}
|
||||
}
|
||||
#leaderboardsWrapper {
|
||||
#leaderboards {
|
||||
|
|
|
@ -6,6 +6,7 @@ import Config from "../config";
|
|||
import * as TestWords from "../test/test-words";
|
||||
import * as ConfigEvent from "../observables/config-event";
|
||||
import { Auth } from "../firebase";
|
||||
import * as CustomTextState from "../states/custom-text-name";
|
||||
|
||||
ConfigEvent.subscribe((eventKey) => {
|
||||
if (
|
||||
|
@ -57,6 +58,14 @@ export async function update(): Promise<void> {
|
|||
}
|
||||
}
|
||||
|
||||
const customTextName = CustomTextState.getCustomTextName();
|
||||
const isLong = CustomTextState.isCustomTextLong();
|
||||
if (Config.mode === "custom" && customTextName !== "" && isLong) {
|
||||
$(".pageTest #testModesNotice").append(
|
||||
`<div class="textButton noInteraction"><i class="fas fa-book"></i>tracking progress for ${customTextName}</div>`
|
||||
);
|
||||
}
|
||||
|
||||
if (TestState.activeChallenge) {
|
||||
$(".pageTest #testModesNotice").append(
|
||||
`<div class="textButton noInteraction"><i class="fas fa-award"></i>${TestState.activeChallenge.display}</div>`
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import * as CustomText from "../test/custom-text";
|
||||
import * as CustomTextState from "../states/custom-text-name";
|
||||
import * as ManualRestart from "../test/manual-restart-tracker";
|
||||
import * as TestLogic from "../test/test-logic";
|
||||
import * as ChallengeController from "../controllers/challenge-controller";
|
||||
|
@ -7,12 +8,22 @@ import * as Misc from "../utils/misc";
|
|||
import * as WordFilterPopup from "./word-filter-popup";
|
||||
import * as Notifications from "../elements/notifications";
|
||||
import * as SavedTextsPopup from "./saved-texts-popup";
|
||||
import * as SaveCustomTextPopup from "./save-custom-text-popup";
|
||||
|
||||
const wrapper = "#customTextPopupWrapper";
|
||||
const popup = "#customTextPopup";
|
||||
|
||||
function updateLongTextWarning(): void {
|
||||
if (CustomTextState.isCustomTextLong() === true) {
|
||||
$(`${popup} .longCustomTextWarning`).removeClass("hidden");
|
||||
} else {
|
||||
$(`${popup} .longCustomTextWarning`).addClass("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
export function show(): void {
|
||||
if ($(wrapper).hasClass("hidden")) {
|
||||
updateLongTextWarning();
|
||||
if ($(`${popup} .checkbox input`).prop("checked")) {
|
||||
$(`${popup} .inputs .randomInputFields`).removeClass("hidden");
|
||||
} else {
|
||||
|
@ -36,7 +47,9 @@ export function show(): void {
|
|||
});
|
||||
}
|
||||
setTimeout(() => {
|
||||
$(`${popup} textarea`).trigger("focus");
|
||||
if (!CustomTextState.isCustomTextLong()) {
|
||||
$(`${popup} textarea`).trigger("focus");
|
||||
}
|
||||
}, 150);
|
||||
}
|
||||
|
||||
|
@ -99,9 +112,20 @@ $(`${popup} .inputs .checkbox input`).on("change", () => {
|
|||
});
|
||||
|
||||
$(`${popup} textarea`).on("keypress", (e) => {
|
||||
if (!$(`${popup} .longCustomTextWarning`).hasClass("hidden")) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
if (e.code === "Enter" && e.ctrlKey) {
|
||||
$(`${popup} .button.apply`).trigger("click");
|
||||
}
|
||||
if (
|
||||
CustomTextState.isCustomTextLong() &&
|
||||
CustomTextState.getCustomTextName() !== ""
|
||||
) {
|
||||
CustomTextState.setCustomTextName("", undefined);
|
||||
Notifications.add("Disabled long custom text progress tracking", 0, 5);
|
||||
}
|
||||
});
|
||||
|
||||
$(`${popup} .randomInputFields .wordcount input`).on("keypress", () => {
|
||||
|
@ -216,3 +240,11 @@ $(document).on("keydown", (event) => {
|
|||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
$(`#customTextPopup .buttonsTop .saveCustomText`).on("click", () => {
|
||||
SaveCustomTextPopup.show();
|
||||
});
|
||||
|
||||
$(`#customTextPopup .longCustomTextWarning .button`).on("click", () => {
|
||||
$(`#customTextPopup .longCustomTextWarning`).addClass("hidden");
|
||||
});
|
||||
|
|
99
frontend/src/ts/popups/save-custom-text-popup.ts
Normal file
99
frontend/src/ts/popups/save-custom-text-popup.ts
Normal file
|
@ -0,0 +1,99 @@
|
|||
import * as CustomText from "../test/custom-text";
|
||||
import * as Notifications from "../elements/notifications";
|
||||
import * as CustomTextState from "../states/custom-text-name";
|
||||
import { InputIndicator } from "../elements/input-indicator";
|
||||
import { debounce } from "throttle-debounce";
|
||||
|
||||
const indicator = new InputIndicator($("#saveCustomTextPopup .textName"), {
|
||||
available: {
|
||||
icon: "fa-check",
|
||||
level: 1,
|
||||
},
|
||||
unavailable: {
|
||||
icon: "fa-times",
|
||||
level: -1,
|
||||
},
|
||||
loading: {
|
||||
icon: "fa-circle-notch",
|
||||
spinIcon: true,
|
||||
level: 0,
|
||||
},
|
||||
});
|
||||
|
||||
export async function show(): Promise<void> {
|
||||
$("#saveCustomTextPopupWrapper").removeClass("hidden");
|
||||
$("#customTextPopupWrapper").addClass("hidden");
|
||||
$("#saveCustomTextPopupWrapper .textName").val("");
|
||||
$("#saveCustomTextPopupWrapper .isLongText").prop("checked", false);
|
||||
$("#saveCustomTextPopupWrapper .button.save").addClass("disabled");
|
||||
}
|
||||
|
||||
function hide(full = false): void {
|
||||
$("#saveCustomTextPopupWrapper").addClass("hidden");
|
||||
if (!full) {
|
||||
$("#customTextPopupWrapper").removeClass("hidden").css("opacity", 1);
|
||||
}
|
||||
}
|
||||
|
||||
function save(): boolean {
|
||||
const name = $("#saveCustomTextPopupWrapper .textName").val() as string;
|
||||
const text = ($(`#customTextPopup textarea`).val() as string).normalize();
|
||||
const checkbox = $("#saveCustomTextPopupWrapper .isLongText").prop("checked");
|
||||
|
||||
if (!name) {
|
||||
Notifications.add("Custom text needs a name", 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
CustomText.setCustomText(name, text, checkbox);
|
||||
CustomTextState.setCustomTextName(name, checkbox);
|
||||
Notifications.add("Custom text saved", 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
$(document).on("click", `#saveCustomTextPopupWrapper .button.save`, () => {
|
||||
if (save() === true) hide();
|
||||
});
|
||||
|
||||
$("#saveCustomTextPopupWrapper").on("mousedown", (e) => {
|
||||
if ($(e.target).attr("id") === "saveCustomTextPopupWrapper") {
|
||||
hide();
|
||||
}
|
||||
});
|
||||
|
||||
function updateIndicatorAndButton(): void {
|
||||
const val = $("#saveCustomTextPopup .textName").val() as string;
|
||||
const checkbox = $("#saveCustomTextPopupWrapper .isLongText").prop("checked");
|
||||
|
||||
if (!val) {
|
||||
indicator.hide();
|
||||
$("#saveCustomTextPopupWrapper .button.save").addClass("disabled");
|
||||
} else {
|
||||
const names = CustomText.getCustomTextNames(checkbox);
|
||||
if (names.includes(val)) {
|
||||
indicator.show("unavailable");
|
||||
$("#saveCustomTextPopupWrapper .button.save").addClass("disabled");
|
||||
} else {
|
||||
indicator.show("available");
|
||||
$("#saveCustomTextPopupWrapper .button.save").removeClass("disabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const updateInputAndButtonDebounced = debounce(500, updateIndicatorAndButton);
|
||||
|
||||
$("#saveCustomTextPopup .textName").on("input", () => {
|
||||
const val = $("#saveCustomTextPopup .textName").val() as string;
|
||||
if (val.length > 0) {
|
||||
indicator.show("loading");
|
||||
updateInputAndButtonDebounced();
|
||||
}
|
||||
});
|
||||
|
||||
$("#saveCustomTextPopupWrapper .isLongText").on("change", () => {
|
||||
const val = $("#saveCustomTextPopup .textName").val() as string;
|
||||
if (val.length > 0) {
|
||||
indicator.show("loading");
|
||||
updateInputAndButtonDebounced();
|
||||
}
|
||||
});
|
|
@ -1,4 +1,5 @@
|
|||
import * as CustomText from "../test/custom-text";
|
||||
import * as CustomTextState from "../states/custom-text-name";
|
||||
|
||||
export async function show(): Promise<void> {
|
||||
const names = CustomText.getCustomTextNames();
|
||||
|
@ -17,17 +18,48 @@ export async function show(): Promise<void> {
|
|||
}
|
||||
}
|
||||
listEl.html(list);
|
||||
|
||||
const longNames = CustomText.getCustomTextNames(true);
|
||||
const longListEl = $(`#savedTextsPopup .listLong`).empty();
|
||||
let longList = "";
|
||||
if (longNames.length === 0) {
|
||||
longList += "<div>No saved long custom texts found</div>";
|
||||
} else {
|
||||
for (const name of longNames) {
|
||||
longList += `<div class="savedText">
|
||||
<div class="button name">${name}</div>
|
||||
<div class="button ${
|
||||
CustomText.getCustomTextLongProgress(name) <= 0 ? "disabled" : ""
|
||||
} resetProgress">reset</div>
|
||||
<div class="button delete">
|
||||
<i class="fas fa-fw fa-trash"></i>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
longListEl.html(longList);
|
||||
|
||||
$("#savedTextsPopupWrapper").removeClass("hidden");
|
||||
$("#customTextPopupWrapper").addClass("hidden");
|
||||
}
|
||||
|
||||
function hide(full = false): void {
|
||||
$("#savedTextsPopupWrapper").addClass("hidden");
|
||||
if (!full) $("#customTextPopupWrapper").removeClass("hidden");
|
||||
if (!full) {
|
||||
if (CustomTextState.isCustomTextLong() === true) {
|
||||
$(`#customTextPopup .longCustomTextWarning`).removeClass("hidden");
|
||||
} else {
|
||||
$(`#customTextPopup .longCustomTextWarning`).addClass("hidden");
|
||||
}
|
||||
$("#customTextPopupWrapper").removeClass("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function applySaved(name: string): void {
|
||||
const text = CustomText.getCustomText(name);
|
||||
function applySaved(name: string, long: boolean): void {
|
||||
let text = CustomText.getCustomText(name, long);
|
||||
if (long) {
|
||||
text = text.slice(CustomText.getCustomTextLongProgress(name));
|
||||
}
|
||||
$(`#customTextPopupWrapper textarea`).val(text.join(CustomText.delimiter));
|
||||
}
|
||||
|
||||
|
@ -36,7 +68,8 @@ $(document).on(
|
|||
`#savedTextsPopupWrapper .list .savedText .button.name`,
|
||||
(e) => {
|
||||
const name = $(e.target).text();
|
||||
applySaved(name);
|
||||
CustomTextState.setCustomTextName(name, false);
|
||||
applySaved(name, false);
|
||||
hide();
|
||||
}
|
||||
);
|
||||
|
@ -49,6 +82,33 @@ $(document).on(
|
|||
}
|
||||
);
|
||||
|
||||
$(document).on(
|
||||
"click",
|
||||
`#savedTextsPopupWrapper .listLong .savedText .button.name`,
|
||||
(e) => {
|
||||
const name = $(e.target).text();
|
||||
CustomTextState.setCustomTextName(name, true);
|
||||
applySaved(name, true);
|
||||
hide();
|
||||
}
|
||||
);
|
||||
|
||||
$(document).on(
|
||||
"click",
|
||||
`#savedTextsPopupWrapper .listLong .savedText .button.resetProgress`,
|
||||
() => {
|
||||
hide(true);
|
||||
}
|
||||
);
|
||||
|
||||
$(document).on(
|
||||
"click",
|
||||
`#savedTextsPopupWrapper .listLong .savedText .button.delete`,
|
||||
() => {
|
||||
hide(true);
|
||||
}
|
||||
);
|
||||
|
||||
$("#savedTextsPopupWrapper").on("mousedown", (e) => {
|
||||
if ($(e.target).attr("id") === "savedTextsPopupWrapper") {
|
||||
hide();
|
||||
|
|
|
@ -8,7 +8,6 @@ import * as Settings from "../pages/settings";
|
|||
import * as ApeKeysPopup from "../popups/ape-keys-popup";
|
||||
import * as ThemePicker from "../settings/theme-picker";
|
||||
import * as CustomText from "../test/custom-text";
|
||||
import * as CustomTextPopup from "../popups/custom-text-popup";
|
||||
import * as SavedTextsPopup from "./saved-texts-popup";
|
||||
import * as AccountButton from "../elements/account-button";
|
||||
import { FirebaseError } from "firebase/app";
|
||||
|
@ -1076,32 +1075,6 @@ list["editApeKey"] = new SimplePopup(
|
|||
}
|
||||
);
|
||||
|
||||
list["saveCustomText"] = new SimplePopup(
|
||||
"saveCustomText",
|
||||
"text",
|
||||
"Save custom text",
|
||||
[
|
||||
{
|
||||
placeholder: "Name",
|
||||
initVal: "",
|
||||
},
|
||||
],
|
||||
"",
|
||||
"Save",
|
||||
(_thisPopup, input) => {
|
||||
const text = ($(`#customTextPopup textarea`).val() as string).normalize();
|
||||
CustomText.setCustomText(input, text);
|
||||
Notifications.add("Custom text saved", 1);
|
||||
CustomTextPopup.show();
|
||||
},
|
||||
() => {
|
||||
//
|
||||
},
|
||||
() => {
|
||||
//
|
||||
}
|
||||
);
|
||||
|
||||
list["deleteCustomText"] = new SimplePopup(
|
||||
"deleteCustomText",
|
||||
"text",
|
||||
|
@ -1122,6 +1095,49 @@ list["deleteCustomText"] = new SimplePopup(
|
|||
}
|
||||
);
|
||||
|
||||
list["deleteCustomTextLong"] = new SimplePopup(
|
||||
"deleteCustomTextLong",
|
||||
"text",
|
||||
"Delete custom text",
|
||||
[],
|
||||
"Are you sure?",
|
||||
"Delete",
|
||||
(_thisPopup) => {
|
||||
CustomText.deleteCustomText(_thisPopup.parameters[0], true);
|
||||
Notifications.add("Custom text deleted", 1);
|
||||
SavedTextsPopup.show();
|
||||
},
|
||||
(_thisPopup) => {
|
||||
_thisPopup.text = `Are you sure you want to delete custom text ${_thisPopup.parameters[0]}?`;
|
||||
},
|
||||
() => {
|
||||
//
|
||||
}
|
||||
);
|
||||
|
||||
list["resetProgressCustomTextLong"] = new SimplePopup(
|
||||
"resetProgressCustomTextLong",
|
||||
"text",
|
||||
"Reset progress for custom text",
|
||||
[],
|
||||
"Are you sure?",
|
||||
"Reset",
|
||||
(_thisPopup) => {
|
||||
CustomText.setCustomTextLongProgress(_thisPopup.parameters[0], 0);
|
||||
Notifications.add("Custom text progress reset", 1);
|
||||
SavedTextsPopup.show();
|
||||
$(`#customTextPopupWrapper textarea`).val(
|
||||
CustomText.getCustomText(_thisPopup.parameters[0], true).join(" ")
|
||||
);
|
||||
},
|
||||
(_thisPopup) => {
|
||||
_thisPopup.text = `Are you sure you want to reset your progress for custom text ${_thisPopup.parameters[0]}?`;
|
||||
},
|
||||
() => {
|
||||
//
|
||||
}
|
||||
);
|
||||
|
||||
list["updateCustomTheme"] = new SimplePopup(
|
||||
"updateCustomTheme",
|
||||
"text",
|
||||
|
@ -1262,10 +1278,6 @@ $("#apeKeysPopup .generateApeKey").on("click", () => {
|
|||
list["generateApeKey"].show();
|
||||
});
|
||||
|
||||
$(`#customTextPopup .buttonsTop .saveCustomText`).on("click", () => {
|
||||
list["saveCustomText"].show();
|
||||
});
|
||||
|
||||
$(document).on(
|
||||
"click",
|
||||
".pageSettings .section.themes .customTheme .delButton",
|
||||
|
@ -1295,6 +1307,24 @@ $(document).on(
|
|||
}
|
||||
);
|
||||
|
||||
$(document).on(
|
||||
"click",
|
||||
`#savedTextsPopupWrapper .listLong .savedText .button.delete`,
|
||||
(e) => {
|
||||
const name = $(e.target).siblings(".button.name").text();
|
||||
list["deleteCustomTextLong"].show([name]);
|
||||
}
|
||||
);
|
||||
|
||||
$(document).on(
|
||||
"click",
|
||||
`#savedTextsPopupWrapper .listLong .savedText .button.resetProgress`,
|
||||
(e) => {
|
||||
const name = $(e.target).siblings(".button.name").text();
|
||||
list["resetProgressCustomTextLong"].show([name]);
|
||||
}
|
||||
);
|
||||
|
||||
$(document).on("click", "#apeKeysPopup table tbody tr .button.delete", (e) => {
|
||||
const keyId = $(e.target).closest("tr").attr("keyId") as string;
|
||||
list["deleteApeKey"].show([keyId]);
|
||||
|
|
18
frontend/src/ts/states/custom-text-name.ts
Normal file
18
frontend/src/ts/states/custom-text-name.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
let customTestName = ""; // It should be empty when the text is not saved or a saved text has been modified
|
||||
let isLong: boolean | undefined = false;
|
||||
|
||||
export function getCustomTextName(): string {
|
||||
return customTestName;
|
||||
}
|
||||
|
||||
export function isCustomTextLong(): boolean | undefined {
|
||||
return isLong;
|
||||
}
|
||||
|
||||
export function setCustomTextName(
|
||||
newName: string,
|
||||
long: boolean | undefined
|
||||
): void {
|
||||
customTestName = newName;
|
||||
isLong = long;
|
||||
}
|
|
@ -19,6 +19,14 @@ export function setText(txt: string[]): void {
|
|||
text = txt;
|
||||
}
|
||||
|
||||
export function getText(): string {
|
||||
return text.join(" ");
|
||||
}
|
||||
|
||||
export function getTextArray(): string[] {
|
||||
return text;
|
||||
}
|
||||
|
||||
export function setIsWordRandom(val: boolean): void {
|
||||
isWordRandom = val;
|
||||
}
|
||||
|
@ -41,33 +49,93 @@ export function setDelimiter(val: string): void {
|
|||
|
||||
type CustomTextObject = Record<string, string>;
|
||||
|
||||
export function getCustomText(name: string): string[] {
|
||||
const customText = getCustomTextObject();
|
||||
type CustomTextLongObject = Record<string, { text: string; progress: number }>;
|
||||
|
||||
return customText[name].split(/ +/);
|
||||
export function getCustomText(name: string, long = false): string[] {
|
||||
if (long) {
|
||||
return getCustomTextLongObject()[name]["text"].split(/ +/);
|
||||
} else {
|
||||
return getCustomTextObject()[name].split(/ +/);
|
||||
}
|
||||
}
|
||||
|
||||
export function setCustomText(name: string, text: string | string[]): void {
|
||||
const customText = getCustomTextObject();
|
||||
export function setCustomText(
|
||||
name: string,
|
||||
text: string | string[],
|
||||
long = false
|
||||
): void {
|
||||
if (long) {
|
||||
const customText = getCustomTextLongObject();
|
||||
|
||||
if (typeof text === "string") customText[name] = text;
|
||||
else customText[name] = text.join(" ");
|
||||
customText[name] = {
|
||||
text: "",
|
||||
progress: 0,
|
||||
};
|
||||
|
||||
window.localStorage.setItem("customText", JSON.stringify(customText));
|
||||
if (typeof text === "string") {
|
||||
customText[name]["text"] = text;
|
||||
} else {
|
||||
customText[name]["text"] = text.join(" ");
|
||||
}
|
||||
|
||||
window.localStorage.setItem("customTextLong", JSON.stringify(customText));
|
||||
} else {
|
||||
const customText = getCustomTextObject();
|
||||
|
||||
if (typeof text === "string") {
|
||||
customText[name] = text;
|
||||
} else {
|
||||
customText[name] = text.join(" ");
|
||||
}
|
||||
|
||||
window.localStorage.setItem("customText", JSON.stringify(customText));
|
||||
}
|
||||
}
|
||||
|
||||
export function deleteCustomText(name: string): void {
|
||||
const customText = getCustomTextObject();
|
||||
export function deleteCustomText(name: string, long = false): void {
|
||||
const customText = long ? getCustomTextLongObject() : getCustomTextObject();
|
||||
|
||||
if (customText[name]) delete customText[name];
|
||||
|
||||
window.localStorage.setItem("customText", JSON.stringify(customText));
|
||||
if (long) {
|
||||
window.localStorage.setItem("customTextLong", JSON.stringify(customText));
|
||||
} else {
|
||||
window.localStorage.setItem("customText", JSON.stringify(customText));
|
||||
}
|
||||
}
|
||||
|
||||
export function getCustomTextLongProgress(name: string): number {
|
||||
const customText = getCustomTextLongObject();
|
||||
|
||||
return customText[name]["progress"] ?? 0;
|
||||
}
|
||||
|
||||
export function setCustomTextLongProgress(
|
||||
name: string,
|
||||
progress: number
|
||||
): void {
|
||||
const customTextProgress = getCustomTextLongObject();
|
||||
|
||||
customTextProgress[name]["progress"] = progress;
|
||||
|
||||
window.localStorage.setItem(
|
||||
"customTextLong",
|
||||
JSON.stringify(customTextProgress)
|
||||
);
|
||||
}
|
||||
|
||||
function getCustomTextObject(): CustomTextObject {
|
||||
return JSON.parse(window.localStorage.getItem("customText") ?? "{}");
|
||||
}
|
||||
|
||||
export function getCustomTextNames(): string[] {
|
||||
return Object.keys(getCustomTextObject());
|
||||
function getCustomTextLongObject(): CustomTextLongObject {
|
||||
return JSON.parse(window.localStorage.getItem("customTextLong") ?? "{}");
|
||||
}
|
||||
|
||||
export function getCustomTextNames(long = false): string[] {
|
||||
if (long) {
|
||||
return Object.keys(getCustomTextLongObject());
|
||||
} else {
|
||||
return Object.keys(getCustomTextObject());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import * as Misc from "../utils/misc";
|
|||
import QuotesController from "../controllers/quotes-controller";
|
||||
import * as Notifications from "../elements/notifications";
|
||||
import * as CustomText from "./custom-text";
|
||||
import * as CustomTextState from "../states/custom-text-name";
|
||||
import * as TestStats from "./test-stats";
|
||||
import * as PractiseWords from "./practise-words";
|
||||
import * as ShiftTracker from "./shift-tracker";
|
||||
|
@ -1604,6 +1605,29 @@ export async function finish(difficultyFailed = false): Promise<void> {
|
|||
|
||||
// test is valid
|
||||
|
||||
const customTextName = CustomTextState.getCustomTextName();
|
||||
const isLong = CustomTextState.isCustomTextLong();
|
||||
if (Config.mode === "custom" && customTextName !== "" && isLong) {
|
||||
// Let's update the custom text progress
|
||||
if (TestInput.bailout) {
|
||||
// They bailed out
|
||||
const newProgress =
|
||||
CustomText.getCustomTextLongProgress(customTextName) +
|
||||
TestInput.input.getHistory().length;
|
||||
CustomText.setCustomTextLongProgress(customTextName, newProgress);
|
||||
Notifications.add("Long custom text progress saved", 1, 5);
|
||||
|
||||
let newText = CustomText.getCustomText(customTextName, true);
|
||||
newText = newText.slice(newProgress);
|
||||
CustomText.setText(newText);
|
||||
} else {
|
||||
// They finished the test
|
||||
CustomText.setCustomTextLongProgress(customTextName, 0);
|
||||
CustomText.setText(CustomText.getCustomText(customTextName, true));
|
||||
Notifications.add("Long custom text completed", 1, 5);
|
||||
}
|
||||
}
|
||||
|
||||
if (!dontSave) {
|
||||
TodayTracker.addSeconds(
|
||||
completedEvent.testDuration +
|
||||
|
|
|
@ -438,7 +438,16 @@
|
|||
<div class="title">saved texts</div>
|
||||
<div class="buttons"></div>
|
||||
</div>
|
||||
<textarea class="textarea" placeholder="Custom text"></textarea>
|
||||
<div style="position: relative">
|
||||
<div class="longCustomTextWarning">
|
||||
<div class="textAndButton">
|
||||
A long custom text is currently loaded. Editing the text will disable
|
||||
progress tracking.
|
||||
<div class="button">Got it</div>
|
||||
</div>
|
||||
</div>
|
||||
<textarea class="textarea" placeholder="Custom text"></textarea>
|
||||
</div>
|
||||
<div class="inputs">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" />
|
||||
|
@ -496,6 +505,31 @@
|
|||
<div id="savedTextsPopup">
|
||||
<div class="title">Saved texts</div>
|
||||
<div class="list"></div>
|
||||
<div class="title">Saved long texts</div>
|
||||
<div class="listLong"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="saveCustomTextPopupWrapper" class="popupWrapper hidden">
|
||||
<div id="saveCustomTextPopup">
|
||||
<div class="title">Save custom text</div>
|
||||
<input class="textName" type="text" placeholder="name" />
|
||||
<div>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" class="isLongText" />
|
||||
<div class="customTextCheckbox">
|
||||
<div class="check">
|
||||
<i class="fas fa-fw fa-check"></i>
|
||||
</div>
|
||||
</div>
|
||||
Long text (book mode)
|
||||
<span>
|
||||
Disables editing this text but allows you to save progress when
|
||||
bailing out. You can then load this text again to continue where you
|
||||
left off.
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="button save">save</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="wordFilterPopupWrapper" class="popupWrapper hidden">
|
||||
|
|
Loading…
Reference in a new issue