chromium/chrome/browser/resources/ash/settings/os_people_page/add_user_dialog.ts

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview
 * 'settings-users-add-user-dialog' is the dialog shown for adding new allowed
 * users to a ChromeOS device.
 */

import 'chrome://resources/ash/common/cr_elements/cr_button/cr_button.js';
import 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
import 'chrome://resources/ash/common/cr_elements/cr_input/cr_input.js';
import '../settings_shared.css.js';
import '../settings_vars.css.js';

import {getInstance as getAnnouncerInstance} from 'chrome://resources/ash/common/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js';
import {CrDialogElement} from 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
import {CrInputElement} from 'chrome://resources/ash/common/cr_elements/cr_input/cr_input.js';
import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
import {assert} from 'chrome://resources/js/assert.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import {getTemplate} from './add_user_dialog.html.js';

/**
 * Regular expression for adding a user where the string provided is just
 * the part before the "@".
 * Email alias only, assuming it's a gmail address.
 *     e.g. 'john'
 */
const NAME_ONLY_REGEX =
    new RegExp('^\\s*([\\w\\.!#\\$%&\'\\*\\+-\\/=\\?\\^`\\{\\|\\}~]+)\\s*$');

/**
 * Regular expression for adding a user where the string provided is a full
 * email address.
 *     e.g. '[email protected]'
 */
const EMAIL_REGEX = new RegExp(
    '^\\s*([\\w\\.!#\\$%&\'\\*\\+-\\/=\\?\\^`\\{\\|\\}~]+)@' +
    '([A-Za-z0-9\-]{2,63}\\..+)\\s*$');

enum UserAddError {
  NO_ERROR = 0,
  INVALID_EMAIL = 1,
  USER_EXISTS = 2,
}

const SettingsUsersAddUserDialogElementBase = I18nMixin(PolymerElement);

export interface SettingsUsersAddUserDialogElement {
  $: {
    dialog: CrDialogElement,
    addUserInput: CrInputElement,
  };
}

export class SettingsUsersAddUserDialogElement extends
    SettingsUsersAddUserDialogElementBase {
  static get is() {
    return 'settings-users-add-user-dialog' as const;
  }

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

  static get properties() {
    return {
      errorCode_: {
        type: Number,
        value: UserAddError.NO_ERROR,
      },

      isEmail_: {
        type: Boolean,
        value: false,
      },

      isEmpty_: {
        type: Boolean,
        value: true,
      },

    };
  }

  private errorCode_: UserAddError;
  private isEmail_: boolean;
  private isEmpty_: boolean;
  private usersPrivate_: typeof chrome.usersPrivate;

  constructor() {
    super();

    this.usersPrivate_ = chrome.usersPrivate;
  }

  open(): void {
    this.$.addUserInput.value = '';
    this.onInput_();
    this.$.dialog.showModal();
    // Set to valid initially since the user has not typed anything yet.
    this.$.addUserInput.invalid = false;
  }

  private async addUser_(): Promise<void> {
    // May be submitted by the Enter key even if the input value is invalid.
    if (this.$.addUserInput.disabled) {
      return;
    }

    const input: string = this.$.addUserInput.value;

    const nameOnlyMatches: RegExpExecArray|null = NAME_ONLY_REGEX.exec(input);
    let userEmail: string;
    if (nameOnlyMatches) {
      userEmail = nameOnlyMatches[1] + '@gmail.com';
    } else {
      const emailMatches = EMAIL_REGEX.exec(input);
      // Assuming the input validated, one of these two must match.
      assert(emailMatches);
      userEmail = emailMatches[1] + '@' + emailMatches[2];
    }
    const isUserInList: boolean =
        await this.usersPrivate_.isUserInList(userEmail);
    if (isUserInList) {
      // This user email had been saved previously
      this.errorCode_ = UserAddError.USER_EXISTS;
      return;
    }

    getAnnouncerInstance().announce(this.i18n('userAddedMessage', userEmail));

    this.$.dialog.close();
    this.usersPrivate_.addUser(userEmail);
    this.$.addUserInput.value = '';
  }

  private canAddUser_(): boolean {
    return this.isEmail_ && !this.isEmpty_;
  }

  private onCancelClick_(): void {
    this.$.dialog.cancel();
  }

  private onInput_(): void {
    const input = this.$.addUserInput.value;
    this.isEmail_ = NAME_ONLY_REGEX.test(input) || EMAIL_REGEX.test(input);
    this.isEmpty_ = input.length === 0;

    if (!this.isEmail_ && !this.isEmpty_) {
      this.errorCode_ = UserAddError.INVALID_EMAIL;
      return;
    }

    this.errorCode_ = UserAddError.NO_ERROR;
  }

  private shouldShowError_(): boolean {
    return this.errorCode_ !== UserAddError.NO_ERROR;
  }

  private getErrorString_(errorCode: UserAddError): string {
    if (errorCode === UserAddError.USER_EXISTS) {
      return this.i18n('userExistsError');
    }
    // TODO errorString for UserAddError.INVALID_EMAIL crbug/1007481

    return '';
  }
}

declare global {
  interface HTMLElementTagNameMap {
    [SettingsUsersAddUserDialogElement.is]: SettingsUsersAddUserDialogElement;
  }
}

customElements.define(
    SettingsUsersAddUserDialogElement.is, SettingsUsersAddUserDialogElement);