From 40426addfa15226c72386346ee08d1577d00b263 Mon Sep 17 00:00:00 2001 From: Kerwin Bryant Date: Thu, 6 Feb 2025 21:07:44 +0800 Subject: [PATCH] Add cropping support when modifying the user/org/repo avatar (#33498) Fixed #33321 --------- Co-authored-by: wxiaoguang --- templates/admin/user/edit.tmpl | 3 +-- templates/org/settings/options.tmpl | 4 +--- templates/repo/settings/options.tmpl | 3 +-- templates/shared/avatar_upload_crop.tmpl | 8 ++++++++ templates/user/settings/profile.tmpl | 8 +------- web_src/css/features/cropper.css | 2 +- web_src/js/features/admin/common.ts | 5 ++++- web_src/js/features/common-organization.ts | 5 ++++- web_src/js/features/comp/Cropper.ts | 9 ++++++++- web_src/js/features/repo-settings.ts | 3 +++ web_src/js/features/user-settings.ts | 13 +++---------- 11 files changed, 35 insertions(+), 28 deletions(-) create mode 100644 templates/shared/avatar_upload_crop.tmpl diff --git a/templates/admin/user/edit.tmpl b/templates/admin/user/edit.tmpl index d591a645d8..c04d332660 100644 --- a/templates/admin/user/edit.tmpl +++ b/templates/admin/user/edit.tmpl @@ -195,8 +195,7 @@
- - + {{template "shared/avatar_upload_crop" dict "LabelText" (ctx.Locale.Tr "settings.choose_new_avatar")}}
diff --git a/templates/org/settings/options.tmpl b/templates/org/settings/options.tmpl index 3b817d068b..76315f3eac 100644 --- a/templates/org/settings/options.tmpl +++ b/templates/org/settings/options.tmpl @@ -89,10 +89,8 @@
{{.CsrfTokenHtml}}
- - + {{template "shared/avatar_upload_crop" dict "LabelText" (ctx.Locale.Tr "settings.choose_new_avatar")}}
-
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index cb596f013b..0520c87cc1 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -40,8 +40,7 @@ {{.CsrfTokenHtml}}
- - + {{template "shared/avatar_upload_crop" dict "LabelText" (ctx.Locale.Tr "settings.choose_new_avatar")}}
diff --git a/templates/shared/avatar_upload_crop.tmpl b/templates/shared/avatar_upload_crop.tmpl new file mode 100644 index 0000000000..2c4166fa9c --- /dev/null +++ b/templates/shared/avatar_upload_crop.tmpl @@ -0,0 +1,8 @@ +{{- /* we do not need to set for/id here, global aria init code will add them automatically */ -}} + + +{{- /* the cropper-panel must be next sibling of the input "avatar" */ -}} +
+
{{ctx.Locale.Tr "settings.cropper_prompt"}}
+
+
diff --git a/templates/user/settings/profile.tmpl b/templates/user/settings/profile.tmpl index 197763425c..03c3c18f28 100644 --- a/templates/user/settings/profile.tmpl +++ b/templates/user/settings/profile.tmpl @@ -124,13 +124,7 @@
- - -
- -
-
{{ctx.Locale.Tr "settings.cropper_prompt"}}
-
+ {{template "shared/avatar_upload_crop" dict "LabelText" (ctx.Locale.Tr "settings.choose_new_avatar")}}
diff --git a/web_src/css/features/cropper.css b/web_src/css/features/cropper.css index ed7171e770..f7f8168006 100644 --- a/web_src/css/features/cropper.css +++ b/web_src/css/features/cropper.css @@ -1,6 +1,6 @@ @import "cropperjs/dist/cropper.css"; -.page-content.user.profile .cropper-panel .cropper-wrapper { +.avatar-file-with-cropper + .cropper-panel .cropper-wrapper { max-width: 400px; max-height: 400px; } diff --git a/web_src/js/features/admin/common.ts b/web_src/js/features/admin/common.ts index b991749d81..14a49af81e 100644 --- a/web_src/js/features/admin/common.ts +++ b/web_src/js/features/admin/common.ts @@ -1,7 +1,8 @@ import $ from 'jquery'; import {checkAppUrl} from '../common-page.ts'; -import {hideElem, showElem, toggleElem} from '../../utils/dom.ts'; +import {hideElem, queryElems, showElem, toggleElem} from '../../utils/dom.ts'; import {POST} from '../../modules/fetch.ts'; +import {initAvatarUploaderWithCropper} from '../comp/Cropper.ts'; const {appSubUrl} = window.config; @@ -258,4 +259,6 @@ export function initAdminCommon(): void { window.location.href = this.getAttribute('data-redirect'); }); } + + queryElems(document, '.avatar-file-with-cropper', initAvatarUploaderWithCropper); } diff --git a/web_src/js/features/common-organization.ts b/web_src/js/features/common-organization.ts index a1f19bedea..9d5964c4c7 100644 --- a/web_src/js/features/common-organization.ts +++ b/web_src/js/features/common-organization.ts @@ -1,5 +1,6 @@ import {initCompLabelEdit} from './comp/LabelEdit.ts'; -import {toggleElem} from '../utils/dom.ts'; +import {queryElems, toggleElem} from '../utils/dom.ts'; +import {initAvatarUploaderWithCropper} from './comp/Cropper.ts'; export function initCommonOrganization() { if (!document.querySelectorAll('.organization').length) { @@ -13,4 +14,6 @@ export function initCommonOrganization() { // Labels initCompLabelEdit('.page-content.organization.settings.labels'); + + queryElems(document, '.avatar-file-with-cropper', initAvatarUploaderWithCropper); } diff --git a/web_src/js/features/comp/Cropper.ts b/web_src/js/features/comp/Cropper.ts index e65dcfbe13..aaa1691152 100644 --- a/web_src/js/features/comp/Cropper.ts +++ b/web_src/js/features/comp/Cropper.ts @@ -6,7 +6,7 @@ type CropperOpts = { fileInput: HTMLInputElement, } -export async function initCompCropper({container, fileInput, imageSource}: CropperOpts) { +async function initCompCropper({container, fileInput, imageSource}: CropperOpts) { const {default: Cropper} = await import(/* webpackChunkName: "cropperjs" */'cropperjs'); let currentFileName = ''; let currentFileLastModified = 0; @@ -38,3 +38,10 @@ export async function initCompCropper({container, fileInput, imageSource}: Cropp } }); } + +export async function initAvatarUploaderWithCropper(fileInput: HTMLInputElement) { + const panel = fileInput.nextElementSibling as HTMLElement; + if (!panel?.matches('.cropper-panel')) throw new Error('Missing cropper panel for avatar uploader'); + const imageSource = panel.querySelector('.cropper-source'); + await initCompCropper({container: panel, fileInput, imageSource}); +} diff --git a/web_src/js/features/repo-settings.ts b/web_src/js/features/repo-settings.ts index b61ef9a153..7e890a43e0 100644 --- a/web_src/js/features/repo-settings.ts +++ b/web_src/js/features/repo-settings.ts @@ -3,6 +3,7 @@ import {minimatch} from 'minimatch'; import {createMonaco} from './codeeditor.ts'; import {onInputDebounce, queryElems, toggleElem} from '../utils/dom.ts'; import {POST} from '../modules/fetch.ts'; +import {initAvatarUploaderWithCropper} from './comp/Cropper.ts'; import {initRepoSettingsBranchesDrag} from './repo-settings-branches.ts'; const {appSubUrl, csrfToken} = window.config; @@ -156,4 +157,6 @@ export function initRepoSettings() { initRepoSettingsSearchTeamBox(); initRepoSettingsGitHook(); initRepoSettingsBranchesDrag(); + + queryElems(document, '.avatar-file-with-cropper', initAvatarUploaderWithCropper); } diff --git a/web_src/js/features/user-settings.ts b/web_src/js/features/user-settings.ts index 6312a8b682..21d20e676f 100644 --- a/web_src/js/features/user-settings.ts +++ b/web_src/js/features/user-settings.ts @@ -1,17 +1,10 @@ -import {hideElem, showElem} from '../utils/dom.ts'; -import {initCompCropper} from './comp/Cropper.ts'; - -function initUserSettingsAvatarCropper() { - const fileInput = document.querySelector('#new-avatar'); - const container = document.querySelector('.user.settings.profile .cropper-panel'); - const imageSource = container.querySelector('.cropper-source'); - initCompCropper({container, fileInput, imageSource}); -} +import {hideElem, queryElems, showElem} from '../utils/dom.ts'; +import {initAvatarUploaderWithCropper} from './comp/Cropper.ts'; export function initUserSettings() { if (!document.querySelector('.user.settings.profile')) return; - initUserSettingsAvatarCropper(); + queryElems(document, '.avatar-file-with-cropper', initAvatarUploaderWithCropper); const usernameInput = document.querySelector('#username'); if (!usernameInput) return;