Skip to content

Commit

Permalink
Tab Organization: Add sync states
Browse files Browse the repository at this point in the history
Gets current sync and account states and listens for updates. Displays
different UI depending on sync state. Does not yet trigger any sync/
sign-in flows, the not started state button is currently a no-op for
these cases.

Bug: 1469126
Change-Id: I6f97b10feeac912180a11cf79c037167486f08f9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4977837
Commit-Queue: Emily Shack <emshack@chromium.org>
Code-Coverage: findit-for-me@appspot.gserviceaccount.com <findit-for-me@appspot.gserviceaccount.com>
Reviewed-by: David Pennington <dpenning@chromium.org>
Reviewed-by: Alison Gale <agale@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1216210}
  • Loading branch information
Emily Shack authored and Chromium LUCI CQ committed Oct 27, 2023
1 parent 52d9aa2 commit 36e0dad
Show file tree
Hide file tree
Showing 13 changed files with 556 additions and 30 deletions.
17 changes: 16 additions & 1 deletion chrome/app/generated_resources.grd
Original file line number Diff line number Diff line change
Expand Up @@ -10437,9 +10437,24 @@ Check your passwords anytime in <ph name="GOOGLE_PASSWORD_MANAGER">$1<ex>Google
<message name="IDS_TAB_ORGANIZATION_NOT_STARTED_BODY_FRE" desc="The body text for the first run experience of the not started state in the tab organization UI" translateable="false">
Chrome can help your tabs stay organized and easy to find
</message>
<message name="IDS_TAB_ORGANIZATION_NOT_STARTED_BUTTON" desc="The label for the button in the not started state of the tab organization UI" translateable="false">
<message name="IDS_TAB_ORGANIZATION_NOT_STARTED_BODY_UNSYNCED" desc="The body text for the not started state in the tab organization UI, when not signed in or signed in but unsynced/sync paused" translateable="false">
Turn on sync to let Chrome keep your tabs organized.
</message>
<message name="IDS_TAB_ORGANIZATION_NOT_STARTED_BODY_UNSYNCED_HISTORY" desc="The body text for the not started state in the tab organization UI, when history sync is disabled" translateable="false">
Turn on History sync in Settings to let Chrome keep your tabs organized.
</message>
<message name="IDS_TAB_ORGANIZATION_NOT_STARTED_BUTTON" desc="The label for the button in the not started state of the tab organization UI, when synced" translateable="false">
Organize tabs
</message>
<message name="IDS_TAB_ORGANIZATION_NOT_STARTED_BUTTON_UNSYNCED" desc="The label for the button in the not started state of the tab organization UI, when not signed in or signed in but unsynced" translateable="false">
Turn on sync
</message>
<message name="IDS_TAB_ORGANIZATION_NOT_STARTED_BUTTON_UNSYNCED_HISTORY" desc="The label for the button in the not started state of the tab organization UI, when history sync is disabled" translateable="false">
Settings
</message>
<message name="IDS_TAB_ORGANIZATION_NOT_STARTED_BUTTON_SYNC_PAUSED" desc="The label for the button in the not started state of the tab organization UI, when sync is paused" translateable="false">
Sign in
</message>
<message name="IDS_TAB_ORGANIZATION_IN_PROGRESS_TITLE" desc="The header text for the in progress state in the tab organization UI" translateable="false">
Organizing…
</message>
Expand Down
1 change: 1 addition & 0 deletions chrome/browser/resources/tab_search/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ build_webui("build") {
"tab_data.ts",
"tab_group_color_helper.ts",
"tab_search_api_proxy.ts",
"tab_search_sync_browser_proxy.ts",
"tab_search.ts",
"tab_search_utils.ts",
"title_item.ts",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,57 @@
<style include="tab-organization-shared-style">
:host {
--avatar-size: 40px;
}

cr-button {
align-self: flex-end;
width: fit-content;
}

.account-email {
font-size: 12px;
}

.account-image {
border-radius: var(--avatar-size);
height: var(--avatar-size);
width: var(--avatar-size);
}

.account-row {
align-items: center;
display: flex;
gap: 10px;
}

.account-text {
display: flex;
flex-direction: column;
gap: 4px;
}
</style>

<div class="tab-organization-container">
<div class="tab-organization-text-container">
<div class="tab-organization-header">[[getTitle_(showFre)]]</div>
<div class="tab-organization-body">[[getBody_(showFre)]]</div>
<div class="tab-organization-body">
[[getBody_(showFre, sync_, account_)]]
</div>
</div>
<cr-button class="action-button" on-click="onOrganizeTabsClick_">
$i18n{notStartedButton}
<template is="dom-if" if="[[shouldShowAccountInfo_(sync_, account_)]]">
<div class="account-row">
<!-- TODO(emshack): Add alt text -->
<img class="account-image" alt=""
src="[[getAccountImageSrc_(account_.avatarImage)]]">
<div class="account-text">
<div class="tab-organization-header">[[account_.name]]</div>
<div class="tab-organization-body account-email">
[[account_.email]]
</div>
</div>
</div>
</template>
<cr-button class="action-button" on-click="onButtonClick_">
[[getButtonText_(sync_, account_)]]
</cr-button>
</div>
120 changes: 112 additions & 8 deletions chrome/browser/resources/tab_search/tab_organization_not_started.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,81 @@ import 'chrome://resources/cr_elements/cr_button/cr_button.js';
import './strings.m.js';
import './tab_organization_shared_style.css.js';

import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import {getTemplate} from './tab_organization_not_started.html.js';
import {AccountInfo, SyncInfo, TabSearchSyncBrowserProxy, TabSearchSyncBrowserProxyImpl} from './tab_search_sync_browser_proxy.js';

enum SyncState {
SIGNED_OUT,
UNSYNCED,
UNSYNCED_HISTORY,
SYNC_PAUSED,
SYNCED,
}

const TabOrganizationNotStartedElementBase = WebUiListenerMixin(PolymerElement);

// Not started state for the tab organization UI.
export class TabOrganizationNotStartedElement extends PolymerElement {
export class TabOrganizationNotStartedElement extends
TabOrganizationNotStartedElementBase {
static get is() {
return 'tab-organization-not-started';
}

static get properties() {
return {
showFre: Boolean,
account_: Object,
sync_: Object,
};
}

showFre: boolean;

private account_?: AccountInfo;
private sync_?: SyncInfo;
private syncBrowserProxy_: TabSearchSyncBrowserProxy =
TabSearchSyncBrowserProxyImpl.getInstance();

static get template() {
return getTemplate();
}

override connectedCallback() {
super.connectedCallback();

this.syncBrowserProxy_.getAccountInfo().then(this.setAccount_.bind(this));
this.addWebUiListener('account-info-changed', this.setAccount_.bind(this));

this.syncBrowserProxy_.getSyncInfo().then(this.setSync_.bind(this));
this.addWebUiListener('sync-info-changed', this.setSync_.bind(this));
}

private setAccount_(account: AccountInfo) {
this.account_ = account;
}

private setSync_(sync: SyncInfo) {
this.sync_ = sync;
}

private getSyncState_(): SyncState {
if (!this.account_) {
return SyncState.SIGNED_OUT;
} else if (!this.sync_?.syncing) {
return SyncState.UNSYNCED;
} else if (this.sync_.paused) {
return SyncState.SYNC_PAUSED;
} else if (!this.sync_.syncingHistory) {
return SyncState.UNSYNCED_HISTORY;
} else {
return SyncState.SYNCED;
}
}

private getTitle_(): string {
if (this.showFre) {
return loadTimeData.getString('notStartedTitleFRE');
Expand All @@ -38,16 +90,68 @@ export class TabOrganizationNotStartedElement extends PolymerElement {
}

private getBody_(): string {
if (this.showFre) {
return loadTimeData.getString('notStartedBodyFRE');
} else {
return loadTimeData.getString('notStartedBody');
switch (this.getSyncState_()) {
case SyncState.SIGNED_OUT:
case SyncState.UNSYNCED:
case SyncState.SYNC_PAUSED:
return loadTimeData.getString('notStartedBodyUnsynced');
case SyncState.UNSYNCED_HISTORY:
return loadTimeData.getString('notStartedBodyUnsyncedHistory');
case SyncState.SYNCED: {
if (this.showFre) {
return loadTimeData.getString('notStartedBodyFRE');
} else {
return loadTimeData.getString('notStartedBody');
}
}
}
}

private onOrganizeTabsClick_() {
this.dispatchEvent(new CustomEvent(
'organize-tabs-click', {bubbles: true, composed: true}));
private shouldShowAccountInfo_(): boolean {
return !!this.account_ &&
(!this.sync_ || !this.sync_.syncing || this.sync_.paused ||
!this.sync_.syncingHistory);
}

private getAccountImageSrc_(image: string|null): string {
// image can be undefined if the account has not set an avatar photo.
return image || 'chrome://theme/IDR_PROFILE_AVATAR_PLACEHOLDER_LARGE';
}

private getButtonText_(): string {
switch (this.getSyncState_()) {
case SyncState.SIGNED_OUT:
case SyncState.UNSYNCED:
return loadTimeData.getString('notStartedButtonUnsynced');
case SyncState.SYNC_PAUSED:
return loadTimeData.getString('notStartedButtonPaused');
case SyncState.UNSYNCED_HISTORY:
return loadTimeData.getString('notStartedButtonUnsyncedHistory');
case SyncState.SYNCED:
return loadTimeData.getString('notStartedButton');
}
}

private onButtonClick_() {
switch (this.getSyncState_()) {
case SyncState.SIGNED_OUT:
// TODO(emshack): Trigger sign in & sync flow
break;
case SyncState.UNSYNCED:
// TODO(emshack): Trigger sync flow
break;
case SyncState.SYNC_PAUSED:
// TODO(emshack): Trigger sign in flow
break;
case SyncState.UNSYNCED_HISTORY:
// TODO(emshack): Trigger opening sync settings
break;
case SyncState.SYNCED:
// Start a tab organization
this.dispatchEvent(new CustomEvent(
'organize-tabs-click', {bubbles: true, composed: true}));
break;
}
}
}

Expand Down
1 change: 1 addition & 0 deletions chrome/browser/resources/tab_search/tab_search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ export {TabSearchApiProxy, TabSearchApiProxyImpl} from './tab_search_api_proxy.j
export {TabSearchGroupItem} from './tab_search_group_item.js';
export {TabSearchItem} from './tab_search_item.js';
export {TabSearchPageElement} from './tab_search_page.js';
export {AccountInfo, SyncInfo, TabSearchSyncBrowserProxy, TabSearchSyncBrowserProxyImpl} from './tab_search_sync_browser_proxy.js';
export {TabAlertState} from './tabs.mojom-webui.js';
export {TitleItem} from './title_item.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import {sendWithPromise} from 'chrome://resources/js/cr.js';

/**
* @see chrome/browser/ui/webui/tab_search/tab_search_sync_handler.cc
*/
export interface AccountInfo {
name: string;
email: string;
avatarImage?: string;
}

export interface SyncInfo {
syncing: boolean;
paused: boolean;
syncingHistory: boolean;
}

export interface TabSearchSyncBrowserProxy {
/**
* Gets the current sync info.
*/
getSyncInfo(): Promise<SyncInfo>;

/**
* Gets the current account info.
*/
getAccountInfo(): Promise<AccountInfo>;
}

export class TabSearchSyncBrowserProxyImpl implements
TabSearchSyncBrowserProxy {
getSyncInfo() {
return sendWithPromise('GetSyncInfo');
}

getAccountInfo() {
return sendWithPromise('GetAccountInfo');
}

static getInstance(): TabSearchSyncBrowserProxy {
return instance || (instance = new TabSearchSyncBrowserProxyImpl());
}

static setInstance(obj: TabSearchSyncBrowserProxy) {
instance = obj;
}
}

let instance: TabSearchSyncBrowserProxy|null = null;
2 changes: 2 additions & 0 deletions chrome/browser/ui/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -1895,6 +1895,8 @@ static_library("ui") {
"webui/tab_search/tab_search_page_handler.h",
"webui/tab_search/tab_search_prefs.cc",
"webui/tab_search/tab_search_prefs.h",
"webui/tab_search/tab_search_sync_handler.cc",
"webui/tab_search/tab_search_sync_handler.h",
"webui/tab_search/tab_search_ui.cc",
"webui/tab_search/tab_search_ui.h",
"webui/theme_handler.cc",
Expand Down

0 comments on commit 36e0dad

Please sign in to comment.