Skip to content

Commit 2fc837e

Browse files
committed
improve non-user error messages
1 parent e5ab9fd commit 2fc837e

File tree

7 files changed

+134
-83
lines changed

7 files changed

+134
-83
lines changed

dist/index.html

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ <h1 lang="tok">ilo Token</h1>
9393
- <a href="https://github.com/ilo-token/ilo-token.github.io">GitHub</a>
9494
</p>
9595
</footer>
96-
<dialog id="settings-box">
96+
<dialog id="settings-box" class="flex">
9797
<h1>
9898
Settings
9999
<a
@@ -133,7 +133,7 @@ <h1>
133133
<button id="confirm-button">Confirm</button>
134134
</div>
135135
</dialog>
136-
<dialog id="custom-dictionary-box">
136+
<dialog id="custom-dictionary-box" class="flex">
137137
<h1>
138138
Custom Dictionary
139139
<a
@@ -155,5 +155,45 @@ <h1>
155155
<button id="save-button">Save and Close</button>
156156
</div>
157157
</dialog>
158+
<dialog id="alert-box">
159+
<p id="message"></p>
160+
<button id="close-button">Close</button>
161+
</dialog>
162+
<dialog id="error-box">
163+
<h1>An error has occurred</h1>
164+
<p>
165+
This is not supposed to happen. If you're using an outdated browser,
166+
please consider updating it. Otherwise please consider reporting this.
167+
We respect user's privacy and so we rely on user-submitted bug reports.
168+
Here are the contact options:
169+
</p>
170+
<ul>
171+
<li>
172+
<a href="https://github.com/ilo-token/ilo-token.github.io/issues/new"
173+
>GitHub</a>
174+
(public)
175+
</li>
176+
<li>
177+
<a
178+
href="https://conlangsfrom.space/t/ilo-token-a-wip-rule-based-toki-pona-to-english-translator/452"
179+
>Conlangs from Space</a>
180+
(public)
181+
</li>
182+
<li>
183+
<a
184+
href="https://docs.google.com/forms/d/e/1FAIpQLSfdDEMbde9mieybZdbZr8haRzNzGsg0BVkuTIzuHaATCdcrlw/viewform?usp=sf_link"
185+
>Google forms</a>
186+
(private)
187+
</li>
188+
<li><a href="mailto:[email protected]">Email</a> (private)</li>
189+
</ul>
190+
<p>Please provide the following useful information:</p>
191+
<ul>
192+
<li>What did you do before this error occurred.</li>
193+
<li>The following error message: <code id="error-code"></code></li>
194+
<li>Your browser and its version (optional).</li>
195+
</ul>
196+
<button id="error-close-button">Close</button>
197+
</dialog>
158198
</body>
159199
</html>

dist/style.css

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ body {
66
margin: 10px;
77
font-family: sans-serif, nasin-nanpa;
88
}
9+
code {
10+
font-family: monospace;
11+
}
912
a {
1013
color: #0057af;
1114
}
@@ -42,49 +45,49 @@ dialog {
4245
padding: 0px;
4346
padding-top: 20px;
4447
padding-bottom: 20px;
45-
flex-direction: column;
4648
resize: both;
4749
}
48-
dialog:modal {
50+
dialog.flex:modal {
4951
display: flex;
52+
flex-direction: column;
5053
}
5154
#custom-dictionary-box {
5255
box-sizing: border-box;
5356
height: 100%;
5457
}
55-
dialog details {
58+
dialog.flex details {
5659
margin: 0;
5760
padding: 0;
5861
flex-direction: column;
5962
resize: both;
6063
display: flex;
6164
}
62-
dialog label,
63-
dialog summary,
64-
dialog > h1 {
65+
dialog.flex label,
66+
dialog.flex summary,
67+
dialog.flex > h1 {
6568
padding: 10px;
6669
margin: 0;
6770
}
68-
dialog > label[for] {
71+
dialog.flex > label[for] {
6972
padding-bottom: 5px;
7073
}
71-
dialog > select {
74+
dialog.flex > select {
7275
margin: 10px;
7376
margin-top: 0px;
7477
}
75-
dialog > textarea {
78+
dialog.flex > textarea {
7679
font-family: monospace;
7780
margin: 10px;
7881
resize: none;
7982
overflow: auto;
8083
flex-grow: 1;
8184
}
82-
dialog > div {
85+
dialog.flex > div {
8386
margin: 10px;
8487
display: flex;
8588
}
86-
dialog > div > div,
87-
dialog > div > input[type="text"] {
89+
dialog.flex > div > div,
90+
dialog.flex > div > input[type="text"] {
8891
flex-grow: 1;
8992
}
9093
@media (min-width: 800px) {

src/array_result.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,3 @@ export function extractArrayResultError(
169169
throw new AggregateError(aggregate.errors);
170170
}
171171
}
172-
export function isArrayResult(errors: ReadonlyArray<unknown>): boolean {
173-
return errors.length > 0 &&
174-
errors.every((error) => error instanceof ArrayResultError);
175-
}

src/dictionary.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { dictionary as globalDictionary } from "../dictionary/dictionary.ts";
22
import { dictionaryParser } from "../dictionary/parser.ts";
33
import { Definition, Dictionary } from "../dictionary/type.ts";
44
import { load } from "../telo_misikeke/telo_misikeke.js";
5+
import { ArrayResultError } from "./array_result.ts";
56

67
// All of these global constants are mutable
78

@@ -17,13 +18,20 @@ export const tokiPonaWordSet: Set<string> = new Set();
1718

1819
update();
1920

20-
export function loadCustomDictionary(dictionaryText: string): void {
21-
const dictionary = dictionaryParser.parse(dictionaryText).unwrap()[0];
21+
export function loadCustomDictionary(
22+
dictionaryText: string,
23+
): null | ReadonlyArray<ArrayResultError> {
24+
const result = dictionaryParser.parse(dictionaryText);
25+
if (result.isError()) {
26+
return result.errors;
27+
}
28+
const dictionary = result.array[0];
2229
customDictionary.clear();
2330
for (const [key, value] of dictionary) {
2431
customDictionary.set(key, value);
2532
}
2633
update();
34+
return null;
2735
}
2836
function update(): void {
2937
dictionary.clear();

src/main.ts

Lines changed: 55 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ if (LIVE_RELOAD) {
99
.addEventListener("change", () => location.reload());
1010
}
1111
import { dictionary } from "../dictionary/dictionary.ts";
12-
import { flattenError } from "../misc/misc.ts";
1312
import PROJECT_DATA from "../project_data.json" with { type: "json" };
14-
import { ArrayResultError, isArrayResult } from "./array_result.ts";
1513
import { loadCustomDictionary } from "./dictionary.ts";
1614
import { checkLocalStorage, setIgnoreError } from "./local_storage.ts";
1715
import { translate } from "./mod.ts";
@@ -46,19 +44,15 @@ custom dictionary comes with
4644
limitations. Press Help above to get
4745
started.`;
4846

49-
const DICTIONARY_LOADING_FAILED_FIXABLE_MESSAGE =
47+
const DICTIONARY_LOADING_FAILED_MESSAGE =
5048
"Failed to load custom dictionary. This is mostly likely because the " +
5149
"syntax has been updated and your custom dictionary still uses the old " +
5250
"syntax. Please fix it. Apologies for the inconvenience.";
53-
const DICTIONARY_LOADING_FAILED_UNFIXABLE_MESSAGE =
54-
"Failed to load custom dictionary. Please report this.";
55-
const WORD_NOT_FOUND_MESSAGE = "Error: Word not found.";
56-
const INVALID_WORD_ERROR =
57-
"Error: Invalid word to add (You may remove this line).";
58-
const DICTIONARY_ERROR_FIXABLE_MESSAGE =
51+
const NO_WORD_MESSAGE = "Please provide a word";
52+
const WORD_NOT_FOUND_MESSAGE = "Word not found";
53+
54+
const DICTIONARY_ERROR_MESSAGE =
5955
"Please fix these errors before saving.\n(You may remove these when fixed)";
60-
const DICTIONARY_ERROR_UNFIXABLE_MESSAGE =
61-
"Unable to save dictionary due to error. Please report this.";
6256

6357
// never change this
6458
const DICTIONARY_KEY = "dictionary";
@@ -119,10 +113,34 @@ function main(): void {
119113
"save-button",
120114
) as HTMLButtonElement;
121115

116+
const alertBox = document.getElementById("alert-box") as HTMLDialogElement;
117+
const message = document.getElementById("message") as HTMLParagraphElement;
118+
const closeButton = document.getElementById(
119+
"close-button",
120+
) as HTMLButtonElement;
121+
122+
const errorBox = document.getElementById("error-box") as HTMLDialogElement;
123+
const errorCode = document.getElementById("error-code") as HTMLElement;
124+
const errorCloseButton = document.getElementById(
125+
"error-close-button",
126+
) as HTMLButtonElement;
127+
122128
const versionDisplay = document.getElementById(
123129
"version",
124130
) as HTMLAnchorElement;
125131

132+
// emulates `window.alert`
133+
function showMessage(useMessage: string): void {
134+
message.innerText = useMessage;
135+
alertBox.showModal();
136+
}
137+
138+
// handle error
139+
addEventListener("error", (event) => {
140+
errorCode.innerText = event.message;
141+
errorBox.showModal();
142+
});
143+
126144
// set version
127145
const displayDate = PROJECT_DATA.onDevelopment
128146
? "(on development)"
@@ -138,14 +156,8 @@ function main(): void {
138156
? localStorage.getItem(DICTIONARY_KEY) ?? ""
139157
: customDictionaryTextBox.value;
140158
if (customDictionary.trim() !== "") {
141-
try {
142-
loadCustomDictionary(customDictionary);
143-
} catch (error) {
144-
errorDisplay.innerText = isArrayResult(flattenError(error))
145-
? DICTIONARY_LOADING_FAILED_FIXABLE_MESSAGE
146-
: DICTIONARY_LOADING_FAILED_UNFIXABLE_MESSAGE;
147-
// deno-lint-ignore no-console
148-
console.error(error);
159+
if (loadCustomDictionary(customDictionary) != null) {
160+
showMessage(DICTIONARY_LOADING_FAILED_MESSAGE);
149161
}
150162
}
151163

@@ -180,14 +192,15 @@ function main(): void {
180192
outputList.innerHTML = "";
181193
errorList.innerHTML = "";
182194
errorDisplay.innerText = "";
183-
try {
184-
for (const translation of translate(inputTextBox.value)) {
195+
const result = translate(inputTextBox.value);
196+
if (!result.isError()) {
197+
for (const translation of result.array) {
185198
const list = document.createElement("li");
186199
list.innerHTML = translation;
187200
outputList.appendChild(list);
188201
}
189-
} catch (error) {
190-
const errors = flattenError(error);
202+
} else {
203+
const errors = result.errors;
191204
switch (errors.length) {
192205
case 0:
193206
errorDisplay.innerText = UNKNOWN_ERROR_MESSAGE;
@@ -200,15 +213,11 @@ function main(): void {
200213
break;
201214
}
202215
for (const item of errors) {
203-
const property = item instanceof ArrayResultError && item.isHtml
204-
? "innerHTML"
205-
: "innerText";
216+
const property = item.isHtml ? "innerHTML" : "innerText";
206217
const list = document.createElement("li");
207-
list[property] = extractErrorMessage(item);
218+
list[property] = item.message;
208219
errorList.appendChild(list);
209220
}
210-
// deno-lint-ignore no-console
211-
console.error(error);
212221
}
213222
}
214223
settingsButton.addEventListener("click", () => {
@@ -249,39 +258,40 @@ function main(): void {
249258
}
250259
function importWord(): void {
251260
const word = importWordTextBox.value.trim();
252-
if (/^[a-z][a-zA-Z]*$/.test(word)) {
261+
if (word === "") {
262+
showMessage(NO_WORD_MESSAGE);
263+
} else {
253264
const definitions = dictionary.get(word)?.source;
254265
if (definitions != null) {
255266
displayToCustomDictionary(`${word}:${definitions}`);
256267
} else {
257-
displayToCustomDictionary(asComment(WORD_NOT_FOUND_MESSAGE));
268+
showMessage(WORD_NOT_FOUND_MESSAGE);
258269
}
259-
} else {
260-
displayToCustomDictionary(asComment(INVALID_WORD_ERROR));
261270
}
262271
}
263272
discardButton.addEventListener("click", () => {
264273
customDictionaryDialogBox.close();
265274
});
266275
saveButton.addEventListener("click", () => {
267276
const { value } = customDictionaryTextBox;
268-
try {
269-
loadCustomDictionary(value);
277+
const errors = loadCustomDictionary(value);
278+
if (errors == null) {
270279
setIgnoreError(DICTIONARY_KEY, value);
271280
customDictionaryDialogBox.close();
272-
} catch (error) {
273-
const errors = flattenError(error);
274-
const message = isArrayResult(errors)
275-
? DICTIONARY_ERROR_FIXABLE_MESSAGE
276-
: DICTIONARY_ERROR_UNFIXABLE_MESSAGE;
281+
} else {
277282
const errorListMessage = errors
278-
.map(extractErrorMessage)
279-
.map((message) => `\n- ${message.replaceAll(/\r?\n/g, "$& ")}`);
280-
displayToCustomDictionary(asComment(`${message}${errorListMessage}`));
281-
// deno-lint-ignore no-console
282-
console.error(error);
283+
.map((error) => `\n- ${error.message.replaceAll(/\r?\n/g, "$& ")}`);
284+
displayToCustomDictionary(
285+
asComment(`${DICTIONARY_ERROR_MESSAGE}${errorListMessage}`),
286+
);
283287
}
284288
});
289+
closeButton.addEventListener("click", () => {
290+
alertBox.close();
291+
});
292+
errorCloseButton.addEventListener("click", () => {
293+
errorBox.close();
294+
});
285295
addEventListener("beforeunload", (event) => {
286296
if (customDictionaryDialogBox.open) {
287297
event.preventDefault();
@@ -301,13 +311,6 @@ const unused = [...new Array(localStorage.length).keys()]
301311
for (const key of unused) {
302312
localStorage.removeItem(key);
303313
}
304-
function extractErrorMessage(error: unknown): string {
305-
if (error instanceof Error) {
306-
return error.message;
307-
} else {
308-
return `${error}`;
309-
}
310-
}
311314
export function asComment(text: string): string {
312315
return text
313316
.replaceAll(/^/mg, "# ")

0 commit comments

Comments
 (0)