Enable strict TypeScript, add errorMessage helper (#37292)

Enable full TypeScript `strict` mode and fix issues discovered during
this refactor. Introduced a `errorMessage` helper function to cleanly
extract a error messages from the `unknown` type.

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (claude-opus-4-7) <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
silverwind
2026-04-20 07:22:05 +00:00
committed by GitHub
co-authored by GitHub Claude wxiaoguang
parent 5a3d8d3224
commit f6960096f3
24 changed files with 74 additions and 51 deletions
-2
View File
@@ -35,8 +35,6 @@
"skipLibCheck": true, "skipLibCheck": true,
"sourceMap": true, "sourceMap": true,
"strict": true, "strict": true,
"strictPropertyInitialization": false,
"useUnknownInCatchVariables": false,
"stripInternal": true, "stripInternal": true,
"verbatimModuleSyntax": true, "verbatimModuleSyntax": true,
"types": [ "types": [
+2 -1
View File
@@ -21,6 +21,7 @@ import {
type DayDataObject, type DayDataObject,
} from '../utils/time.ts'; } from '../utils/time.ts';
import {chartJsColors} from '../utils/color.ts'; import {chartJsColors} from '../utils/color.ts';
import {errorMessage} from '../modules/errors.ts';
import {sleep} from '../utils.ts'; import {sleep} from '../utils.ts';
import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm'; import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm';
import {onMounted, shallowRef} from 'vue'; import {onMounted, shallowRef} from 'vue';
@@ -78,7 +79,7 @@ async function fetchGraphData() {
errorText.value = response.statusText; errorText.value = response.statusText;
} }
} catch (err) { } catch (err) {
errorText.value = err.message; errorText.value = errorMessage(err);
} finally { } finally {
isLoading.value = false; isLoading.value = false;
} }
+2 -1
View File
@@ -24,6 +24,7 @@ import {
fillEmptyStartDaysWithZeroes, fillEmptyStartDaysWithZeroes,
} from '../utils/time.ts'; } from '../utils/time.ts';
import {chartJsColors} from '../utils/color.ts'; import {chartJsColors} from '../utils/color.ts';
import {errorMessage} from '../modules/errors.ts';
import {sleep} from '../utils.ts'; import {sleep} from '../utils.ts';
import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm'; import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm';
import {fomanticQuery} from '../modules/fomantic/base.ts'; import {fomanticQuery} from '../modules/fomantic/base.ts';
@@ -166,7 +167,7 @@ export default defineComponent({
this.errorText = response.statusText; this.errorText = response.statusText;
} }
} catch (err) { } catch (err) {
this.errorText = err.message; this.errorText = errorMessage(err);
} finally { } finally {
this.isLoading = false; this.isLoading = false;
} }
+2 -1
View File
@@ -20,6 +20,7 @@ import {
type DayDataObject, type DayDataObject,
} from '../utils/time.ts'; } from '../utils/time.ts';
import {chartJsColors} from '../utils/color.ts'; import {chartJsColors} from '../utils/color.ts';
import {errorMessage} from '../modules/errors.ts';
import {sleep} from '../utils.ts'; import {sleep} from '../utils.ts';
import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm'; import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm';
import {onMounted, ref, shallowRef} from 'vue'; import {onMounted, ref, shallowRef} from 'vue';
@@ -74,7 +75,7 @@ async function fetchGraphData() {
errorText.value = response.statusText; errorText.value = response.statusText;
} }
} catch (err) { } catch (err) {
errorText.value = err.message; errorText.value = errorMessage(err);
} finally { } finally {
isLoading.value = false; isLoading.value = false;
} }
+2 -1
View File
@@ -2,6 +2,7 @@ import {showTemporaryTooltip} from '../../modules/tippy.ts';
import {POST} from '../../modules/fetch.ts'; import {POST} from '../../modules/fetch.ts';
import {registerGlobalInitFunc} from '../../modules/observer.ts'; import {registerGlobalInitFunc} from '../../modules/observer.ts';
import {queryElems} from '../../utils/dom.ts'; import {queryElems} from '../../utils/dom.ts';
import {errorMessage} from '../../modules/errors.ts';
import {submitFormFetchAction} from '../common-fetch-action.ts'; import {submitFormFetchAction} from '../common-fetch-action.ts';
const {appSubUrl} = window.config; const {appSubUrl} = window.config;
@@ -26,7 +27,7 @@ function initSystemConfigAutoCheckbox(el: HTMLInputElement) {
const json: Record<string, any> = await resp.json(); const json: Record<string, any> = await resp.json();
if (json.errorMessage) throw new Error(json.errorMessage); if (json.errorMessage) throw new Error(json.errorMessage);
} catch (ex) { } catch (ex) {
showTemporaryTooltip(el, ex.toString()); showTemporaryTooltip(el, errorMessage(ex));
el.checked = !el.checked; el.checked = !el.checked;
} }
}); });
+2 -1
View File
@@ -1,4 +1,5 @@
import {getCurrentLocale} from '../utils.ts'; import {getCurrentLocale} from '../utils.ts';
import {errorMessage} from '../modules/errors.ts';
import {fomanticQuery} from '../modules/fomantic/base.ts'; import {fomanticQuery} from '../modules/fomantic/base.ts';
import {localUserSettings} from '../modules/user-settings.ts'; import {localUserSettings} from '../modules/user-settings.ts';
@@ -46,7 +47,7 @@ export async function initCitationFileCopyContent() {
try { try {
await initInputCitationValue(citationCopyApa, citationCopyBibtex); await initInputCitationValue(citationCopyApa, citationCopyBibtex);
} catch (e) { } catch (e) {
console.error(`initCitationFileCopyContent error: ${e}`, e); console.error(`initCitationFileCopyContent error: ${errorMessage(e)}`, e);
return; return;
} }
updateUi(); updateUi();
+5 -4
View File
@@ -1,6 +1,7 @@
import {GET, request} from '../modules/fetch.ts'; import {GET, request} from '../modules/fetch.ts';
import {hideToastsAll, showErrorToast} from '../modules/toast.ts'; import {hideToastsAll, showErrorToast} from '../modules/toast.ts';
import {addDelegatedEventListener, createElementFromHTML} from '../utils/dom.ts'; import {addDelegatedEventListener, createElementFromHTML} from '../utils/dom.ts';
import {errorMessage} from '../modules/errors.ts';
import {confirmModal, createConfirmModal} from './comp/ConfirmModal.ts'; import {confirmModal, createConfirmModal} from './comp/ConfirmModal.ts';
import {ignoreAreYouSure} from '../vendor/jquery.are-you-sure.ts'; import {ignoreAreYouSure} from '../vendor/jquery.are-you-sure.ts';
import {registerGlobalSelectorFunc} from '../modules/observer.ts'; import {registerGlobalSelectorFunc} from '../modules/observer.ts';
@@ -134,10 +135,10 @@ async function performActionRequest(el: HTMLElement, opt: FetchActionOpts) {
return; return;
} }
await handleFetchActionError(resp); await handleFetchActionError(resp);
} catch (e) { } catch (err) {
if (e.name !== 'AbortError') { if ((err as Error).name !== 'AbortError') {
console.error(`Fetch action request error:`, e); console.error(`Fetch action request error:`, err);
showErrorToast(`Error: ${e.message ?? e}`); showErrorToast(`Error: ${errorMessage(err)}`);
} }
} finally { } finally {
toggleLoadingIndicator(el, opt, false); toggleLoadingIndicator(el, opt, false);
+11 -11
View File
@@ -71,26 +71,26 @@ export class ComboMarkdownEditor {
options: ComboMarkdownEditorOptions; options: ComboMarkdownEditorOptions;
tabEditor: HTMLElement; tabEditor?: HTMLElement;
tabPreviewer: HTMLElement; tabPreviewer?: HTMLElement;
supportEasyMDE: boolean; supportEasyMDE!: boolean;
easyMDE: any; easyMDE: any;
easyMDEToolbarActions: any; easyMDEToolbarActions: any;
easyMDEToolbarDefault: any; easyMDEToolbarDefault: any;
textarea: ComboMarkdownEditorTextarea; textarea!: ComboMarkdownEditorTextarea;
textareaMarkdownToolbar: HTMLElement; textareaMarkdownToolbar!: HTMLElement;
textareaAutosize: any; textareaAutosize: any;
buttonMonospace: HTMLButtonElement; buttonMonospace!: HTMLButtonElement;
dropzone: HTMLElement | null; dropzone: HTMLElement | null = null;
attachedDropzoneInst: any; attachedDropzoneInst: any;
previewMode: string; previewMode!: string;
previewUrl: string; previewUrl!: string;
previewContext: string; previewContext!: string;
constructor(container: ComboMarkdownEditorContainer, options:ComboMarkdownEditorOptions = {}) { constructor(container: ComboMarkdownEditorContainer, options:ComboMarkdownEditorOptions = {}) {
if (container._giteaComboMarkdownEditor) throw new Error('ComboMarkdownEditor already initialized'); if (container._giteaComboMarkdownEditor) throw new Error('ComboMarkdownEditor already initialized');
@@ -291,7 +291,7 @@ export class ComboMarkdownEditor {
} }
switchTabToEditor() { switchTabToEditor() {
this.tabEditor.click(); this.tabEditor!.click(); // when this function is called, the tab must exist
} }
prepareEasyMDEToolbarActions() { prepareEasyMDEToolbarActions() {
+2 -1
View File
@@ -5,6 +5,7 @@ import {showTemporaryTooltip} from '../modules/tippy.ts';
import {GET, POST} from '../modules/fetch.ts'; import {GET, POST} from '../modules/fetch.ts';
import {showErrorToast} from '../modules/toast.ts'; import {showErrorToast} from '../modules/toast.ts';
import {createElementFromHTML, createElementFromAttrs} from '../utils/dom.ts'; import {createElementFromHTML, createElementFromAttrs} from '../utils/dom.ts';
import {errorMessage} from '../modules/errors.ts';
import {isImageFile, isVideoFile} from '../utils.ts'; import {isImageFile, isVideoFile} from '../utils.ts';
import type Dropzone from '@deltablot/dropzone'; import type Dropzone from '@deltablot/dropzone';
@@ -149,7 +150,7 @@ export async function initDropzone(dropzoneEl: HTMLElement) {
} catch (error) { } catch (error) {
// TODO: if listing the existing attachments failed, it should stop from operating the content or attachments, // TODO: if listing the existing attachments failed, it should stop from operating the content or attachments,
// otherwise the attachments might be lost. // otherwise the attachments might be lost.
showErrorToast(`Failed to load attachments: ${error}`); showErrorToast(`Failed to load attachments: ${errorMessage(error)}`);
console.error(error); console.error(error);
} }
}); });
+2 -1
View File
@@ -2,6 +2,7 @@ import type {InplaceRenderPlugin} from '../render/plugin.ts';
import {newInplacePluginPdfViewer} from '../render/plugins/inplace-pdf-viewer.ts'; import {newInplacePluginPdfViewer} from '../render/plugins/inplace-pdf-viewer.ts';
import {registerGlobalInitFunc} from '../modules/observer.ts'; import {registerGlobalInitFunc} from '../modules/observer.ts';
import {createElementFromHTML} from '../utils/dom.ts'; import {createElementFromHTML} from '../utils/dom.ts';
import {errorMessage} from '../modules/errors.ts';
import {html} from '../utils/html.ts'; import {html} from '../utils/html.ts';
import {basename} from '../utils.ts'; import {basename} from '../utils.ts';
@@ -30,7 +31,7 @@ async function renderRawFileToContainer(container: HTMLElement, rawFileLink: str
rendered = true; rendered = true;
} }
} catch (e) { } catch (e) {
errorMsg = `${e}`; errorMsg = errorMessage(e);
} finally { } finally {
container.classList.remove('is-loading'); container.classList.remove('is-loading');
} }
+2 -2
View File
@@ -94,8 +94,8 @@ function createContext(imageAfter: HTMLImageElement, imageBefore: HTMLImageEleme
} }
class ImageDiff { class ImageDiff {
containerEl: HTMLElement; containerEl!: HTMLElement;
diffContainerWidth: number; diffContainerWidth!: number;
async init(containerEl: HTMLElement) { async init(containerEl: HTMLElement) {
this.containerEl = containerEl; this.containerEl = containerEl;
+2 -1
View File
@@ -1,4 +1,5 @@
import {queryElems} from '../utils/dom.ts'; import {queryElems} from '../utils/dom.ts';
import {errorMessage} from '../modules/errors.ts';
import {POST} from '../modules/fetch.ts'; import {POST} from '../modules/fetch.ts';
import {showErrorToast} from '../modules/toast.ts'; import {showErrorToast} from '../modules/toast.ts';
import {sleep} from '../utils.ts'; import {sleep} from '../utils.ts';
@@ -26,7 +27,7 @@ async function onDownloadArchive(e: Event) {
window.location.href = el.href; // the archive is ready, start real downloading window.location.href = el.href; // the archive is ready, start real downloading
} catch (e) { } catch (e) {
console.error(e); console.error(e);
showErrorToast(`Failed to download the archive: ${e}`, {duration: 2500}); showErrorToast(`Failed to download the archive: ${errorMessage(e)}`, {duration: 2500});
} finally { } finally {
targetLoading.classList.remove('is-loading', 'loading-icon-2px'); targetLoading.classList.remove('is-loading', 'loading-icon-2px');
} }
+2 -1
View File
@@ -6,6 +6,7 @@ import {initViewedCheckboxListenerFor, initExpandAndCollapseFilesButton} from '.
import {initImageDiff} from './imagediff.ts'; import {initImageDiff} from './imagediff.ts';
import {showErrorToast} from '../modules/toast.ts'; import {showErrorToast} from '../modules/toast.ts';
import {queryElemSiblings, hideElem, showElem, animateOnce, addDelegatedEventListener, createElementFromHTML, queryElems} from '../utils/dom.ts'; import {queryElemSiblings, hideElem, showElem, animateOnce, addDelegatedEventListener, createElementFromHTML, queryElems} from '../utils/dom.ts';
import {errorMessage} from '../modules/errors.ts';
import {POST, GET} from '../modules/fetch.ts'; import {POST, GET} from '../modules/fetch.ts';
import {createTippy} from '../modules/tippy.ts'; import {createTippy} from '../modules/tippy.ts';
import {invertFileFolding} from './file-fold.ts'; import {invertFileFolding} from './file-fold.ts';
@@ -85,7 +86,7 @@ function initRepoDiffConversationForm() {
} }
} catch (error) { } catch (error) {
console.error('Error:', error); console.error('Error:', error);
showErrorToast(`Submit form failed: ${error}`); showErrorToast(`Submit form failed: ${errorMessage(error)}`);
} finally { } finally {
form?.classList.remove('is-loading'); form?.classList.remove('is-loading');
} }
+2 -1
View File
@@ -3,6 +3,7 @@ import {getComboMarkdownEditor, initComboMarkdownEditor, ComboMarkdownEditor} fr
import {POST} from '../modules/fetch.ts'; import {POST} from '../modules/fetch.ts';
import {showErrorToast} from '../modules/toast.ts'; import {showErrorToast} from '../modules/toast.ts';
import {hideElem, querySingleVisibleElem, showElem} from '../utils/dom.ts'; import {hideElem, querySingleVisibleElem, showElem} from '../utils/dom.ts';
import {errorMessage} from '../modules/errors.ts';
import {triggerUploadStateChanged} from './comp/EditorUpload.ts'; import {triggerUploadStateChanged} from './comp/EditorUpload.ts';
import {convertHtmlToMarkdown} from '../markup/html2markdown.ts'; import {convertHtmlToMarkdown} from '../markup/html2markdown.ts';
import {applyAreYouSure, reinitializeAreYouSure} from '../vendor/jquery.are-you-sure.ts'; import {applyAreYouSure, reinitializeAreYouSure} from '../vendor/jquery.are-you-sure.ts';
@@ -73,7 +74,7 @@ async function tryOnEditContent(e: Event) {
} }
comboMarkdownEditor.dropzoneSubmitReload(); comboMarkdownEditor.dropzoneSubmitReload();
} catch (error) { } catch (error) {
showErrorToast(`Failed to save the content: ${error}`); showErrorToast(`Failed to save the content: ${errorMessage(error)}`);
console.error(error); console.error(error);
} finally { } finally {
renderContent.classList.remove('is-loading'); renderContent.classList.remove('is-loading');
+4 -1
View File
@@ -2,6 +2,7 @@ import {updateIssuesMeta} from './repo-common.ts';
import {toggleElem, queryElems, isElemVisible} from '../utils/dom.ts'; import {toggleElem, queryElems, isElemVisible} from '../utils/dom.ts';
import {html, htmlRaw} from '../utils/html.ts'; import {html, htmlRaw} from '../utils/html.ts';
import {confirmModal} from './comp/ConfirmModal.ts'; import {confirmModal} from './comp/ConfirmModal.ts';
import {errorMessage} from '../modules/errors.ts';
import {showErrorToast} from '../modules/toast.ts'; import {showErrorToast} from '../modules/toast.ts';
import {createSortable} from '../modules/sortable.ts'; import {createSortable} from '../modules/sortable.ts';
import {DELETE, POST} from '../modules/fetch.ts'; import {DELETE, POST} from '../modules/fetch.ts';
@@ -87,7 +88,9 @@ function initRepoIssueListCheckboxes() {
await updateIssuesMeta(url, action, issueIDs, elementId); await updateIssuesMeta(url, action, issueIDs, elementId);
window.location.reload(); window.location.reload();
} catch (err) { } catch (err) {
showErrorToast(err.responseJSON?.error ?? err.message); // FIXME: this logic (including updateIssuesMeta) is not right, should refactor to our JSONError framework
const e = err as {responseJSON?: {error: string}};
showErrorToast(e.responseJSON?.error ?? errorMessage(err));
} }
}, },
)); ));
@@ -2,6 +2,7 @@ import {fomanticQuery} from '../modules/fomantic/base.ts';
import {GET, POST} from '../modules/fetch.ts'; import {GET, POST} from '../modules/fetch.ts';
import {showErrorToast} from '../modules/toast.ts'; import {showErrorToast} from '../modules/toast.ts';
import {addDelegatedEventListener, queryElemChildren, queryElems, toggleElem} from '../utils/dom.ts'; import {addDelegatedEventListener, queryElemChildren, queryElems, toggleElem} from '../utils/dom.ts';
import {errorMessage} from '../modules/errors.ts';
import {parseDom} from '../utils.ts'; import {parseDom} from '../utils.ts';
export function syncIssueMainContentTimelineItems(oldMainContent: Element, newMainContent: Element) { export function syncIssueMainContentTimelineItems(oldMainContent: Element, newMainContent: Element) {
@@ -42,7 +43,7 @@ export class IssueSidebarComboList {
elDropdown: HTMLElement; elDropdown: HTMLElement;
elList: HTMLElement | null; elList: HTMLElement | null;
elComboValue: HTMLInputElement; elComboValue: HTMLInputElement;
initialValues: string[]; initialValues: string[] = [];
container: HTMLElement; container: HTMLElement;
elIssueMainContent: HTMLElement; elIssueMainContent: HTMLElement;
@@ -129,7 +130,7 @@ export class IssueSidebarComboList {
await this.reloadPagePartially(); await this.reloadPagePartially();
} catch (e) { } catch (e) {
console.error('Failed to update to backend', e); console.error('Failed to update to backend', e);
showErrorToast(`Failed to update to backend: ${e}`); showErrorToast(`Failed to update to backend: ${errorMessage(e)}`);
} finally { } finally {
this.elIssueSidebar.classList.remove('is-loading'); this.elIssueSidebar.classList.remove('is-loading');
} }
+2 -1
View File
@@ -1,3 +1,4 @@
import {errorMessage} from '../modules/errors.ts';
import {htmlEscape} from '../utils/html.ts'; import {htmlEscape} from '../utils/html.ts';
import {createTippy} from '../modules/tippy.ts'; import {createTippy} from '../modules/tippy.ts';
import { import {
@@ -425,7 +426,7 @@ export function initRepoIssueTitleEdit() {
window.location.reload(); window.location.reload();
} catch (error) { } catch (error) {
console.error(error); console.error(error);
showErrorToast(error.message); showErrorToast(errorMessage(error));
} }
}); });
} }
@@ -2,6 +2,7 @@ import {createSortable} from '../modules/sortable.ts';
import {POST} from '../modules/fetch.ts'; import {POST} from '../modules/fetch.ts';
import {showErrorToast} from '../modules/toast.ts'; import {showErrorToast} from '../modules/toast.ts';
import {queryElemChildren} from '../utils/dom.ts'; import {queryElemChildren} from '../utils/dom.ts';
import {errorMessage} from '../modules/errors.ts';
export function initRepoSettingsBranchesDrag() { export function initRepoSettingsBranchesDrag() {
const protectedBranchesList = document.querySelector<HTMLElement>('#protected-branches-list'); const protectedBranchesList = document.querySelector<HTMLElement>('#protected-branches-list');
@@ -23,8 +24,7 @@ export function initRepoSettingsBranchesDrag() {
}, },
}); });
} catch (err) { } catch (err) {
const errorMessage = String(err); showErrorToast(`Failed to update branch protection rule priority: ${errorMessage(err)}`);
showErrorToast(`Failed to update branch protection rule priority:, error: ${errorMessage}`);
} }
})(); })();
}, },
+5 -4
View File
@@ -1,5 +1,6 @@
import {encodeURLEncodedBase64, decodeURLEncodedBase64} from '../utils.ts'; import {encodeURLEncodedBase64, decodeURLEncodedBase64} from '../utils.ts';
import {hideElem, showElem} from '../utils/dom.ts'; import {hideElem, showElem} from '../utils/dom.ts';
import {errorMessage} from '../modules/errors.ts';
import {GET, POST} from '../modules/fetch.ts'; import {GET, POST} from '../modules/fetch.ts';
const {appSubUrl} = window.config; const {appSubUrl} = window.config;
@@ -80,7 +81,7 @@ async function loginPasskey() {
window.location.href = reply?.redirect ?? `${appSubUrl}/`; window.location.href = reply?.redirect ?? `${appSubUrl}/`;
} catch (err) { } catch (err) {
webAuthnError('general', err.message); webAuthnError('general', errorMessage(err));
} }
} }
@@ -104,7 +105,7 @@ async function login2FA() {
await verifyAssertion(credential); await verifyAssertion(credential);
} catch (err) { } catch (err) {
if (!options.publicKey.extensions?.appid) { if (!options.publicKey.extensions?.appid) {
webAuthnError('general', err.message); webAuthnError('general', errorMessage(err));
return; return;
} }
delete options.publicKey.extensions.appid; delete options.publicKey.extensions.appid;
@@ -114,7 +115,7 @@ async function login2FA() {
}); });
await verifyAssertion(credential); await verifyAssertion(credential);
} catch (err) { } catch (err) {
webAuthnError('general', err.message); webAuthnError('general', errorMessage(err));
} }
} }
} }
@@ -262,6 +263,6 @@ async function webAuthnRegisterRequest() {
}); });
await webauthnRegistered(credential); await webauthnRegistered(credential);
} catch (err) { } catch (err) {
webAuthnError('unknown', err); webAuthnError('unknown', errorMessage(err));
} }
} }
+4 -2
View File
@@ -1,8 +1,10 @@
export function displayError(el: Element, err: Error): void { import {errorMessage} from '../modules/errors.ts';
export function displayError(el: Element, err: unknown): void {
el.classList.remove('is-loading'); el.classList.remove('is-loading');
const errorNode = document.createElement('pre'); const errorNode = document.createElement('pre');
errorNode.setAttribute('class', 'ui message error markup-block-error'); errorNode.setAttribute('class', 'ui message error markup-block-error');
errorNode.textContent = err.message || String(err); errorNode.textContent = errorMessage(err);
el.before(errorNode); el.before(errorNode);
el.setAttribute('data-render-done', 'true'); el.setAttribute('data-render-done', 'true');
} }
+2 -1
View File
@@ -1,4 +1,5 @@
import {generateElemId} from '../utils/dom.ts'; import {generateElemId} from '../utils/dom.ts';
import {errorMessage} from '../modules/errors.ts';
import {isDarkTheme} from '../utils.ts'; import {isDarkTheme} from '../utils.ts';
import {GET} from '../modules/fetch.ts'; import {GET} from '../modules/fetch.ts';
@@ -11,7 +12,7 @@ function safeRenderIframeLink(link: any): string | null {
} }
return url.href; return url.href;
} catch (e) { } catch (e) {
console.error(`Failed to parse link: ${link}, error: ${e}`); console.error(`Failed to parse link: ${link}, error: ${errorMessage(e)}`);
return null; return null;
} }
} }
+4
View File
@@ -2,6 +2,10 @@
import {html} from '../utils/html.ts'; import {html} from '../utils/html.ts';
import type {Intent} from '../types.ts'; import type {Intent} from '../types.ts';
export function errorMessage(err: unknown): string {
return (err as Error)?.message || String(err);
}
export function showGlobalErrorMessage(msg: string, msgType: Intent = 'error') { export function showGlobalErrorMessage(msg: string, msgType: Intent = 'error') {
const msgContainer = document.querySelector('.page-content') ?? document.body; const msgContainer = document.querySelector('.page-content') ?? document.body;
if (!msgContainer) { if (!msgContainer) {
+2 -1
View File
@@ -2,6 +2,7 @@ import emojis from '../../../assets/emoji.json' with {type: 'json'};
import {GET} from '../modules/fetch.ts'; import {GET} from '../modules/fetch.ts';
import {showErrorToast} from '../modules/toast.ts'; import {showErrorToast} from '../modules/toast.ts';
import {parseIssuePageInfo} from '../utils.ts'; import {parseIssuePageInfo} from '../utils.ts';
import {errorMessage} from '../modules/errors.ts';
import type {Issue, Mention} from '../types.ts'; import type {Issue, Mention} from '../types.ts';
const maxMatches = 6; const maxMatches = 6;
@@ -47,7 +48,7 @@ export function fetchMentions(mentionsUrl: string): Promise<Mention[]> {
if (!res.ok) throw new Error(res.statusText); if (!res.ok) throw new Error(res.statusText);
return await res.json() as Mention[]; return await res.json() as Mention[];
} catch (e) { } catch (e) {
showErrorToast(`Failed to load mentions: ${e}`); showErrorToast(`Failed to load mentions: ${errorMessage(e)}`);
return []; return [];
} }
})(); })();
+8 -8
View File
@@ -3,13 +3,13 @@ import {addDelegatedEventListener, generateElemId, isDocumentFragmentOrElementNo
import octiconKebabHorizontal from '../../../public/assets/img/svg/octicon-kebab-horizontal.svg'; import octiconKebabHorizontal from '../../../public/assets/img/svg/octicon-kebab-horizontal.svg';
window.customElements.define('overflow-menu', class extends HTMLElement { window.customElements.define('overflow-menu', class extends HTMLElement {
popup: HTMLDivElement; popup!: HTMLDivElement;
overflowItems: Array<HTMLElement>; overflowItems: Array<HTMLElement> = [];
button: HTMLButtonElement | null; button: HTMLButtonElement | null = null;
menuItemsEl: HTMLElement; menuItemsEl!: HTMLElement;
resizeObserver: ResizeObserver; resizeObserver!: ResizeObserver;
mutationObserver: MutationObserver; mutationObserver!: MutationObserver;
lastWidth: number; lastWidth!: number;
updateButtonActivationState() { updateButtonActivationState() {
if (!this.button || !this.popup) return; if (!this.button || !this.popup) return;
@@ -100,7 +100,7 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
const itemOverFlowMenuButton = this.querySelector<HTMLButtonElement>('.overflow-menu-button'); const itemOverFlowMenuButton = this.querySelector<HTMLButtonElement>('.overflow-menu-button');
// move items in popup back into the menu items for subsequent measurement // move items in popup back into the menu items for subsequent measurement
for (const item of this.overflowItems || []) { for (const item of this.overflowItems) {
if (!itemFlexSpace || item.getAttribute('data-after-flex-space')) { if (!itemFlexSpace || item.getAttribute('data-after-flex-space')) {
this.menuItemsEl.append(item); this.menuItemsEl.append(item);
} else { } else {