mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2026-01-09 00:45:32 +08:00
Added popup to share test settings through url (#2870)
* added package for compressing strings * added popup to share test settings * added function to parse test settings from url * returning if no settings were applied * removed pointless try catches * added type for shared setings instead of casting * small refactor * unnecessary ??
This commit is contained in:
parent
9ca477da54
commit
4d365802a1
8 changed files with 385 additions and 6 deletions
11
frontend/package-lock.json
generated
11
frontend/package-lock.json
generated
|
|
@ -21,6 +21,7 @@
|
|||
"firebase": "9.6.0",
|
||||
"howler": "2.2.3",
|
||||
"html2canvas": "1.4.1",
|
||||
"lz-ts": "1.1.2",
|
||||
"object-hash": "3.0.0",
|
||||
"remove-files-webpack-plugin": "1.5.0",
|
||||
"stemmer": "2.0.0",
|
||||
|
|
@ -8194,6 +8195,11 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/lz-ts": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lz-ts/-/lz-ts-1.1.2.tgz",
|
||||
"integrity": "sha512-ye8sVndmvzs46cPgX1Yjlk3o/Sueu0VHn253rKpsWiK2/bAbsVkD7DEJiaueiPfbZTi17GLRPkv3W5O3BUNd2g=="
|
||||
},
|
||||
"node_modules/madge": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/madge/-/madge-5.0.1.tgz",
|
||||
|
|
@ -20833,6 +20839,11 @@
|
|||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"lz-ts": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lz-ts/-/lz-ts-1.1.2.tgz",
|
||||
"integrity": "sha512-ye8sVndmvzs46cPgX1Yjlk3o/Sueu0VHn253rKpsWiK2/bAbsVkD7DEJiaueiPfbZTi17GLRPkv3W5O3BUNd2g=="
|
||||
},
|
||||
"madge": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/madge/-/madge-5.0.1.tgz",
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
"firebase": "9.6.0",
|
||||
"howler": "2.2.3",
|
||||
"html2canvas": "1.4.1",
|
||||
"lz-ts": "1.1.2",
|
||||
"object-hash": "3.0.0",
|
||||
"remove-files-webpack-plugin": "1.5.0",
|
||||
"stemmer": "2.0.0",
|
||||
|
|
|
|||
|
|
@ -302,6 +302,7 @@ const authListener = Auth.onAuthStateChanged(async function (user) {
|
|||
}
|
||||
|
||||
URLHandler.loadCustomThemeFromUrl();
|
||||
URLHandler.loadTestSettingsFromUrl();
|
||||
if (/challenge_.+/g.test(window.location.pathname)) {
|
||||
Notifications.add(
|
||||
"Challenge links temporarily disabled. Please use the command line to load the challenge manually",
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import * as PaceCaret from "../test/pace-caret";
|
|||
import * as TestInput from "../test/test-input";
|
||||
import * as ModesNotice from "../elements/modes-notice";
|
||||
import * as ConfigEvent from "../observables/config-event";
|
||||
import * as ShareTestSettingsPopup from "../popups/share-test-settings-popup";
|
||||
import { Auth } from "../firebase";
|
||||
|
||||
export let current: MonkeyTypes.CommandsGroup[] = [];
|
||||
|
|
@ -3311,6 +3312,14 @@ export const defaultCommands: MonkeyTypes.CommandsGroup = {
|
|||
visible: false,
|
||||
subgroup: commandsMonkeyPowerLevel,
|
||||
},
|
||||
{
|
||||
id: "shareTestSettings",
|
||||
display: "Share test settings",
|
||||
icon: "fa-share",
|
||||
exec: async (): Promise<void> => {
|
||||
ShareTestSettingsPopup.show();
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "clearSwCache",
|
||||
display: "Clear SW cache",
|
||||
|
|
|
|||
146
frontend/src/scripts/popups/share-test-settings-popup.ts
Normal file
146
frontend/src/scripts/popups/share-test-settings-popup.ts
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
import Config from "../config";
|
||||
import { randomQuote } from "../test/test-words";
|
||||
import { getMode2 } from "../utils/misc";
|
||||
import * as CustomText from "../test/custom-text";
|
||||
import { compressToURI } from "lz-ts";
|
||||
|
||||
function getCheckboxValue(checkbox: string): boolean {
|
||||
return $(`#shareTestSettingsPopupWrapper label.${checkbox} input`).prop(
|
||||
"checked"
|
||||
);
|
||||
}
|
||||
|
||||
type SharedTestSettings = [
|
||||
MonkeyTypes.Mode | null,
|
||||
MonkeyTypes.Mode2<MonkeyTypes.Mode> | null,
|
||||
MonkeyTypes.CustomText | null,
|
||||
boolean | null,
|
||||
boolean | null,
|
||||
string | null,
|
||||
MonkeyTypes.Difficulty | null,
|
||||
string | null
|
||||
];
|
||||
|
||||
function updateURL(): void {
|
||||
const baseUrl = location.origin + "?testSettings=";
|
||||
const settings: SharedTestSettings = [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
];
|
||||
|
||||
if (getCheckboxValue("mode")) {
|
||||
settings[0] = Config.mode;
|
||||
}
|
||||
|
||||
if (getCheckboxValue("mode2")) {
|
||||
settings[1] = getMode2(
|
||||
Config,
|
||||
randomQuote
|
||||
) as MonkeyTypes.Mode2<MonkeyTypes.Mode>;
|
||||
}
|
||||
|
||||
if (getCheckboxValue("customText")) {
|
||||
settings[2] = {
|
||||
text: CustomText.text,
|
||||
isWordRandom: CustomText.isWordRandom,
|
||||
isTimeRandom: CustomText.isTimeRandom,
|
||||
word: CustomText.word,
|
||||
time: CustomText.time,
|
||||
delimiter: CustomText.delimiter,
|
||||
};
|
||||
}
|
||||
|
||||
if (getCheckboxValue("punctuation")) {
|
||||
settings[3] = Config.punctuation;
|
||||
}
|
||||
|
||||
if (getCheckboxValue("numbers")) {
|
||||
settings[4] = Config.numbers;
|
||||
}
|
||||
|
||||
if (getCheckboxValue("language")) {
|
||||
settings[5] = Config.language;
|
||||
}
|
||||
|
||||
if (getCheckboxValue("difficulty")) {
|
||||
settings[6] = Config.difficulty;
|
||||
}
|
||||
|
||||
if (getCheckboxValue("funbox")) {
|
||||
settings[7] = Config.funbox;
|
||||
}
|
||||
|
||||
const compressed = compressToURI(JSON.stringify(settings));
|
||||
|
||||
const url = baseUrl + compressed;
|
||||
$(`#shareTestSettingsPopupWrapper textarea.url`).val(url);
|
||||
if (url.length > 2000) {
|
||||
$(`#shareTestSettingsPopupWrapper .tooLongWarning`).removeClass("hidden");
|
||||
} else {
|
||||
$(`#shareTestSettingsPopupWrapper .tooLongWarning`).addClass("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function updateSubgroups(): void {
|
||||
if (getCheckboxValue("mode")) {
|
||||
$(`#shareTestSettingsPopupWrapper .subgroup`).removeClass("hidden");
|
||||
} else {
|
||||
$(`#shareTestSettingsPopupWrapper .subgroup`).addClass("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
export function show(): void {
|
||||
if ($("#shareTestSettingsPopupWrapper").hasClass("hidden")) {
|
||||
updateURL();
|
||||
updateSubgroups();
|
||||
$("#shareTestSettingsPopupWrapper")
|
||||
.stop(true, true)
|
||||
.css("opacity", 0)
|
||||
.removeClass("hidden")
|
||||
.animate({ opacity: 1 }, 100);
|
||||
}
|
||||
}
|
||||
|
||||
export async function hide(): Promise<void> {
|
||||
if (!$("#shareTestSettingsPopupWrapper").hasClass("hidden")) {
|
||||
$("#shareTestSettingsPopupWrapper")
|
||||
.stop(true, true)
|
||||
.css("opacity", 1)
|
||||
.animate(
|
||||
{
|
||||
opacity: 0,
|
||||
},
|
||||
100,
|
||||
() => {
|
||||
$("#shareTestSettingsPopupWrapper").addClass("hidden");
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$(`#shareTestSettingsPopupWrapper label input`).on("change", () => {
|
||||
updateURL();
|
||||
updateSubgroups();
|
||||
});
|
||||
|
||||
$("#shareTestSettingsPopupWrapper").on("mousedown", (e) => {
|
||||
if ($(e.target).attr("id") === "shareTestSettingsPopupWrapper") {
|
||||
hide();
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("keydown", (event) => {
|
||||
if (
|
||||
event.key === "Escape" &&
|
||||
!$("#shareTestSettingsPopupWrapper").hasClass("hidden")
|
||||
) {
|
||||
hide();
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
import * as Misc from "./misc";
|
||||
import Config, * as UpdateConfig from "../config";
|
||||
import * as Notifications from "../elements/notifications";
|
||||
import { decompressFromURI } from "lz-ts";
|
||||
import * as QuoteSearchPopup from "../popups/quote-search-popup";
|
||||
import * as ManualRestart from "../test/manual-restart-tracker";
|
||||
import * as CustomText from "../test/custom-text";
|
||||
import { restart as restartTest } from "../test/test-logic";
|
||||
|
||||
export function loadCustomThemeFromUrl(): void {
|
||||
const getValue = Misc.findGetParameter("customTheme");
|
||||
if (getValue === null) return;
|
||||
|
||||
const urlEncoded = getValue.split(",");
|
||||
let base64decoded = null;
|
||||
try {
|
||||
base64decoded = JSON.parse(atob(getValue) ?? "");
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
const base64decoded = JSON.parse(atob(getValue) ?? "");
|
||||
|
||||
let colorArray = [];
|
||||
if (Array.isArray(urlEncoded) && urlEncoded.length === 9) {
|
||||
|
|
@ -39,3 +39,98 @@ export function loadCustomThemeFromUrl(): void {
|
|||
UpdateConfig.setCustomThemeColors(oldCustomThemeColors);
|
||||
}
|
||||
}
|
||||
|
||||
type SharedTestSettings = [
|
||||
MonkeyTypes.Mode | null,
|
||||
MonkeyTypes.Mode2<MonkeyTypes.Mode> | null,
|
||||
MonkeyTypes.CustomText | null,
|
||||
boolean | null,
|
||||
boolean | null,
|
||||
string | null,
|
||||
MonkeyTypes.Difficulty | null,
|
||||
string | null
|
||||
];
|
||||
|
||||
export function loadTestSettingsFromUrl(): void {
|
||||
const getValue = Misc.findGetParameter("testSettings");
|
||||
if (getValue === null) return;
|
||||
|
||||
const de: SharedTestSettings = JSON.parse(decompressFromURI(getValue) ?? "");
|
||||
|
||||
const applied: { [key: string]: string } = {};
|
||||
|
||||
if (de[0]) {
|
||||
UpdateConfig.setMode(de[0], true);
|
||||
applied["mode"] = de[0];
|
||||
}
|
||||
|
||||
if (de[1]) {
|
||||
if (Config.mode === "time") {
|
||||
UpdateConfig.setTimeConfig(parseInt(de[1], 10), true);
|
||||
} else if (Config.mode === "words") {
|
||||
UpdateConfig.setWordCount(parseInt(de[1], 10), true);
|
||||
} else if (Config.mode === "quote") {
|
||||
UpdateConfig.setQuoteLength(-2, false);
|
||||
QuoteSearchPopup.setSelectedId(parseInt(de[1], 10));
|
||||
ManualRestart.set();
|
||||
}
|
||||
applied["mode2"] = de[1];
|
||||
}
|
||||
|
||||
if (de[2]) {
|
||||
const customTextSettings = de[2];
|
||||
CustomText.setText(customTextSettings["text"]);
|
||||
CustomText.setIsTimeRandom(customTextSettings["isTimeRandom"]);
|
||||
CustomText.setIsWordRandom(customTextSettings["isWordRandom"]);
|
||||
if (customTextSettings["isTimeRandom"]) {
|
||||
CustomText.setWord(customTextSettings["time"]);
|
||||
}
|
||||
if (customTextSettings["isWordRandom"]) {
|
||||
CustomText.setTime(customTextSettings["word"]);
|
||||
}
|
||||
CustomText.setDelimiter(customTextSettings["delimiter"]);
|
||||
applied["custom text settings"] = "";
|
||||
}
|
||||
|
||||
if (de[3]) {
|
||||
UpdateConfig.setPunctuation(de[3], true);
|
||||
applied["punctuation"] = de[3] ? "on" : "off";
|
||||
}
|
||||
|
||||
if (de[4]) {
|
||||
UpdateConfig.setNumbers(de[4], true);
|
||||
applied["numbers"] = de[4] ? "on" : "off";
|
||||
}
|
||||
|
||||
if (de[5]) {
|
||||
UpdateConfig.setLanguage(de[5], true);
|
||||
applied["language"] = de[5];
|
||||
}
|
||||
|
||||
if (de[6]) {
|
||||
UpdateConfig.setDifficulty(de[6], true);
|
||||
applied["difficulty"] = de[6];
|
||||
}
|
||||
|
||||
if (de[7]) {
|
||||
UpdateConfig.setFunbox(de[7], true);
|
||||
applied["funbox"] = de[7];
|
||||
}
|
||||
|
||||
restartTest();
|
||||
|
||||
let appliedString = "";
|
||||
|
||||
Object.keys(applied).forEach((setKey) => {
|
||||
const set = applied[setKey];
|
||||
appliedString += `${setKey}${set ? ": " + set : ""}<br>`;
|
||||
});
|
||||
|
||||
if (appliedString !== "") {
|
||||
Notifications.add(
|
||||
"Settings applied from URL:<br><br>" + appliedString,
|
||||
1,
|
||||
10
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -399,6 +399,26 @@
|
|||
}
|
||||
}
|
||||
|
||||
#shareTestSettingsPopup {
|
||||
background: var(--bg-color);
|
||||
border-radius: var(--roundness);
|
||||
padding: 2rem;
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
width: 500px;
|
||||
.title {
|
||||
font-size: 1.5rem;
|
||||
color: var(--sub-color);
|
||||
}
|
||||
.subgroup {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
.tooLongWarning {
|
||||
font-size: 0.75rem;
|
||||
color: var(--error-color);
|
||||
}
|
||||
}
|
||||
|
||||
#pbTablesPopupWrapper #pbTablesPopup {
|
||||
.title {
|
||||
color: var(--text-color);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,102 @@
|
|||
<div id="simplePopup" popupId=""></div>
|
||||
</div>
|
||||
|
||||
<div id="shareTestSettingsPopupWrapper" class="popupWrapper hidden">
|
||||
<div id="shareTestSettingsPopup">
|
||||
<div class="title">Share test settings</div>
|
||||
<label class="checkbox mode">
|
||||
<input type="checkbox" setting="mode" />
|
||||
<div class="customTextCheckbox">
|
||||
<div class="check">
|
||||
<i class="fas fa-fw fa-check"></i>
|
||||
</div>
|
||||
</div>
|
||||
Mode
|
||||
<span>Time, Words, Quote, Zen, Custom</span>
|
||||
</label>
|
||||
<label class="checkbox mode2 subgroup hidden">
|
||||
<input type="checkbox" setting="mode2" />
|
||||
<div class="customTextCheckbox">
|
||||
<div class="check">
|
||||
<i class="fas fa-fw fa-check"></i>
|
||||
</div>
|
||||
</div>
|
||||
Mode2
|
||||
<span>Test seconds, Test words, Quote Id</span>
|
||||
</label>
|
||||
<label class="checkbox customText subgroup hidden">
|
||||
<input type="checkbox" setting="customText" />
|
||||
<div class="customTextCheckbox">
|
||||
<div class="check">
|
||||
<i class="fas fa-fw fa-check"></i>
|
||||
</div>
|
||||
</div>
|
||||
Custom text
|
||||
<span></span>
|
||||
</label>
|
||||
<label class="checkbox punctuation">
|
||||
<input type="checkbox" setting="punctuation" />
|
||||
<div class="customTextCheckbox">
|
||||
<div class="check">
|
||||
<i class="fas fa-fw fa-check"></i>
|
||||
</div>
|
||||
</div>
|
||||
Punctuation
|
||||
<span></span>
|
||||
</label>
|
||||
<label class="checkbox numbers">
|
||||
<input type="checkbox" setting="numbers" />
|
||||
<div class="customTextCheckbox">
|
||||
<div class="check">
|
||||
<i class="fas fa-fw fa-check"></i>
|
||||
</div>
|
||||
</div>
|
||||
Numbers
|
||||
<span></span>
|
||||
</label>
|
||||
<label class="checkbox language">
|
||||
<input type="checkbox" setting="language" />
|
||||
<div class="customTextCheckbox">
|
||||
<div class="check">
|
||||
<i class="fas fa-fw fa-check"></i>
|
||||
</div>
|
||||
</div>
|
||||
Language
|
||||
<span></span>
|
||||
</label>
|
||||
<label class="checkbox difficulty">
|
||||
<input type="checkbox" setting="difficulty" />
|
||||
<div class="customTextCheckbox">
|
||||
<div class="check">
|
||||
<i class="fas fa-fw fa-check"></i>
|
||||
</div>
|
||||
</div>
|
||||
Difficulty
|
||||
<span></span>
|
||||
</label>
|
||||
<label class="checkbox funbox">
|
||||
<input type="checkbox" setting="funbox" />
|
||||
<div class="customTextCheckbox">
|
||||
<div class="check">
|
||||
<i class="fas fa-fw fa-check"></i>
|
||||
</div>
|
||||
</div>
|
||||
Funbox
|
||||
<span></span>
|
||||
</label>
|
||||
<textarea
|
||||
class="url"
|
||||
placeholder="url"
|
||||
value="monkeytype.com"
|
||||
readonly
|
||||
></textarea>
|
||||
<div class="tooLongWarning hidden">
|
||||
<i class="fas fa-fw fa-exclamation-triangle"></i>
|
||||
<span>The URL is over 2000 characters long - it might not work</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mobileTestConfigPopupWrapper" class="popupWrapper hidden">
|
||||
<div id="mobileTestConfigPopup">
|
||||
<div class="group">
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue