Implement First Time Use UI for Tab Focus (#28363)

* Enable Tab Focus UI

* Backend change to support first time use UI for tab focus

* Fix Illustration Resource

* npm run format

* Update showFRE related logics in both backend and frontend

* FE Review Changes

* Update API method name to SetTabFocusEnabled

---------

Co-authored-by: Jocelyn Liu <yrliou@gmail.com>
This commit is contained in:
Douglas Daniel
2025-04-08 17:44:07 -05:00
committed by GitHub
co-authored by Jocelyn Liu
parent 8f6ac99cfd
commit e7cec19db2
13 changed files with 159 additions and 19 deletions
+6
View File
@@ -1408,6 +1408,12 @@ Or change later at <ph name="SETTINGS_EXTENIONS_LINK">$2<ex>brave://settings/ext
<message name="IDS_BRAVE_ORGANIZE_TAB_DISMISS_BUTTON_LABEL" desc="Text of the dismiss button">
Dismiss
</message>
<message name="IDS_BRAVE_ORGANIZE_TAB_PRIVACY_DISCLAIMER" desc="Disclaimer text for enabling the tab focus feature">
When opened, this will automatically send the titles and origin of all non-private tabs to Leo to classify them.
</message>
<message name="IDS_BRAVE_ORGANIZE_TAB_ENABLE_BUTTON_LABEL" desc="Text of enable tab focus button">
Enable
</message>
<!--Add new items to the appropriate sections above -->
</messages>
</release>
@@ -1 +0,0 @@
Example
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 29 KiB

+1 -1
View File
@@ -7,7 +7,7 @@ brave_tab_search_extras_grdp =
"$root_gen_dir/brave/browser/resources/tab_search/brave_extras.grdp"
brave_tab_search_static_files = [
"brave/remove_me.txt",
"brave/tab_organizer_illustration.svg",
# SVG and other static files
]
@@ -148,6 +148,7 @@
}
.learn-more-link {
cursor: pointer;
color: var(--leo-color-text-tertiary);
font-feature-settings: 'ss03' on;
text-decoration-line: underline;
@@ -166,3 +167,50 @@
padding: var(--leo-spacing-l);
box-sizing: border-box;
}
.enable-tab-focus-wrapper {
display: flex;
flex-direction: column;
align-items: stretch;
box-sizing: border-box;
padding: var(--leo-spacing-m);
gap: var(--leo-spacing-m);
}
.enable-tab-focus-content-wrapper {
display: flex;
flex-direction: column;
align-items: stretch;
box-sizing: border-box;
border-radius: var(--leo-radius-m);
background-color: var(--leo-color-neutral-10);
overflow: hidden;
}
.enable-tab-focus-illustration {
background-image: url(/brave/tab_organizer_illustration.svg);
background-size: cover;
align-items: stretch;
height: 174px;
}
.enable-tab-focus-info-wrapper {
display: flex;
flex-direction: column;
padding: var(--leo-spacing-xl);
gap: var(--leo-spacing-m);
}
.enable-tab-focus-info-text {
font: var(--leo-font-small-regular);
color: var(--leo-color-text-secondary);
text-align: left;
}
.enable-tab-focus-button-row {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
width: 100%;
}
@@ -62,7 +62,66 @@ function getTopicsHtml(this: AutoTabGroupsPageElement) {
`)
}
function getHeaderHtml(this: AutoTabGroupsPageElement) {
return html`
<div class="title-row">
${!this.showBackButton ? html`
<div>
<leo-button
size="medium"
kind="plain-faint"
fab
@click=${this.onBackClick_}
>
<leo-icon name="arrow-left"></leo-icon>
</leo-button>
</div>
` : ''}
<div class='title-column'>
<div class="title">${this.getTitle_()}</div>
${!this.showFRE_ ? html`
<div class="subtitle">${this.getSubtitle_()}</div>
` : ''}
</div>
</div>
`
}
function getEnableTabFocusHtml(this: AutoTabGroupsPageElement) {
return html`
<div class="enable-tab-focus-wrapper">
${getHeaderHtml.bind(this)()}
<div class="enable-tab-focus-content-wrapper">
<div class="enable-tab-focus-illustration"></div>
<div class="enable-tab-focus-info-wrapper">
<span class="enable-tab-focus-info-text">
${this.getPrivacyDisclaimerMessage_()}
</span>
<div class="enable-tab-focus-button-row">
<span class="learn-more-link" @click=${this.onLearnMoreClicked_}>
${this.getLearnMoreLabel_()}
</span>
<div>
<leo-button
id="enableButton"
kind="filled"
size="small"
@click="${this.onEnableTabFocusClicked_}"
>
${this.getEnableButtonLabel_()}
</leo-button>
</div>
</div>
</div>
</div>
</div>
`
}
export function getHtml(this: AutoTabGroupsPageElement) {
if (this.showFRE_) {
return getEnableTabFocusHtml.bind(this)()
}
return html`<!--_html_template_start_-->
<div id="brave-tab-focus" class="brave-tab-focus">
<div
@@ -71,22 +130,7 @@ export function getHtml(this: AutoTabGroupsPageElement) {
aria-live="polite"
aria-relevant="all"
>
<div class='title-row'>
${this.showBackButton ? html`
<leo-button
size="medium"
kind="plain-faint"
fab
@click=${this.onBackClick_}
>
<leo-icon name="arrow-left"></leo-icon>
</leo-button>
` : ''}
<div class='title-column'>
<div class="title">${this.getTitle_()}</div>
<div class="subtitle">${this.getSubtitle_()}</div>
</div>
</div>
${getHeaderHtml.bind(this)()}
<div class="input-row">
<leo-input
id="topic-input"
@@ -23,6 +23,8 @@ export class AutoTabGroupsPageElement extends CrLitElement {
protected topics_: string[] = []
protected topic = ''
protected undoTopic_ = ''
protected showFRE_ =
loadTimeData.getBoolean('showTabOrganizationFRE')
private apiProxy_: BraveTabSearchApiProxy =
TabSearchApiProxyImpl.getInstance() as BraveTabSearchApiProxy
@@ -41,6 +43,7 @@ export class AutoTabGroupsPageElement extends CrLitElement {
isLoadingTopics: {type: Boolean},
errorMessage: {type: String},
needsPremium: {type: Boolean},
showFRE_: {type: Boolean},
}
}
@@ -212,6 +215,14 @@ export class AutoTabGroupsPageElement extends CrLitElement {
return loadTimeData.getString('tabOrganizationDismissButtonLabel')
}
protected getPrivacyDisclaimerMessage_(): string {
return loadTimeData.getString('tabOrganizationPrivacyDisclaimer')
}
protected getEnableButtonLabel_(): string {
return loadTimeData.getString('tabOrganizationEnableButtonLabel')
}
protected onLearnMoreClicked_() {
this.apiProxy_.openHelpPage()
}
@@ -220,6 +231,11 @@ export class AutoTabGroupsPageElement extends CrLitElement {
this.apiProxy_.openLeoGoPremiumPage()
}
protected onEnableTabFocusClicked_() {
this.apiProxy_.setTabFocusEnabled()
this.showFRE_ = false
}
protected onDismissErrorClicked_() {
this.errorMessage = ''
}
@@ -12,6 +12,7 @@ export interface BraveTabSearchApiProxy extends TabSearchApiProxy {
getFocusTabs: (topic: string) => Promise<{ windowCreated: boolean, error: Error | null }>
undoFocusTabs: () => Promise<void>
openLeoGoPremiumPage: () => void
setTabFocusEnabled: () => void
}
export class BraveTabSearchApiProxyImpl extends TabSearchApiProxyImpl implements BraveTabSearchApiProxy {
@@ -30,6 +31,10 @@ export class BraveTabSearchApiProxyImpl extends TabSearchApiProxyImpl implements
openLeoGoPremiumPage() {
this.handler.openLeoGoPremiumPage()
}
setTabFocusEnabled() {
this.handler.setTabFocusEnabled()
}
}
TabSearchApiProxyImpl.getInstance = () => {
@@ -36,4 +36,6 @@ interface PageHandler {
UndoFocusTabs() => ();
// Open a new tab for purchasing Leo premium.
OpenLeoGoPremiumPage();
// Set the user pref to enable tab focus with Leo.
SetTabFocusEnabled();
};
@@ -246,3 +246,8 @@ void TabSearchPageHandler::OpenLeoGoPremiumPage() {
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
Navigate(&params);
}
void TabSearchPageHandler::SetTabFocusEnabled() {
Profile::FromWebUI(web_ui_)->GetPrefs()->SetBoolean(
ai_chat::prefs::kBraveAIChatTabOrganizationEnabled, true);
}
@@ -59,6 +59,7 @@ class TabSearchPageHandler : public TabSearchPageHandler_ChromiumImpl {
GetFocusTabsCallback callback) override;
void UndoFocusTabs(UndoFocusTabsCallback callback) override;
void OpenLeoGoPremiumPage() override;
void SetTabFocusEnabled() override;
void SetOriginalTabsInfoByWindowForTesting(
const base::flat_map<SessionID, std::vector<TabInfo>>&
@@ -26,6 +26,11 @@ TabSearchUI::TabSearchUI(content::WebUI* web_ui)
profile->GetPrefs()->GetBoolean(
ai_chat::prefs::kBraveAIChatTabOrganizationEnabled));
// Show FRE if user doesn't explicitly enable/disble the feature pref.
update_data.Set("showTabOrganizationFRE",
!profile->GetPrefs()->HasPrefPath(
ai_chat::prefs::kBraveAIChatTabOrganizationEnabled));
update_data.Set("autoTabGroupsSelectorHeading",
l10n_util::GetStringUTF16(IDS_BRAVE_ORGANIZE_TAB_TITLE));
@@ -70,6 +75,14 @@ TabSearchUI::TabSearchUI(content::WebUI* web_ui)
"tabOrganizationDismissButtonLabel",
l10n_util::GetStringUTF16(IDS_BRAVE_ORGANIZE_TAB_DISMISS_BUTTON_LABEL));
update_data.Set(
"tabOrganizationPrivacyDisclaimer",
l10n_util::GetStringUTF16(IDS_BRAVE_ORGANIZE_TAB_PRIVACY_DISCLAIMER));
update_data.Set(
"tabOrganizationEnableButtonLabel",
l10n_util::GetStringUTF16(IDS_BRAVE_ORGANIZE_TAB_ENABLE_BUTTON_LABEL));
content::WebUIDataSource::Update(profile, chrome::kChromeUITabSearchHost,
std::move(update_data));
}
+1 -1
View File
@@ -31,7 +31,7 @@ void RegisterProfilePrefs(PrefRegistrySimple* registry) {
#endif
registry->RegisterBooleanPref(kBraveAIChatContextMenuEnabled, true);
registry->RegisterBooleanPref(kBraveAIChatShowToolbarButton, true);
registry->RegisterBooleanPref(kBraveAIChatTabOrganizationEnabled, false);
registry->RegisterBooleanPref(kBraveAIChatTabOrganizationEnabled, true);
}
registry->RegisterBooleanPref(kEnabledByPolicy, true);
}