| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import './setup_cancel_dialog.js'; |
| |
| import {assert} from 'chrome://resources/js/assert.js'; |
| |
| import {CANCEL_SETUP_EVENT, NEXT_PAGE_EVENT} from './base_setup_page.js'; |
| import {MetricsRecordedSetupPage, UserAction} from './cloud_upload.mojom-webui.js'; |
| import {CloudUploadBrowserProxy} from './cloud_upload_browser_proxy.js'; |
| import {OfficePwaInstallPageElement} from './office_pwa_install_page.js'; |
| import {OfficeSetupCompletePageElement} from './office_setup_complete_page.js'; |
| import type {SetupCancelDialogElement} from './setup_cancel_dialog.js'; |
| import {SignInPageElement} from './sign_in_page.js'; |
| import {WelcomePageElement} from './welcome_page.js'; |
| |
| /** |
| * The CloudUploadElement is the main dialog controller that aggregates all the |
| * individual setup pages and determines which one to show. |
| */ |
| export class CloudUploadElement extends HTMLElement { |
| private proxy = CloudUploadBrowserProxy.getInstance(); |
| |
| /** Resolved once the element's shadow DOM has finished initializing. */ |
| initPromise: Promise<void>; |
| |
| /** List of pages to show. */ |
| pages: HTMLElement[] = []; |
| |
| /** The current page index into `pages`. */ |
| private currentPageIdx: number = 0; |
| |
| /** The modal dialog shown to confirm if the user wants to cancel setup. */ |
| private cancelDialog: SetupCancelDialogElement; |
| |
| // Save reference to listener so it can be removed from the document in |
| // disconnectedCallback(). |
| private boundKeyDownListener_: (e: KeyboardEvent) => void; |
| |
| /** |
| True if the setup flow should end with setting Microsoft 365 as default |
| handler. Note: This is usually done if no default file handlers have been |
| set for Office files, which means that the setup flow is being completed for |
| the first time. |
| */ |
| private setOfficeAsDefaultHandler: boolean = true; |
| |
| /** The names of the files to upload. */ |
| private fileNames: string[] = []; |
| |
| constructor() { |
| super(); |
| const shadow = this.attachShadow({mode: 'open'}); |
| |
| this.cancelDialog = document.createElement('setup-cancel-dialog'); |
| shadow.appendChild(this.cancelDialog); |
| |
| this.boundKeyDownListener_ = this.onKeyDown.bind(this); |
| |
| this.initPromise = this.init(); |
| } |
| |
| connectedCallback(): void { |
| document.addEventListener('keydown', this.boundKeyDownListener_); |
| } |
| |
| disconnectedCallback(): void { |
| document.removeEventListener('keydown', this.boundKeyDownListener_); |
| } |
| |
| async init(): Promise<void> { |
| const [, {installed: isOfficeWebAppInstalled}, {mounted: isOdfsMounted}] = |
| await Promise.all([ |
| this.processDialogArgs(), |
| this.proxy.handler.isOfficeWebAppInstalled(), |
| this.proxy.handler.isODFSMounted(), |
| ]); |
| |
| // Only skip this page if the setup flow is not run as part of the "file |
| // upload" flow, and file handlers still need to be set. |
| if (this.fileNames.length !== 0 || this.setOfficeAsDefaultHandler) { |
| const welcomePage = new WelcomePageElement(); |
| welcomePage.setInstalled(isOfficeWebAppInstalled, isOdfsMounted); |
| this.pages.push(welcomePage); |
| } |
| |
| if (!isOfficeWebAppInstalled) { |
| this.pages.push(new OfficePwaInstallPageElement()); |
| } |
| |
| if (!isOdfsMounted) { |
| this.pages.push(new SignInPageElement()); |
| } |
| |
| const officeSetupCompletePage = new OfficeSetupCompletePageElement(); |
| officeSetupCompletePage.setDefaultHandlerOnPageShown( |
| this.setOfficeAsDefaultHandler); |
| this.pages.push(officeSetupCompletePage); |
| |
| for (const page of this.pages) { |
| page.addEventListener(NEXT_PAGE_EVENT, () => this.goNextPage()); |
| page.addEventListener(CANCEL_SETUP_EVENT, () => this.cancelSetup()); |
| } |
| |
| this.switchPage(0); |
| } |
| |
| $<T extends HTMLElement>(query: string): T { |
| return this.shadowRoot!.querySelector(query)!; |
| } |
| |
| /** |
| * Gets the element corresponding to the currently shown page. |
| */ |
| get currentPage(): HTMLElement|undefined { |
| return this.pages[this.currentPageIdx]; |
| } |
| |
| /** |
| * Switches the currently shown page. |
| * @param page Page index to show. |
| */ |
| private switchPage(page: number): void { |
| this.currentPage?.remove(); |
| this.currentPageIdx = page; |
| this.shadowRoot!.appendChild(this.currentPage!); |
| } |
| |
| /** |
| * Initialises the class members based off the given dialog arguments. |
| */ |
| private async processDialogArgs(): Promise<void> { |
| try { |
| const dialogArgs = await this.proxy.handler.getDialogArgs(); |
| assert(dialogArgs.args); |
| assert(dialogArgs.args.dialogSpecificArgs.oneDriveSetupDialogArgs); |
| this.setOfficeAsDefaultHandler = |
| dialogArgs.args.dialogSpecificArgs.oneDriveSetupDialogArgs |
| .setOfficeAsDefaultHandler; |
| this.fileNames = dialogArgs.args.fileNames; |
| } catch (e) { |
| // TODO(b/243095484) Define expected behavior. |
| console.error(`Unable to get dialog arguments . Error: ${e}.`); |
| } |
| } |
| |
| private onKeyDown(event: KeyboardEvent) { |
| if (event.key === 'Escape' && !this.cancelDialog.open) { |
| this.cancelSetup(); |
| // Stop escape from also immediately closing the dialog. |
| event.stopImmediatePropagation(); |
| event.preventDefault(); |
| } |
| } |
| |
| private currentPageToMetricsPage(): MetricsRecordedSetupPage|null { |
| if (this.currentPage instanceof WelcomePageElement) { |
| return MetricsRecordedSetupPage.kOneDriveSetupWelcome; |
| } else if (this.currentPage instanceof OfficePwaInstallPageElement) { |
| return MetricsRecordedSetupPage.kOneDriveSetupPWAInstall; |
| } else if (this.currentPage instanceof SignInPageElement) { |
| return MetricsRecordedSetupPage.kOneDriveSetupODFSMount; |
| } else if (this.currentPage instanceof OfficeSetupCompletePageElement) { |
| return MetricsRecordedSetupPage.kOneDriveSetupComplete; |
| } |
| return null; |
| } |
| |
| /** |
| * Invoked when a page fires a `CANCEL_SETUP_EVENT` event. |
| */ |
| private cancelSetup(): void { |
| if (this.currentPage instanceof OfficeSetupCompletePageElement) { |
| // No need to show the cancel dialog as setup is finished. |
| this.proxy.handler.respondWithUserActionAndClose(UserAction.kCancel); |
| return; |
| } |
| this.cancelDialog.show(() => { |
| const metricsPage = this.currentPageToMetricsPage(); |
| if (metricsPage != null) { |
| this.proxy.handler.recordCancel(metricsPage); |
| } |
| this.proxy.handler.respondWithUserActionAndClose(UserAction.kCancel); |
| }); |
| } |
| |
| /** |
| * Invoked when a page fires a `NEXT_PAGE_EVENT` event. |
| */ |
| private goNextPage(): void { |
| if (this.currentPageIdx < this.pages.length - 1) { |
| this.switchPage(this.currentPageIdx + 1); |
| } |
| } |
| } |
| |
| declare global { |
| interface HTMLElementTagNameMap { |
| 'cloud-upload': CloudUploadElement; |
| } |
| } |
| |
| customElements.define('cloud-upload', CloudUploadElement); |