chromium/ui/file_manager/file_manager/foreground/elements/files_format_dialog.ts

// Copyright 2019 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/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 'chrome://resources/ash/common/cr_elements/cr_shared_style.css.js';
import 'chrome://resources/ash/common/cr_elements/cr_shared_vars.css.js';
import 'chrome://resources/ash/common/cr_elements/icons.html.js';
import 'chrome://resources/ash/common/cr_elements/md_select.css.js';
import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';

import type {CrDialogElement} from 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
import type {CrInputElement} from 'chrome://resources/ash/common/cr_elements/cr_input/cr_input.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import type {VolumeInfo} from '../../background/js/volume_info.js';
import type {EntryList, FilesAppEntry} from '../../common/js/files_app_entry_types.js';
import {isSinglePartitionFormatEnabled} from '../../common/js/flags.js';
import {bytesToString, str, strf} from '../../common/js/translations.js';
import type {FileSystemType} from '../../common/js/volume_manager_types.js';
import {validateExternalDriveName} from '../js/file_rename.js';

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

export interface FilesFormatDialog {
  $: {
    dialog: CrDialogElement,
    label: CrInputElement,
    'warning-container': HTMLDivElement,
  };
  label_: string;
  formatType_: chrome.fileManagerPrivate.FormatFileSystemType;
  spaceUsed_: string;
  isErase_: boolean;
}

function getVolumeInfoDisplayRoot(entry: Entry|FilesAppEntry): DirectoryEntry|
    null {
  if ('volumeInfo' in entry) {
    return (entry.volumeInfo as VolumeInfo).displayRoot || null;
  }
  return null;
}

export class FilesFormatDialog extends PolymerElement {
  static get is() {
    return 'files-format-dialog' as const;
  }

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

  static get properties() {
    return {
      label_: {
        type: String,
        value: '',
      },

      formatType_: {
        type: String,
        value: chrome.fileManagerPrivate.FormatFileSystemType.VFAT,
      },

      spaceUsed_: {
        type: String,
        value: '',
      },

      isErase_: {
        type: Boolean,
        value: false,
      },
    };
  }

  private volumeInfo_: VolumeInfo|null = null;
  private root_: EntryList|null = null;

  override ready() {
    super.ready();

    this.$.dialog.consumeKeydownEvent = true;
  }

  cancel() {
    this.$.dialog.cancel();
  }

  format() {
    try {
      validateExternalDriveName(
          this.label_, this.formatType_ as unknown as FileSystemType);
    } catch (error: any) {
      this.$.label.setAttribute('error-message', error.message);
      this.$.label.invalid = true;
      return;
    }

    if (this.isErase_) {
      chrome.fileManagerPrivate.singlePartitionFormat(
          this.root_?.devicePath || '', this.formatType_, this.label_);
    } else {
      chrome.fileManagerPrivate.formatVolume(
          this.volumeInfo_?.volumeId || '', this.formatType_, this.label_);
    }
    this.$.dialog.close();
  }


  /**
   * Used to set "single-partition-format" attribute on element.
   * It is used to check flag status in the tests.
   */
  getSinglePartitionFormat() {
    if (isSinglePartitionFormatEnabled()) {
      return 'single-partition-format';
    }
    return '';
  }

  getConfirmLabel(isErase: boolean) {
    if (isSinglePartitionFormatEnabled()) {
      if (isErase) {
        return str('REPARTITION_DIALOG_CONFIRM_LABEL');
      } else {
        return str('FORMAT_DIALOG_CONFIRM_SHORT_LABEL');
      }
    } else {
      return str('FORMAT_DIALOG_CONFIRM_LABEL');
    }
  }

  getDialogMessage(isErase: boolean) {
    if (isSinglePartitionFormatEnabled()) {
      if (isErase) {
        return str('REPARTITION_DIALOG_MESSAGE');
      } else {
        return str('FORMAT_PARTITION_DIALOG_MESSAGE');
      }
    } else {
      return str('FORMAT_DIALOG_MESSAGE');
    }
  }

  getStrf(token: string, value: string): string {
    return strf(token, value);
  }

  /**
   * Shows the dialog for drive represented by |volumeInfo|.
   */
  showModal(volumeInfo: VolumeInfo) {
    this.isErase_ = false;
    this.label_ = '';
    this.formatType_ = chrome.fileManagerPrivate.FormatFileSystemType.VFAT;
    this.spaceUsed_ = '';

    this.volumeInfo_ = volumeInfo;
    this.title = this.volumeInfo_.label;
    if (volumeInfo.displayRoot) {
      chrome.fileManagerPrivate.getDirectorySize(
          volumeInfo.displayRoot, (spaceUsed: number) => {
            if (spaceUsed > 0 && volumeInfo === this.volumeInfo_) {
              this.spaceUsed_ = bytesToString(spaceUsed);
            }
            if (window.IN_TEST) {
              this.$['warning-container'].setAttribute('fully-initialized', '');
            }
          });
    }

    this.$.dialog.showModal();
  }

  /**
   * Shows the dialog for erasing device.
   */
  showEraseModal(root: EntryList) {
    this.isErase_ = true;
    this.label_ = '';
    this.formatType_ = chrome.fileManagerPrivate.FormatFileSystemType.VFAT;
    this.spaceUsed_ = '';

    this.root_ = root;
    this.title = root.label;
    const childVolumes = this.root_.getUiChildren();
    let totalSpaceUsed = 0;

    const getSpaceUsedRequests = childVolumes.map((childVolume) => {
      return new Promise((resolve: (value: void) => void) => {
        const displayRoot = getVolumeInfoDisplayRoot(childVolume);
        if (displayRoot) {
          chrome.fileManagerPrivate.getDirectorySize(
              displayRoot, (spaceUsed: number) => {
                totalSpaceUsed += spaceUsed;
                if (totalSpaceUsed > 0) {
                  this.spaceUsed_ = bytesToString(totalSpaceUsed);
                }
                resolve();
              });
        }
      });
    });

    Promise.all(getSpaceUsedRequests).then(() => {
      if (window.IN_TEST) {
        this.$['warning-container'].setAttribute('fully-initialized', '');
      }
    });
    this.$.dialog.showModal();
  }
}

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

customElements.define(FilesFormatDialog.is, FilesFormatDialog);