Skip to content

[Desktop] Custom profile image manageProfile integration - #39432

Open
chrislacy wants to merge 21 commits into
masterfrom
desktop/custom-profile-image/manage-profile-integration
Open

[Desktop] Custom profile image manageProfile integration#39432
chrislacy wants to merge 21 commits into
masterfrom
desktop/custom-profile-image/manage-profile-integration

Conversation

@chrislacy

@chrislacy chrislacy commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Screenshots

Screenshot_2026-08-27.at.12.01.32.mp4

String Usage

IDS_CUSTOM_PROFILE_IMAGE_TITLE
IDS_CUSTOM_PROFILE_IMAGE_UPLOAD_ACTION
IDS_CUSTOM_PROFILE_IMAGE_UPLOAD_TOOLTIP:
Screenshot_2026-08-27 at 06 26 18

IDS_CUSTOM_PROFILE_IMAGE_REPLACE_ACTION
IDS_CUSTOM_PROFILE_IMAGE_REPLACE_TOOLTIP:
Screenshot_2026-08-27 at 12 42 20

IDS_CUSTOM_PROFILE_IMAGE_REMOVE_ACTION
IDS_CUSTOM_PROFILE_IMAGE_REMOVE_TOOLTIP:
Screenshot_2026-08-27 at 06 27 27

IDS_CUSTOM_PROFILE_IMAGE_INVALID_IMAGE:
Screenshot_2026-08-27 at 06 28 01

IDS_CUSTOM_PROFILE_IMAGE_SELECTED_PREVIEW_LABEL:
Screenshot_2026-08-27 at 12 45 22

- 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
@chrislacy chrislacy self-assigned this Aug 27, 2026
@chrislacy chrislacy added CI/skip-android Do not run CI builds for Android CI/skip-ios Do not run CI builds for iOS CI/skip-origin Do not run CI builds for Origin labels Aug 27, 2026
@github-actions github-actions Bot added the CI/storybook-url Deploy storybook and provide a unique URL for each build label Aug 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📋 Code Owners Summary

13 file(s) changed, 3 with assigned owners

2 team(s) affected: @brave/chromium-src-web-reviewers, @brave/string-reviewers-team


Owners and Their Files

@brave/string-reviewers-team — 2 file(s)

@brave/chromium-src-web-reviewers — 1 file(s)

- add custom profile image replace tooltip
- remove unused custom profile image preview label
@chrislacy
chrislacy marked this pull request as ready for review August 27, 2026 02:47
@chrislacy
chrislacy requested review from a team as code owners August 27, 2026 02:47

@fallaciousreasoning fallaciousreasoning left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Comment thread browser/resources/settings/br/settings_manage_profile.ts Outdated
Comment thread app/custom_profile_image_strings.grdp Outdated
Comment thread browser/resources/settings/br/settings_manage_profile.ts
Comment thread browser/resources/settings/br/settings_manage_profile.ts Outdated
Comment thread browser/resources/settings/br/settings_manage_profile.ts Outdated
Comment thread ui/webui/resources/custom_profile_image_row.html.ts Outdated
Comment thread ui/webui/resources/custom_profile_image_row.html.ts Outdated
Comment thread ui/webui/resources/custom_profile_image_row.ts Outdated
Comment thread ui/webui/resources/custom_profile_image_row.html.ts Outdated
Comment thread ui/webui/resources/custom_profile_image_row.ts Outdated
@brave-builds

Copy link
Copy Markdown
Collaborator

A Storybook has been deployed to preview UI for the latest push

chrislacy and others added 9 commits August 27, 2026 15:24
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{...}`
@chrislacy

Copy link
Copy Markdown
Contributor Author

(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)

@chrislacy

Copy link
Copy Markdown
Contributor Author

I tried the suggested $i18n{...} replacement in 9f86316.

It could be an issue with my implementation, but the placeholder was not expanded at runtime. The literal "$i18n{CUSTOM_PROFILE_IMAGE_SELECTED_PREVIEW_LABEL}" became the image’s accessible name:

642524181-39a4caeb-ab1f-41e4-b926-fbcd0199d6c4

I reverted this change with the subsequent 42727e5.

@fallaciousreasoning fallaciousreasoning left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm % nits and a followup

Comment on lines +168 to +173
async function settleRow(row: BrCustomProfileImageRowElement) {
await microtasksFinished()
await row.updateComplete
await microtasksFinished()
await row.updateComplete
}

@fallaciousreasoning fallaciousreasoning Aug 28, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Comment on lines +213 to +224
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()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need all these mocks?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah I see

assertDeepEquals(createdUrls, revokedUrls)
})

test('DisconnectWhileDecodePendingRevokesUrl', async function() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need to stress too much about testing we revoke the urls. Probably safe to remove a bunch of these.

Comment on lines +71 to +73
protected onUploadClick_() {
this.shadowRoot.querySelector<HTMLInputElement>('#fileInput')!.click()
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread browser/ui/BUILD.gn
]
}

buildflag_header("custom_profile_image_buildflags") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@chrislacy chrislacy changed the title Desktop/custom profile image/manage profile integration [Desktop] Custom profile image manageProfile integration Aug 28, 2026

@bsclifton bsclifton left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strings LGTM! 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI/skip-android Do not run CI builds for Android CI/skip-ios Do not run CI builds for iOS CI/skip-origin Do not run CI builds for Origin CI/storybook-url Deploy storybook and provide a unique URL for each build

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Desktop] Add shared Lit custom profile image row [Desktop] Add manageProfile custom profile image selection UI

4 participants