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:
Jack 2022-04-24 17:05:04 +02:00 committed by GitHub
parent 9ca477da54
commit 4d365802a1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 385 additions and 6 deletions

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View 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();
}
});

View file

@ -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
);
}
}

View file

@ -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);

View file

@ -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">