[Desktop] Custom profile image manageProfile integration - #39432
[Desktop] Custom profile image manageProfile integration#39432chrislacy wants to merge 21 commits into
manageProfile integration#39432Conversation
- handle image validation, preview replacement, and object URL cleanup - keep strings and feature state owned by consumers - package the row only when custom profile image WebUI support is enabled
- insert the feature-gated row between the theme and avatar pickers - supply labels through generated BraveSettingsStrings - cover feature state, image selection, and preview lifecycle in Settings tests
- reuse the upload label as hover text for the icon button - verify the tooltip matches its accessible name in Settings tests
📋 Code Owners Summary13 file(s) changed, 3 with assigned owners 2 team(s) affected: Owners and Their Files
|
- add custom profile image replace tooltip - remove unused custom profile image preview label
fallaciousreasoning
left a comment
There was a problem hiding this comment.
hey looking pretty good. I've left a few comments. One thing I was wondering is whether we need the separate remove/replace state. Could we just leave it as an upload custom image button and let the user pick one of the default profiles?
|
A Storybook has been deployed to preview UI for the latest push |
Co-authored-by: Jay Harris <jay.harris@outlook.co.nz>
Compare the two section parents directly, then use the shared parent to insert the custom image section.
Replace Chromium styling usage with corresponding Leo items.
Compute the row state from `localPreviewUrl_` so upload/removal paths don't maintain duplicate state.
- keep the title in the DOM for `aria-labelledby` - let the reflected hide-title attribute control its layout and visibility
- resolve labels and tooltips in Lit template so consumers don't need localization-only properties - update Settings test to verify rendered localized content
- use reactive `localPreviewUrl_` as the single source of truth - remove the public state API and unused `state-*` class - centralize dynamic upload/replace strings
- share preview styles through `#preview` - size the selected indicator with `--leo-icon-size` - remove unused button-specific styling
- add the Lit template replacement markers
- replace the static selected-preview label with `$i18n{...}`
This reverts commit 9f86316.
|
(Note: the most recent round of feedback came via commit comments. I won't reply to these commit comments as I'm unsure if they will survive a potential future branch rebase) |
|
I tried the suggested It could be an issue with my implementation, but the placeholder was not expanded at runtime. The literal
I reverted this change with the subsequent 42727e5. |
fallaciousreasoning
left a comment
There was a problem hiding this comment.
lgtm % nits and a followup
| async function settleRow(row: BrCustomProfileImageRowElement) { | ||
| await microtasksFinished() | ||
| await row.updateComplete | ||
| await microtasksFinished() | ||
| await row.updateComplete | ||
| } |
There was a problem hiding this comment.
any chance we could give ourselves an event we can wait for instead? If not, then maybe a comment about why we need to wait twice?
| createdUrls = [] | ||
| revokedUrls = [] | ||
| originalCreateObjectUrl = URL.createObjectURL | ||
| originalRevokeObjectUrl = URL.revokeObjectURL | ||
| originalDecode = HTMLImageElement.prototype.decode | ||
| URL.createObjectURL = () => { | ||
| const url = `blob:custom-profile-image-${createdUrls.length + 1}` | ||
| createdUrls.push(url) | ||
| return url | ||
| } | ||
| URL.revokeObjectURL = (url: string) => revokedUrls.push(url) | ||
| HTMLImageElement.prototype.decode = () => Promise.resolve() |
There was a problem hiding this comment.
do we need all these mocks?
| assertDeepEquals(createdUrls, revokedUrls) | ||
| }) | ||
|
|
||
| test('DisconnectWhileDecodePendingRevokesUrl', async function() { |
There was a problem hiding this comment.
I think we don't need to stress too much about testing we revoke the urls. Probably safe to remove a bunch of these.
| protected onUploadClick_() { | ||
| this.shadowRoot.querySelector<HTMLInputElement>('#fileInput')!.click() | ||
| } |
There was a problem hiding this comment.
I think you can declare a $ property on the interface with an HTMLInputLEement on it and Lit will automatically wire it up.
Have a look at ui/webui/resources/cr_elements/cr_button/cr_button.ts for an example
| } | ||
|
|
||
| private clearLocalPreview_() { | ||
| ++this.uploadAttemptId_ |
There was a problem hiding this comment.
do we need to increment here? Won't it get a new id when you do a new upload?
| } | ||
|
|
||
| if (!this.isConnected || uploadAttemptId !== this.uploadAttemptId_) { | ||
| URL.revokeObjectURL(previewUrl) |
There was a problem hiding this comment.
super optional, but if you wanted you could play around with using here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using
| ] | ||
| } | ||
|
|
||
| buildflag_header("custom_profile_image_buildflags") { |
There was a problem hiding this comment.
I might've lead you astray here. Thinking about it, I think we should have a
//brave/ui/webui/custom_profile_image/buildflags/buildflags.gni for declaring the buildflags and a BUILD.gn in that folder that does this.
Can happen in a separate PR - sorry about that.
manageProfile integration

manageProfilecustom profile image selection UI brave-browser#58372Screenshots
Screenshot_2026-08-27.at.12.01.32.mp4
String Usage
IDS_CUSTOM_PROFILE_IMAGE_TITLEIDS_CUSTOM_PROFILE_IMAGE_UPLOAD_ACTIONIDS_CUSTOM_PROFILE_IMAGE_UPLOAD_TOOLTIP:IDS_CUSTOM_PROFILE_IMAGE_REPLACE_ACTIONIDS_CUSTOM_PROFILE_IMAGE_REPLACE_TOOLTIP:IDS_CUSTOM_PROFILE_IMAGE_REMOVE_ACTIONIDS_CUSTOM_PROFILE_IMAGE_REMOVE_TOOLTIP:IDS_CUSTOM_PROFILE_IMAGE_INVALID_IMAGE:IDS_CUSTOM_PROFILE_IMAGE_SELECTED_PREVIEW_LABEL: