diff --git a/templates/base/footer.tmpl b/templates/base/footer.tmpl index fed426a469277..3af66e736990b 100644 --- a/templates/base/footer.tmpl +++ b/templates/base/footer.tmpl @@ -5,16 +5,10 @@
{{end}} - {{template "custom/body_inner_post" .}} - + {{template "custom/body_inner_post" .}}
- {{template "custom/body_outer_post" .}} - {{template "base/footer_content" .}} - - - {{template "custom/footer" .}} diff --git a/templates/base/head_script.tmpl b/templates/base/head_script.tmpl index 7c931e740494c..f6648b59d8c53 100644 --- a/templates/base/head_script.tmpl +++ b/templates/base/head_script.tmpl @@ -46,4 +46,4 @@ If you introduce mistakes in it, Gitea JavaScript code wouldn't run correctly. {{/* in case some pages don't render the pageData, we make sure it is an object to prevent null access */}} window.config.pageData = window.config.pageData || {}; - + diff --git a/web_src/js/bootstrap.ts b/web_src/js/bootstrap.ts index 96a2759a2397f..4d3f39f5bfa71 100644 --- a/web_src/js/bootstrap.ts +++ b/web_src/js/bootstrap.ts @@ -20,6 +20,10 @@ function shouldIgnoreError(err: Error) { export function showGlobalErrorMessage(msg: string, msgType: Intent = 'error') { const msgContainer = document.querySelector('.page-content') ?? document.body; + if (!msgContainer) { + alert(`${msgType}: ${msg}`); + return; + } const msgCompact = msg.replace(/\W/g, '').trim(); // compact the message to a data attribute to avoid too many duplicated messages let msgDiv = msgContainer.querySelector(`.js-global-error[data-global-error-msg-compact="${msgCompact}"]`); if (!msgDiv) { diff --git a/web_src/js/globals.d.ts b/web_src/js/globals.d.ts index e4b540122d350..00f1744a957b2 100644 --- a/web_src/js/globals.d.ts +++ b/web_src/js/globals.d.ts @@ -50,17 +50,12 @@ interface Element { _tippy: import('tippy.js').Instance; } -type Writable = { -readonly [K in keyof T]: T[K] }; - interface Window { __webpack_public_path__: string; config: import('./web_src/js/types.ts').Config; $: typeof import('@types/jquery'), jQuery: typeof import('@types/jquery'), - htmx: Omit & { - config?: Writable, - process?: (elt: Element | string) => void, - }, + htmx: typeof import('htmx.org').default, _globalHandlerErrors: Array & { _inited: boolean, push: (e: ErrorEvent & PromiseRejectionEvent) => void | number, diff --git a/web_src/js/globals.ts b/web_src/js/globals.ts index 24974da90e247..955515d25021c 100644 --- a/web_src/js/globals.ts +++ b/web_src/js/globals.ts @@ -1,5 +1,2 @@ import jquery from 'jquery'; -import htmx from 'htmx.org/dist/htmx.esm.js'; - -window.$ = window.jQuery = jquery; -window.htmx = htmx; +window.$ = window.jQuery = jquery; // only for Fomantic UI diff --git a/web_src/js/htmx.ts b/web_src/js/htmx.ts index c23c3a21fa21a..9d433dfd57fc2 100644 --- a/web_src/js/htmx.ts +++ b/web_src/js/htmx.ts @@ -1,21 +1,26 @@ -import {showErrorToast} from './modules/toast.ts'; +import htmx from 'htmx.org'; import 'idiomorph/htmx'; import type {HtmxResponseInfo} from 'htmx.org'; +import {showErrorToast} from './modules/toast.ts'; type HtmxEvent = Event & {detail: HtmxResponseInfo}; -// https://htmx.org/reference/#config -window.htmx.config.requestClass = 'is-loading'; -window.htmx.config.scrollIntoViewOnBoost = false; +export function initHtmx() { + window.htmx = htmx; + + // https://htmx.org/reference/#config + htmx.config.requestClass = 'is-loading'; + htmx.config.scrollIntoViewOnBoost = false; -// https://htmx.org/events/#htmx:sendError -document.body.addEventListener('htmx:sendError', (event: Partial) => { - // TODO: add translations - showErrorToast(`Network error when calling ${event.detail.requestConfig.path}`); -}); + // https://htmx.org/events/#htmx:sendError + document.body.addEventListener('htmx:sendError', (event: Partial) => { + // TODO: add translations + showErrorToast(`Network error when calling ${event.detail.requestConfig.path}`); + }); -// https://htmx.org/events/#htmx:responseError -document.body.addEventListener('htmx:responseError', (event: Partial) => { - // TODO: add translations - showErrorToast(`Error ${event.detail.xhr.status} when calling ${event.detail.requestConfig.path}`); -}); + // https://htmx.org/events/#htmx:responseError + document.body.addEventListener('htmx:responseError', (event: Partial) => { + // TODO: add translations + showErrorToast(`Error ${event.detail.xhr.status} when calling ${event.detail.requestConfig.path}`); + }); +} diff --git a/web_src/js/index-domready.ts b/web_src/js/index-domready.ts new file mode 100644 index 0000000000000..4d7ab98db0398 --- /dev/null +++ b/web_src/js/index-domready.ts @@ -0,0 +1,177 @@ +import './globals.ts'; +import '../fomantic/build/fomantic.js'; +import '../../node_modules/easymde/dist/easymde.min.css'; // TODO: lazy load in "switchToEasyMDE" + +import {initHtmx} from './htmx.ts'; +import {initDashboardRepoList} from './features/dashboard.ts'; +import {initGlobalCopyToClipboardListener} from './features/clipboard.ts'; +import {initContextPopups} from './features/contextpopup.ts'; +import {initRepoGraphGit} from './features/repo-graph.ts'; +import {initHeatmap} from './features/heatmap.ts'; +import {initImageDiff} from './features/imagediff.ts'; +import {initRepoMigration} from './features/repo-migration.ts'; +import {initRepoProject} from './features/repo-projects.ts'; +import {initTableSort} from './features/tablesort.ts'; +import {initAdminUserListSearchForm} from './features/admin/users.ts'; +import {initAdminConfigs} from './features/admin/config.ts'; +import {initMarkupAnchors} from './markup/anchors.ts'; +import {initNotificationCount, initNotificationsTable} from './features/notification.ts'; +import {initRepoIssueContentHistory} from './features/repo-issue-content.ts'; +import {initStopwatch} from './features/stopwatch.ts'; +import {initFindFileInRepo} from './features/repo-findfile.ts'; +import {initMarkupContent} from './markup/content.ts'; +import {initRepoFileView} from './features/file-view.ts'; +import {initUserAuthOauth2, initUserCheckAppUrl} from './features/user-auth.ts'; +import {initRepoPullRequestAllowMaintainerEdit, initRepoPullRequestReview, initRepoIssueSidebarDependency, initRepoIssueFilterItemLabel} from './features/repo-issue.ts'; +import {initRepoEllipsisButton, initCommitStatuses} from './features/repo-commit.ts'; +import {initRepoTopicBar} from './features/repo-home.ts'; +import {initAdminCommon} from './features/admin/common.ts'; +import {initRepoCodeView} from './features/repo-code.ts'; +import {initSshKeyFormParser} from './features/sshkey-helper.ts'; +import {initUserSettings} from './features/user-settings.ts'; +import {initRepoActivityTopAuthorsChart, initRepoArchiveLinks} from './features/repo-common.ts'; +import {initRepoMigrationStatusChecker} from './features/repo-migrate.ts'; +import {initRepoDiffView} from './features/repo-diff.ts'; +import {initOrgTeam} from './features/org-team.ts'; +import {initUserAuthWebAuthn, initUserAuthWebAuthnRegister} from './features/user-auth-webauthn.ts'; +import {initRepoRelease, initRepoReleaseNew} from './features/repo-release.ts'; +import {initRepoEditor} from './features/repo-editor.ts'; +import {initCompSearchUserBox} from './features/comp/SearchUserBox.ts'; +import {initInstall} from './features/install.ts'; +import {initCompWebHookEditor} from './features/comp/WebHookEditor.ts'; +import {initRepoBranchButton} from './features/repo-branch.ts'; +import {initCommonOrganization} from './features/common-organization.ts'; +import {initRepoWikiForm} from './features/repo-wiki.ts'; +import {initRepository, initBranchSelectorTabs} from './features/repo-legacy.ts'; +import {initCopyContent} from './features/copycontent.ts'; +import {initCaptcha} from './features/captcha.ts'; +import {initRepositoryActionView} from './features/repo-actions.ts'; +import {initGlobalTooltips} from './modules/tippy.ts'; +import {initGiteaFomantic} from './modules/fomantic.ts'; +import {initSubmitEventPolyfill} from './utils/dom.ts'; +import {initRepoIssueList} from './features/repo-issue-list.ts'; +import {initCommonIssueListQuickGoto} from './features/common-issue-list.ts'; +import {initRepoContributors} from './features/contributors.ts'; +import {initRepoCodeFrequency} from './features/code-frequency.ts'; +import {initRepoRecentCommits} from './features/recent-commits.ts'; +import {initRepoDiffCommitBranchesAndTags} from './features/repo-diff-commit.ts'; +import {initGlobalSelectorObserver} from './modules/observer.ts'; +import {initRepositorySearch} from './features/repo-search.ts'; +import {initColorPickers} from './features/colorpicker.ts'; +import {initAdminSelfCheck} from './features/admin/selfcheck.ts'; +import {initOAuth2SettingsDisableCheckbox} from './features/oauth2-settings.ts'; +import {initGlobalFetchAction} from './features/common-fetch-action.ts'; +import {initFootLanguageMenu, initGlobalAvatarUploader, initGlobalDropdown, initGlobalInput, initGlobalTabularMenu, initHeadNavbarContentToggle} from './features/common-page.ts'; +import {initGlobalButtonClickOnEnter, initGlobalButtons, initGlobalDeleteButton} from './features/common-button.ts'; +import {initGlobalComboMarkdownEditor, initGlobalEnterQuickSubmit, initGlobalFormDirtyLeaveConfirm} from './features/common-form.ts'; +import {callInitFunctions} from './modules/init.ts'; +import {initRepoViewFileTree} from './features/repo-view-file-tree.ts'; + +const initStartTime = performance.now(); +const initPerformanceTracer = callInitFunctions([ + initHtmx, + initSubmitEventPolyfill, + initGiteaFomantic, + + initGlobalAvatarUploader, + initGlobalDropdown, + initGlobalTabularMenu, + initGlobalFetchAction, + initGlobalTooltips, + initGlobalButtonClickOnEnter, + initGlobalButtons, + initGlobalCopyToClipboardListener, + initGlobalEnterQuickSubmit, + initGlobalFormDirtyLeaveConfirm, + initGlobalComboMarkdownEditor, + initGlobalDeleteButton, + initGlobalInput, + + initCommonOrganization, + initCommonIssueListQuickGoto, + + initCompSearchUserBox, + initCompWebHookEditor, + + initInstall, + + initHeadNavbarContentToggle, + initFootLanguageMenu, + + initContextPopups, + initHeatmap, + initImageDiff, + initMarkupAnchors, + initMarkupContent, + initSshKeyFormParser, + initStopwatch, + initTableSort, + initFindFileInRepo, + initCopyContent, + + initAdminCommon, + initAdminUserListSearchForm, + initAdminConfigs, + initAdminSelfCheck, + + initDashboardRepoList, + + initNotificationCount, + initNotificationsTable, + + initOrgTeam, + + initRepoActivityTopAuthorsChart, + initRepoArchiveLinks, + initRepoBranchButton, + initRepoCodeView, + initBranchSelectorTabs, + initRepoEllipsisButton, + initRepoDiffCommitBranchesAndTags, + initRepoEditor, + initRepoGraphGit, + initRepoIssueContentHistory, + initRepoIssueList, + initRepoIssueFilterItemLabel, + initRepoIssueSidebarDependency, + initRepoMigration, + initRepoMigrationStatusChecker, + initRepoProject, + initRepoPullRequestAllowMaintainerEdit, + initRepoPullRequestReview, + initRepoRelease, + initRepoReleaseNew, + initRepoTopicBar, + initRepoViewFileTree, + initRepoWikiForm, + initRepository, + initRepositoryActionView, + initRepositorySearch, + initRepoContributors, + initRepoCodeFrequency, + initRepoRecentCommits, + + initCommitStatuses, + initCaptcha, + + initUserCheckAppUrl, + initUserAuthOauth2, + initUserAuthWebAuthn, + initUserAuthWebAuthnRegister, + initUserSettings, + initRepoDiffView, + initColorPickers, + + initOAuth2SettingsDisableCheckbox, + + initRepoFileView, +]); + +// it must be the last one, then the "querySelectorAll" only needs to be executed once for global init functions. +initGlobalSelectorObserver(initPerformanceTracer); +if (initPerformanceTracer) initPerformanceTracer.printResults(); + +const initDur = performance.now() - initStartTime; +if (initDur > 500) { + console.error(`slow init functions took ${initDur.toFixed(3)}ms`); +} diff --git a/web_src/js/index.ts b/web_src/js/index.ts index 347aad270997c..e78b3cb64f3ba 100644 --- a/web_src/js/index.ts +++ b/web_src/js/index.ts @@ -1,177 +1,13 @@ // bootstrap module must be the first one to be imported, it handles webpack lazy-loading and global errors import './bootstrap.ts'; -import './htmx.ts'; +import './webcomponents/index.ts'; +import {onDomReady} from './utils/dom.ts'; -import {initDashboardRepoList} from './features/dashboard.ts'; -import {initGlobalCopyToClipboardListener} from './features/clipboard.ts'; -import {initContextPopups} from './features/contextpopup.ts'; -import {initRepoGraphGit} from './features/repo-graph.ts'; -import {initHeatmap} from './features/heatmap.ts'; -import {initImageDiff} from './features/imagediff.ts'; -import {initRepoMigration} from './features/repo-migration.ts'; -import {initRepoProject} from './features/repo-projects.ts'; -import {initTableSort} from './features/tablesort.ts'; -import {initAdminUserListSearchForm} from './features/admin/users.ts'; -import {initAdminConfigs} from './features/admin/config.ts'; -import {initMarkupAnchors} from './markup/anchors.ts'; -import {initNotificationCount, initNotificationsTable} from './features/notification.ts'; -import {initRepoIssueContentHistory} from './features/repo-issue-content.ts'; -import {initStopwatch} from './features/stopwatch.ts'; -import {initFindFileInRepo} from './features/repo-findfile.ts'; -import {initMarkupContent} from './markup/content.ts'; -import {initRepoFileView} from './features/file-view.ts'; -import {initUserAuthOauth2, initUserCheckAppUrl} from './features/user-auth.ts'; -import {initRepoPullRequestAllowMaintainerEdit, initRepoPullRequestReview, initRepoIssueSidebarDependency, initRepoIssueFilterItemLabel} from './features/repo-issue.ts'; -import {initRepoEllipsisButton, initCommitStatuses} from './features/repo-commit.ts'; -import {initRepoTopicBar} from './features/repo-home.ts'; -import {initAdminCommon} from './features/admin/common.ts'; -import {initRepoCodeView} from './features/repo-code.ts'; -import {initSshKeyFormParser} from './features/sshkey-helper.ts'; -import {initUserSettings} from './features/user-settings.ts'; -import {initRepoActivityTopAuthorsChart, initRepoArchiveLinks} from './features/repo-common.ts'; -import {initRepoMigrationStatusChecker} from './features/repo-migrate.ts'; -import {initRepoDiffView} from './features/repo-diff.ts'; -import {initOrgTeam} from './features/org-team.ts'; -import {initUserAuthWebAuthn, initUserAuthWebAuthnRegister} from './features/user-auth-webauthn.ts'; -import {initRepoRelease, initRepoReleaseNew} from './features/repo-release.ts'; -import {initRepoEditor} from './features/repo-editor.ts'; -import {initCompSearchUserBox} from './features/comp/SearchUserBox.ts'; -import {initInstall} from './features/install.ts'; -import {initCompWebHookEditor} from './features/comp/WebHookEditor.ts'; -import {initRepoBranchButton} from './features/repo-branch.ts'; -import {initCommonOrganization} from './features/common-organization.ts'; -import {initRepoWikiForm} from './features/repo-wiki.ts'; -import {initRepository, initBranchSelectorTabs} from './features/repo-legacy.ts'; -import {initCopyContent} from './features/copycontent.ts'; -import {initCaptcha} from './features/captcha.ts'; -import {initRepositoryActionView} from './features/repo-actions.ts'; -import {initGlobalTooltips} from './modules/tippy.ts'; -import {initGiteaFomantic} from './modules/fomantic.ts'; -import {initSubmitEventPolyfill, onDomReady} from './utils/dom.ts'; -import {initRepoIssueList} from './features/repo-issue-list.ts'; -import {initCommonIssueListQuickGoto} from './features/common-issue-list.ts'; -import {initRepoContributors} from './features/contributors.ts'; -import {initRepoCodeFrequency} from './features/code-frequency.ts'; -import {initRepoRecentCommits} from './features/recent-commits.ts'; -import {initRepoDiffCommitBranchesAndTags} from './features/repo-diff-commit.ts'; -import {initGlobalSelectorObserver} from './modules/observer.ts'; -import {initRepositorySearch} from './features/repo-search.ts'; -import {initColorPickers} from './features/colorpicker.ts'; -import {initAdminSelfCheck} from './features/admin/selfcheck.ts'; -import {initOAuth2SettingsDisableCheckbox} from './features/oauth2-settings.ts'; -import {initGlobalFetchAction} from './features/common-fetch-action.ts'; -import {initFootLanguageMenu, initGlobalAvatarUploader, initGlobalDropdown, initGlobalInput, initGlobalTabularMenu, initHeadNavbarContentToggle} from './features/common-page.ts'; -import {initGlobalButtonClickOnEnter, initGlobalButtons, initGlobalDeleteButton} from './features/common-button.ts'; -import {initGlobalComboMarkdownEditor, initGlobalEnterQuickSubmit, initGlobalFormDirtyLeaveConfirm} from './features/common-form.ts'; -import {callInitFunctions} from './modules/init.ts'; -import {initRepoViewFileTree} from './features/repo-view-file-tree.ts'; +// TODO: There is a bug in htmx, it incorrectly checks "readyState === 'complete'" when the DOM tree is ready and won't trigger DOMContentLoaded +// Then importing the htmx in our onDomReady will make htmx skip its initialization. +// If the bug would be fixed (https://github.com/bigskysoftware/htmx/pull/3365), then we can only import htmx in "onDomReady" +import 'htmx.org'; -initGiteaFomantic(); -initSubmitEventPolyfill(); - -onDomReady(() => { - const initStartTime = performance.now(); - const initPerformanceTracer = callInitFunctions([ - initGlobalAvatarUploader, - initGlobalDropdown, - initGlobalTabularMenu, - initGlobalFetchAction, - initGlobalTooltips, - initGlobalButtonClickOnEnter, - initGlobalButtons, - initGlobalCopyToClipboardListener, - initGlobalEnterQuickSubmit, - initGlobalFormDirtyLeaveConfirm, - initGlobalComboMarkdownEditor, - initGlobalDeleteButton, - initGlobalInput, - - initCommonOrganization, - initCommonIssueListQuickGoto, - - initCompSearchUserBox, - initCompWebHookEditor, - - initInstall, - - initHeadNavbarContentToggle, - initFootLanguageMenu, - - initContextPopups, - initHeatmap, - initImageDiff, - initMarkupAnchors, - initMarkupContent, - initSshKeyFormParser, - initStopwatch, - initTableSort, - initFindFileInRepo, - initCopyContent, - - initAdminCommon, - initAdminUserListSearchForm, - initAdminConfigs, - initAdminSelfCheck, - - initDashboardRepoList, - - initNotificationCount, - initNotificationsTable, - - initOrgTeam, - - initRepoActivityTopAuthorsChart, - initRepoArchiveLinks, - initRepoBranchButton, - initRepoCodeView, - initBranchSelectorTabs, - initRepoEllipsisButton, - initRepoDiffCommitBranchesAndTags, - initRepoEditor, - initRepoGraphGit, - initRepoIssueContentHistory, - initRepoIssueList, - initRepoIssueFilterItemLabel, - initRepoIssueSidebarDependency, - initRepoMigration, - initRepoMigrationStatusChecker, - initRepoProject, - initRepoPullRequestAllowMaintainerEdit, - initRepoPullRequestReview, - initRepoRelease, - initRepoReleaseNew, - initRepoTopicBar, - initRepoViewFileTree, - initRepoWikiForm, - initRepository, - initRepositoryActionView, - initRepositorySearch, - initRepoContributors, - initRepoCodeFrequency, - initRepoRecentCommits, - - initCommitStatuses, - initCaptcha, - - initUserCheckAppUrl, - initUserAuthOauth2, - initUserAuthWebAuthn, - initUserAuthWebAuthnRegister, - initUserSettings, - initRepoDiffView, - initColorPickers, - - initOAuth2SettingsDisableCheckbox, - - initRepoFileView, - ]); - - // it must be the last one, then the "querySelectorAll" only needs to be executed once for global init functions. - initGlobalSelectorObserver(initPerformanceTracer); - if (initPerformanceTracer) initPerformanceTracer.printResults(); - - const initDur = performance.now() - initStartTime; - if (initDur > 500) { - console.error(`slow init functions took ${initDur.toFixed(3)}ms`); - } +onDomReady(async () => { + await import(/* webpackChunkName: "index-domready" */'./index-domready.ts'); }); diff --git a/webpack.config.js b/webpack.config.js index 931bf67071700..92f479bc0ce9c 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -76,16 +76,10 @@ export default { mode: isProduction ? 'production' : 'development', entry: { index: [ - fileURLToPath(new URL('web_src/js/globals.ts', import.meta.url)), - fileURLToPath(new URL('web_src/fomantic/build/fomantic.js', import.meta.url)), fileURLToPath(new URL('web_src/js/index.ts', import.meta.url)), - fileURLToPath(new URL('node_modules/easymde/dist/easymde.min.css', import.meta.url)), fileURLToPath(new URL('web_src/fomantic/build/fomantic.css', import.meta.url)), fileURLToPath(new URL('web_src/css/index.css', import.meta.url)), ], - webcomponents: [ - fileURLToPath(new URL('web_src/js/webcomponents/index.ts', import.meta.url)), - ], swagger: [ fileURLToPath(new URL('web_src/js/standalone/swagger.ts', import.meta.url)), fileURLToPath(new URL('web_src/css/standalone/swagger.css', import.meta.url)),