| // Copyright 2020 The Chromium Authors |
| // 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_view_manager/cr_view_manager.js'; |
| import 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render_lit.js'; |
| import './profile_picker_main_view.js'; |
| import '/strings.m.js'; |
| |
| import type {CrViewManagerElement} from 'chrome://resources/cr_elements/cr_view_manager/cr_view_manager.js'; |
| import {I18nMixinLit} from 'chrome://resources/cr_elements/i18n_mixin_lit.js'; |
| import {WebUiListenerMixinLit} from 'chrome://resources/cr_elements/web_ui_listener_mixin_lit.js'; |
| import {assert, assertNotReached} from 'chrome://resources/js/assert.js'; |
| import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; |
| import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js'; |
| |
| import {ensureLazyLoaded} from './ensure_lazy_loaded.js'; |
| import type {AutogeneratedThemeColorInfo, ManageProfilesBrowserProxy} from './manage_profiles_browser_proxy.js'; |
| import {ManageProfilesBrowserProxyImpl} from './manage_profiles_browser_proxy.js'; |
| import {navigateTo, NavigationMixin, ProfileCreationSteps, Routes} from './navigation_mixin.js'; |
| import {getCss} from './profile_picker_app.css.js'; |
| import {getHtml} from './profile_picker_app.html.js'; |
| import {isForceSigninEnabled, isGlicVersion, isProfileCreationAllowed} from './profile_picker_flags.js'; |
| |
| export interface ProfilePickerAppElement { |
| $: { |
| viewManager: CrViewManagerElement, |
| }; |
| } |
| |
| const ProfilePickerAppElementBase = |
| WebUiListenerMixinLit(I18nMixinLit(NavigationMixin(CrLitElement))); |
| |
| // Helper enum to determine which 'mode' the app should display. Used in the CSS |
| // styling, where the string literals are used for attributes matching. |
| enum AppMode { |
| REGULAR = 'regular', |
| NO_BANNER = 'no-banner', |
| GLIC = 'glic', |
| } |
| |
| export class ProfilePickerAppElement extends ProfilePickerAppElementBase { |
| static get is() { |
| return 'profile-picker-app'; |
| } |
| |
| static override get styles() { |
| return getCss(); |
| } |
| |
| override render() { |
| return getHtml.bind(this)(); |
| } |
| |
| static override get properties() { |
| return { |
| /** |
| * Suggested new profile theme info for the profile creation flow. |
| */ |
| newProfileThemeInfo: { |
| type: Object, |
| notify: true, |
| }, |
| |
| /** |
| * True if a new profile (local or signed-in) is being created, all |
| * buttons that create a new profile are then disabled (to avoid creating |
| * two profiles). |
| */ |
| profileCreationInProgress: { |
| type: Boolean, |
| notify: true, |
| }, |
| |
| // Reflected as `app-mode_` to be used in the CSS styling with attributes |
| // matching. |
| appMode_: { |
| type: String, |
| reflect: true, |
| }, |
| }; |
| } |
| |
| accessor newProfileThemeInfo: AutogeneratedThemeColorInfo|undefined; |
| accessor profileCreationInProgress: boolean = false; |
| protected shouldDisplayVerticalBanners_: boolean = false; |
| private currentRoute_: Routes|null = null; |
| private manageProfilesBrowserProxy_: ManageProfilesBrowserProxy = |
| ManageProfilesBrowserProxyImpl.getInstance(); |
| protected accessor appMode_: AppMode = AppMode.REGULAR; |
| |
| override connectedCallback() { |
| super.connectedCallback(); |
| this.setMinimumSize_(); |
| } |
| |
| override onRouteChange(route: Routes, step: string) { |
| if (!isProfileCreationAllowed() && route === Routes.NEW_PROFILE) { |
| navigateTo(Routes.MAIN); |
| return; |
| } |
| |
| if (step === ProfileCreationSteps.LOAD_FORCE_SIGNIN) { |
| assert( |
| route === Routes.NEW_PROFILE, |
| 'LOAD_FORCE_SIGNIN step must be a part of NEW_PROFILE route'); |
| assert( |
| isForceSigninEnabled(), |
| 'Force signin policy must be enabled to start the force signin flow'); |
| // The force sign-in flow is displayed in a dialog on top of the main |
| // view. Load the main view if it isn't already shown. |
| // Do not call `navigateTo(Routes.MAIN)` to not update the history. |
| // It's fine to skip `initializeModules()` for the main view since the |
| // main view will never be lazy loaded. |
| if (this.currentRoute_ !== Routes.MAIN) { |
| this.currentRoute_ = Routes.MAIN; |
| document.title = this.getDocumentTitle_('mainView'); |
| this.$.viewManager.switchView('mainView', 'fade-in', 'no-animation'); |
| } |
| this.manageProfilesBrowserProxy_.selectNewAccount(null); |
| |
| return; |
| } |
| |
| assert( |
| step !== ProfileCreationSteps.LOAD_SIGNIN, |
| 'LOAD_SIGNIN should not appear in navigation (only used for metrics)'); |
| |
| if (step === ProfileCreationSteps.LOCAL_PROFILE_CUSTOMIZATION) { |
| if (this.profileCreationInProgress) { |
| return; |
| } |
| this.profileCreationInProgress = true; |
| // TODO(crbug.com/40209493): Add createShortcut parameter. |
| this.initializeNewProfileThemeInfo_().then( |
| () => this.manageProfilesBrowserProxy_.continueWithoutAccount( |
| this.newProfileThemeInfo!.color)); |
| return; |
| } |
| |
| const setStep = () => { |
| document.title = this.getDocumentTitle_(step); |
| this.updateAppMode_(step); |
| this.$.viewManager.switchView(step, 'fade-in', 'no-animation'); |
| }; |
| |
| // If the route changed, initialize modules for that route. |
| if (this.currentRoute_ !== route) { |
| this.currentRoute_ = route; |
| this.initializeModules_().then(setStep); |
| } else { |
| setStep(); |
| } |
| } |
| |
| private updateAppMode_(step: string) { |
| if (this.currentRoute_ === Routes.MAIN || |
| (this.currentRoute_ === Routes.NEW_PROFILE && |
| step === ProfileCreationSteps.PROFILE_TYPE_CHOICE)) { |
| this.appMode_ = isGlicVersion() ? AppMode.GLIC : AppMode.REGULAR; |
| } else { |
| assert(!isGlicVersion(), 'Only `Routes.MAIN` supports Glic version'); |
| this.appMode_ = AppMode.NO_BANNER; |
| } |
| } |
| |
| private getDocumentTitle_(step: string): string { |
| switch (step) { |
| case 'mainView': |
| // This is needed because some version of the title have parts of the |
| // text with special styling through the `class`. |
| return this.i18nAdvanced('mainViewTitle', {attrs: ['class']}) |
| .toString(); |
| case ProfileCreationSteps.PROFILE_TYPE_CHOICE: |
| return this.i18n('profileTypeChoiceTitle'); |
| case ProfileCreationSteps.LOCAL_PROFILE_CUSTOMIZATION: |
| return this.i18n('localProfileCreationTitle'); |
| case 'profileSwitch': |
| return this.i18n('profileSwitchTitle'); |
| default: |
| return ''; |
| } |
| } |
| |
| private initializeModules_(): Promise<any> { |
| switch (this.currentRoute_) { |
| case Routes.MAIN: |
| return Promise.resolve(); |
| case Routes.NEW_PROFILE: |
| return Promise.all( |
| [this.initializeNewProfileThemeInfo_(), ensureLazyLoaded()]); |
| case Routes.PROFILE_SWITCH: |
| return ensureLazyLoaded(); |
| default: |
| // |this.currentRoute_| should be set by now. |
| assertNotReached(); |
| } |
| } |
| |
| private async initializeNewProfileThemeInfo_(): Promise<void> { |
| if (this.newProfileThemeInfo) { |
| return Promise.resolve(); |
| } |
| this.newProfileThemeInfo = await this.manageProfilesBrowserProxy_ |
| .getNewProfileSuggestedThemeInfo(); |
| } |
| |
| private setMinimumSize_() { |
| this.style.setProperty( |
| '--view-min-size', loadTimeData.getString('minimumPickerSize')); |
| } |
| } |
| |
| declare global { |
| interface HTMLElementTagNameMap { |
| 'profile-picker-app': ProfilePickerAppElement; |
| } |
| } |
| |
| customElements.define(ProfilePickerAppElement.is, ProfilePickerAppElement); |