Skip to content

Commit 356ca19

Browse files
authored
impr(commandline): add local background/font commands (@byseif21) (monkeytypegame#6816)
* add commands for adding and removing custom local background and font. * fix the url image input not updating when changing the background from the commandline while in the settings page.
1 parent f9b22ad commit 356ca19

File tree

8 files changed

+237
-16
lines changed

8 files changed

+237
-16
lines changed

frontend/src/ts/commandline/commandline-metadata.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -604,13 +604,6 @@ export const commandlineConfigMetadata: CommandlineConfigMetadataObject = {
604604
},
605605
hover: (name) => UI.previewFontFamily(name),
606606
},
607-
input: {
608-
inputValueConvert: (name) => name.replaceAll(/ /g, "_"),
609-
defaultValue: () => Config.fontFamily.replace(/_/g, " "),
610-
hover: (): void => {
611-
UI.clearFontPreview();
612-
},
613-
},
614607
},
615608
keymapMode: {
616609
alias: "keyboard",

frontend/src/ts/commandline/commandline.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ async function filterSubgroup(): Promise<void> {
247247

248248
const matchCounts: number[] = [];
249249
for (const command of list) {
250-
const isAvailable = command.available?.() ?? true;
250+
const isAvailable = (await command.available?.()) ?? true;
251251
if (!isAvailable) {
252252
matches.push({
253253
matchCount: -1,

frontend/src/ts/commandline/lists.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import QuoteFavoriteCommands from "./lists/quote-favorites";
44
import ResultSavingCommands from "./lists/result-saving";
55
import NavigationCommands from "./lists/navigation";
66
import ResultScreenCommands from "./lists/result-screen";
7+
import CustomBackgroundCommands from "./lists/custom-background";
8+
import FontFamilyCommands from "./lists/font-family";
79
import CustomBackgroundFilterCommands from "./lists/background-filter";
810
import AddOrRemoveThemeToFavorite from "./lists/add-or-remove-theme-to-favorites";
911
import TagsCommands from "./lists/tags";
@@ -174,7 +176,7 @@ export const commands: CommandsSubgroup = {
174176
"startGraphsAtZero",
175177
"maxLineWidth",
176178
"fontSize",
177-
"fontFamily",
179+
...FontFamilyCommands,
178180
"keymapMode",
179181
"keymapStyle",
180182
"keymapLegendStyle",
@@ -192,7 +194,7 @@ export const commands: CommandsSubgroup = {
192194
"flipTestColors",
193195
"colorfulMode",
194196
...AddOrRemoveThemeToFavorite,
195-
"customBackground",
197+
...CustomBackgroundCommands,
196198
"customBackgroundSize",
197199
...CustomBackgroundFilterCommands,
198200
"randomTheme"
@@ -473,10 +475,10 @@ function buildSingleListCommands(
473475
icon: parentCommand.icon,
474476
alias: newAlias,
475477
visible: (parentCommand.visible ?? true) && (command.visible ?? true),
476-
available: (): boolean => {
478+
available: async (): Promise<boolean> => {
477479
return (
478-
(parentCommand?.available?.() ?? true) &&
479-
(command?.available?.() ?? true)
480+
((await parentCommand?.available?.()) ?? true) &&
481+
((await command?.available?.()) ?? true)
480482
);
481483
},
482484
};
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { Command } from "../types";
2+
import { buildCommandForConfigKey } from "../util";
3+
import FileStorage from "../../utils/file-storage";
4+
import { applyCustomBackground } from "../../controllers/theme-controller";
5+
import { updateUI } from "../../elements/settings/custom-background-picker";
6+
import * as Notifications from "../../elements/notifications";
7+
8+
const fromMeta = buildCommandForConfigKey("customBackground");
9+
10+
const customBackgroundCommand: Command = {
11+
id: "customBackground",
12+
display: "Custom background...",
13+
icon: "fa-image",
14+
subgroup: {
15+
title: "Custom background...",
16+
list: [
17+
fromMeta,
18+
{
19+
id: "customLocalBackground",
20+
display: "Local background...",
21+
icon: "fa-file-import fa-fw",
22+
alias: "upload background",
23+
available: async (): Promise<boolean> => {
24+
return !(await FileStorage.hasFile("LocalBackgroundFile"));
25+
},
26+
exec: async (): Promise<void> => {
27+
const inputElement = document.createElement("input");
28+
inputElement.type = "file";
29+
inputElement.accept = "image/*";
30+
inputElement.style.display = "none";
31+
document.body.appendChild(inputElement);
32+
33+
const cleanup = (): void => {
34+
document.body.removeChild(inputElement);
35+
};
36+
37+
inputElement.onchange = async (event) => {
38+
const file = (event.target as HTMLInputElement).files?.[0];
39+
if (!file) {
40+
cleanup();
41+
return;
42+
}
43+
44+
// check type
45+
if (!file.type.match(/image\/(jpeg|jpg|png|gif)/)) {
46+
Notifications.add("Unsupported image format", 0);
47+
cleanup();
48+
return;
49+
}
50+
51+
const reader = new FileReader();
52+
reader.onload = async (readerEvent) => {
53+
const dataUrl = readerEvent.target?.result as string;
54+
try {
55+
await FileStorage.storeFile("LocalBackgroundFile", dataUrl);
56+
await applyCustomBackground();
57+
await updateUI();
58+
} catch (e) {
59+
Notifications.add(
60+
"Error uploading background: " + (e as Error).message,
61+
0
62+
);
63+
}
64+
cleanup();
65+
};
66+
reader.onerror = cleanup;
67+
reader.readAsDataURL(file);
68+
};
69+
inputElement.click();
70+
},
71+
},
72+
{
73+
id: "removeLocalBackground",
74+
display: "Remove local background",
75+
icon: "fa-trash",
76+
alias: "remove background",
77+
available: async (): Promise<boolean> => {
78+
return await FileStorage.hasFile("LocalBackgroundFile");
79+
},
80+
exec: async (): Promise<void> => {
81+
try {
82+
await FileStorage.deleteFile("LocalBackgroundFile");
83+
await applyCustomBackground();
84+
await updateUI();
85+
} catch (e) {
86+
Notifications.add(
87+
"Error removing background: " + (e as Error).message,
88+
0
89+
);
90+
}
91+
},
92+
},
93+
],
94+
},
95+
};
96+
97+
const commands: Command[] = [customBackgroundCommand];
98+
99+
export default commands;
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import { Command } from "../types";
2+
import { buildCommandForConfigKey } from "../util";
3+
import FileStorage from "../../utils/file-storage";
4+
import { applyFontFamily } from "../../controllers/theme-controller";
5+
import { updateUI } from "../../elements/settings/custom-font-picker";
6+
import * as Notifications from "../../elements/notifications";
7+
import Config, * as UpdateConfig from "../../config";
8+
9+
const fromMeta = buildCommandForConfigKey("fontFamily");
10+
11+
if (fromMeta.subgroup) {
12+
fromMeta.subgroup.list.push({
13+
id: "customFont",
14+
display: "Custom font...",
15+
icon: "fa-font",
16+
alias: "custom font options",
17+
subgroup: {
18+
title: "Custom font...",
19+
list: [
20+
{
21+
id: "customFontName",
22+
display: "Custom name...",
23+
icon: "fa-font",
24+
alias: "custom font name",
25+
input: true,
26+
defaultValue: (): string => {
27+
return Config.fontFamily.replace(/_/g, " ");
28+
},
29+
exec: ({ input }): void => {
30+
if (input === undefined || input === "") return;
31+
const fontName = input.replaceAll(/ /g, "_");
32+
UpdateConfig.setFontFamily(fontName);
33+
},
34+
},
35+
{
36+
id: "customLocalFont",
37+
display: "Local font...",
38+
icon: "fa-file-import fa-fw",
39+
alias: "upload font",
40+
available: async (): Promise<boolean> => {
41+
return !(await FileStorage.hasFile("LocalFontFamilyFile"));
42+
},
43+
exec: async (): Promise<void> => {
44+
const inputElement = document.createElement("input");
45+
inputElement.type = "file";
46+
inputElement.accept = "font/woff,font/woff2,font/ttf,font/otf";
47+
inputElement.style.display = "none";
48+
document.body.appendChild(inputElement);
49+
50+
const cleanup = (): void => {
51+
document.body.removeChild(inputElement);
52+
};
53+
54+
inputElement.onchange = async (event) => {
55+
const file = (event.target as HTMLInputElement).files?.[0];
56+
if (!file) {
57+
cleanup();
58+
return;
59+
}
60+
61+
// check type
62+
if (
63+
!file.type.match(/font\/(woff|woff2|ttf|otf)/) &&
64+
!file.name.match(/\.(woff|woff2|ttf|otf)$/i)
65+
) {
66+
Notifications.add(
67+
"Unsupported font format, must be woff, woff2, ttf or otf.",
68+
0
69+
);
70+
cleanup();
71+
return;
72+
}
73+
74+
const reader = new FileReader();
75+
reader.onload = async (readerEvent) => {
76+
const dataUrl = readerEvent.target?.result as string;
77+
try {
78+
await FileStorage.storeFile("LocalFontFamilyFile", dataUrl);
79+
await applyFontFamily();
80+
await updateUI();
81+
} catch (e) {
82+
Notifications.add(
83+
"Error uploading font: " + (e as Error).message,
84+
0
85+
);
86+
}
87+
cleanup();
88+
};
89+
reader.onerror = cleanup;
90+
reader.readAsDataURL(file);
91+
};
92+
inputElement.click();
93+
},
94+
},
95+
{
96+
id: "removeLocalFont",
97+
display: "Remove local font",
98+
icon: "fa-trash",
99+
alias: "remove font",
100+
available: async (): Promise<boolean> => {
101+
return await FileStorage.hasFile("LocalFontFamilyFile");
102+
},
103+
exec: async (): Promise<void> => {
104+
try {
105+
await FileStorage.deleteFile("LocalFontFamilyFile");
106+
await updateUI();
107+
await applyFontFamily();
108+
} catch (e) {
109+
Notifications.add(
110+
"Error removing font: " + (e as Error).message,
111+
0
112+
);
113+
}
114+
},
115+
},
116+
],
117+
},
118+
});
119+
}
120+
121+
const commands: Command[] = [fromMeta];
122+
123+
export default commands;

frontend/src/ts/commandline/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export type Command = {
3030
configValueMode?: "include";
3131
exec?: (options: CommandExecOptions<string>) => void;
3232
hover?: () => void;
33-
available?: () => boolean;
33+
available?: () => boolean | Promise<boolean>;
3434
active?: () => boolean;
3535
shouldFocusTestUI?: boolean;
3636
customData?: Record<string, string | boolean>;

frontend/src/ts/config-metadata.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -622,8 +622,8 @@ export const configMetadata: ConfigMetadataObject = {
622622
changeRequiresRestart: false,
623623
},
624624
customBackground: {
625-
icon: "fa-image",
626-
displayString: "custom background",
625+
icon: "fa-link",
626+
displayString: "URL background",
627627
changeRequiresRestart: false,
628628
overrideValue: ({ value }) => {
629629
return value.trim();

frontend/src/ts/controllers/theme-controller.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,10 @@ export async function applyCustomBackground(): Promise<void> {
385385

386386
let backgroundUrl = Config.customBackground;
387387

388+
$(
389+
".pageSettings .section[data-config-name='customBackgroundSize'] input[type='text']"
390+
).val(backgroundUrl);
391+
388392
//if there is a localBackgroundFile available, use it.
389393
const localBackgroundFile = await fileStorage.getFile("LocalBackgroundFile");
390394

0 commit comments

Comments
 (0)