| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'chrome://resources/cr_elements/cr_button/cr_button.m.js'; |
| import 'chrome://resources/cr_elements/icons.m.js'; |
| import 'chrome://resources/cr_elements/shared_vars_css.m.js'; |
| import 'chrome://resources/js/cr.m.js'; |
| import 'chrome://resources/js/util.m.js'; |
| import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js'; |
| import '../shared/animations_css.js'; |
| import '../shared/chooser_shared_css.js'; |
| import '../shared/step_indicator.js'; |
| import '../strings.m.js'; |
| |
| import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js'; |
| import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; |
| import {isRTL} from 'chrome://resources/js/util.m.js'; |
| import {IronA11yAnnouncer} from 'chrome://resources/polymer/v3_0/iron-a11y-announcer/iron-a11y-announcer.js'; |
| import {afterNextRender, html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; |
| |
| import {navigateToNextStep, NavigationMixin, NavigationMixinInterface} from '../navigation_behavior.js'; |
| import {BookmarkBarManager, BookmarkProxy, BookmarkProxyImpl} from '../shared/bookmark_proxy.js'; |
| import {ModuleMetricsManager} from '../shared/module_metrics_proxy.js'; |
| import {stepIndicatorModel} from '../shared/nux_types.js'; |
| |
| import {GoogleAppProxy, GoogleAppProxyImpl} from './google_app_proxy.js'; |
| import {GoogleAppsMetricsProxyImpl} from './google_apps_metrics_proxy.js'; |
| |
| type AppItem = { |
| id: number, |
| name: string, |
| icon: string, |
| url: string, |
| bookmarkId: string|null, |
| selected: boolean, |
| }; |
| |
| type AppItemModel = { |
| item: AppItem, |
| set: (p1: string, p2: boolean) => void |
| }; |
| |
| const KEYBOARD_FOCUSED = 'keyboard-focused'; |
| |
| const NuxGoogleAppsElementBase = |
| mixinBehaviors([I18nBehavior], NavigationMixin(PolymerElement)) as |
| {new (): PolymerElement & NavigationMixinInterface & I18nBehaviorInterface}; |
| |
| /** @polymer */ |
| export class NuxGoogleAppsElement extends NuxGoogleAppsElementBase { |
| static get is() { |
| return 'nux-google-apps'; |
| } |
| |
| static get properties() { |
| return { |
| indicatorModel: Object, |
| |
| appList_: Array, |
| |
| hasAppsSelected_: { |
| type: Boolean, |
| notify: true, |
| }, |
| |
| subtitle: { |
| type: String, |
| value: loadTimeData.getString('googleAppsDescription'), |
| }, |
| }; |
| } |
| |
| private appProxy_: GoogleAppProxy; |
| private metricsManager_: ModuleMetricsManager; |
| private finalized_: boolean = false; |
| private bookmarkProxy_: BookmarkProxy; |
| private bookmarkBarManager_: BookmarkBarManager; |
| private wasBookmarkBarShownOnInit_: boolean = false; |
| private appList_: AppItem[]|null = null; |
| private hasAppsSelected_: boolean = true; |
| indicatorModel?: stepIndicatorModel; |
| |
| constructor() { |
| super(); |
| |
| this.appProxy_ = GoogleAppProxyImpl.getInstance(); |
| this.metricsManager_ = |
| new ModuleMetricsManager(GoogleAppsMetricsProxyImpl.getInstance()); |
| this.bookmarkProxy_ = BookmarkProxyImpl.getInstance(); |
| this.bookmarkBarManager_ = BookmarkBarManager.getInstance(); |
| } |
| |
| connectedCallback() { |
| super.connectedCallback(); |
| afterNextRender(this, () => IronA11yAnnouncer.requestAvailability()); |
| } |
| |
| onRouteEnter() { |
| this.finalized_ = false; |
| this.metricsManager_.recordPageInitialized(); |
| this.populateAllBookmarks_(); |
| } |
| |
| onRouteExit() { |
| if (this.finalized_) { |
| return; |
| } |
| this.cleanUp_(); |
| this.metricsManager_.recordBrowserBackOrForward(); |
| } |
| |
| onRouteUnload() { |
| if (this.finalized_) { |
| return; |
| } |
| this.cleanUp_(); |
| this.metricsManager_.recordNavigatedAway(); |
| } |
| |
| private changeFocus_(element: EventTarget, direction: number) { |
| if (isRTL()) { |
| direction *= -1; // Reverse direction if RTL. |
| } |
| |
| const buttons = this.shadowRoot!.querySelectorAll('button'); |
| const targetIndex = Array.prototype.indexOf.call(buttons, element); |
| |
| const oldFocus = buttons[targetIndex]; |
| if (!oldFocus) { |
| return; |
| } |
| |
| const newFocus = buttons[targetIndex + direction]; |
| |
| // New target and we're changing direction. |
| if (newFocus && direction) { |
| newFocus.classList.add(KEYBOARD_FOCUSED); |
| oldFocus.classList.remove(KEYBOARD_FOCUSED); |
| newFocus.focus(); |
| } else { |
| oldFocus.classList.add(KEYBOARD_FOCUSED); |
| } |
| } |
| |
| private announceA11y_(text: string) { |
| this.dispatchEvent(new CustomEvent( |
| 'iron-announce', {bubbles: true, composed: true, detail: {text}})); |
| } |
| |
| /** |
| * Called when bookmarks should be removed for all selected apps. |
| */ |
| private cleanUp_() { |
| this.finalized_ = true; |
| |
| if (!this.appList_) { |
| return; |
| } // No apps to remove. |
| |
| let removedBookmarks = false; |
| this.appList_.forEach(app => { |
| if (app.selected && app.bookmarkId) { |
| // Don't call |updateBookmark_| b/c we want to save the selection in the |
| // event of a browser back/forward. |
| this.bookmarkProxy_.removeBookmark(app.bookmarkId); |
| app.bookmarkId = null; |
| removedBookmarks = true; |
| } |
| }); |
| // Only update and announce if we removed bookmarks. |
| if (removedBookmarks) { |
| this.bookmarkBarManager_.setShown(this.wasBookmarkBarShownOnInit_); |
| this.announceA11y_(this.i18n('bookmarksRemoved')); |
| } |
| } |
| |
| /** |
| * Handle toggling the apps selected. |
| */ |
| private onAppClick_(e: {model: AppItemModel}) { |
| const item = e.model.item; |
| |
| e.model.set('item.selected', !item.selected); |
| |
| this.updateBookmark_(item); |
| this.updateHasAppsSelected_(); |
| |
| this.metricsManager_.recordClickedOption(); |
| |
| // Announcements should NOT be in |updateBookmark_| because there should be |
| // a different utterance when all app bookmarks are added/removed. |
| const i18nKey = item.selected ? 'bookmarkAdded' : 'bookmarkRemoved'; |
| this.announceA11y_(this.i18n(i18nKey)); |
| } |
| |
| private onAppKeyUp_(e: KeyboardEvent) { |
| if (e.key === 'ArrowRight') { |
| this.changeFocus_(e.currentTarget!, 1); |
| } else if (e.key === 'ArrowLeft') { |
| this.changeFocus_(e.currentTarget!, -1); |
| } else { |
| (e.currentTarget as HTMLElement).classList.add(KEYBOARD_FOCUSED); |
| } |
| } |
| |
| private onAppPointerDown_(e: Event) { |
| (e.currentTarget as HTMLElement).classList.remove(KEYBOARD_FOCUSED); |
| } |
| |
| private onNextClicked_() { |
| this.finalized_ = true; |
| this.appList_!.forEach(app => { |
| if (app.selected) { |
| this.appProxy_.recordProviderSelected(app.id); |
| } |
| }); |
| this.metricsManager_.recordGetStarted(); |
| navigateToNextStep(); |
| } |
| |
| private onNoThanksClicked_() { |
| this.cleanUp_(); |
| this.metricsManager_.recordNoThanks(); |
| navigateToNextStep(); |
| } |
| |
| /** |
| * Called when bookmarks should be created for all selected apps. |
| */ |
| private populateAllBookmarks_() { |
| this.wasBookmarkBarShownOnInit_ = this.bookmarkBarManager_.getShown(); |
| |
| if (this.appList_) { |
| this.appList_.forEach(app => this.updateBookmark_(app)); |
| } else { |
| this.appProxy_.getAppList().then(list => { |
| this.appList_ = list as AppItem[]; |
| this.appList_.forEach((app, index) => { |
| // Default select first few items. |
| app.selected = index < 3; |
| this.updateBookmark_(app); |
| }); |
| this.updateHasAppsSelected_(); |
| this.announceA11y_(this.i18n('bookmarksAdded')); |
| }); |
| } |
| } |
| |
| private updateBookmark_(item: AppItem) { |
| if (item.selected && !item.bookmarkId) { |
| this.bookmarkBarManager_.setShown(true); |
| this.bookmarkProxy_.addBookmark( |
| { |
| title: item.name, |
| url: item.url, |
| parentId: '1', |
| }, |
| result => { |
| item.bookmarkId = result.id; |
| }); |
| // Cache bookmark icon. |
| this.appProxy_.cacheBookmarkIcon(item.id); |
| } else if (!item.selected && item.bookmarkId) { |
| this.bookmarkProxy_.removeBookmark(item.bookmarkId); |
| item.bookmarkId = null; |
| } |
| } |
| |
| /** |
| * Updates the value of hasAppsSelected_. |
| */ |
| private updateHasAppsSelected_() { |
| this.hasAppsSelected_ = |
| !!this.appList_ && this.appList_.some(a => a.selected); |
| if (!this.hasAppsSelected_) { |
| this.bookmarkBarManager_.setShown(this.wasBookmarkBarShownOnInit_); |
| } |
| } |
| |
| /** |
| * Converts a boolean to a string because aria-pressed needs a string value. |
| */ |
| private getAriaPressed_(value: boolean): string { |
| return value ? 'true' : 'false'; |
| } |
| |
| static get template() { |
| return html`{__html_template__}`; |
| } |
| } |
| customElements.define(NuxGoogleAppsElement.is, NuxGoogleAppsElement); |