chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_edit_dialog.ts

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

import '../strings.m.js';
import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
import 'chrome://resources/cr_elements/cr_input/cr_input.js';
import 'chrome://resources/cr_elements/cr_shared_style.css.js';
import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';

import type {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
import type {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
import {assertNotReached} from 'chrome://resources/js/assert.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import type {DomRepeatEvent} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import {getTemplate} from './power_bookmarks_edit_dialog.html.js';
import {getFolderDescendants} from './power_bookmarks_service.js';

export const TEMP_FOLDER_ID_PREFIX = 'tmp_new_folder_';

export interface PowerBookmarksEditDialogElement {
  $: {
    dialog: CrDialogElement,
    nameInput: CrInputElement,
    urlInput: CrInputElement,
  };
}

export class PowerBookmarksEditDialogElement extends PolymerElement {
  static get is() {
    return 'power-bookmarks-edit-dialog';
  }

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

  static get properties() {
    return {
      topLevelBookmarks_: {
        type: Array,
        value: () => [],
      },

      selectedBookmarks_: {
        type: Array,
        value: () => [],
      },

      selectedFolder_: {
        type: Object,
        value: null,
      },

      activeFolderPath_: {
        type: Array,
        value: () => [],
      },

      newFolders_: {
        type: Array,
        value: () => [],
      },

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

  private topLevelBookmarks_: chrome.bookmarks.BookmarkTreeNode[];
  private selectedBookmarks_: chrome.bookmarks.BookmarkTreeNode[];
  private selectedFolder_: chrome.bookmarks.BookmarkTreeNode|undefined;
  private activeFolderPath_: chrome.bookmarks.BookmarkTreeNode[];
  private newFolders_: chrome.bookmarks.BookmarkTreeNode[];
  private moveOnly_: boolean;

  showDialog(
      activeFolderPath: chrome.bookmarks.BookmarkTreeNode[],
      topLevelBookmarks: chrome.bookmarks.BookmarkTreeNode[],
      selectedBookmarks: chrome.bookmarks.BookmarkTreeNode[],
      moveOnly: boolean) {
    this.activeFolderPath_ = activeFolderPath.slice();
    this.topLevelBookmarks_ = topLevelBookmarks;
    this.selectedBookmarks_ = selectedBookmarks;
    this.newFolders_ = [];
    this.moveOnly_ = moveOnly;
    this.$.dialog.showModal();
  }

  private isAvailableFolder_(node: chrome.bookmarks.BookmarkTreeNode): boolean {
    if (node.url) {
      return false;
    }
    for (const selectedBookmark of this.selectedBookmarks_) {
      // Don't allow moving a folder to itself or any of its descendants.
      const descendants = getFolderDescendants(selectedBookmark);
      if (descendants.includes(node)) {
        return false;
      }
    }
    return true;
  }

  private getDialogTitle_(): string {
    if (this.moveOnly_) {
      return loadTimeData.getString('editMoveFolderTo');
    } else {
      return loadTimeData.getString('editBookmark');
    }
  }

  private getBookmarkName_(): string {
    if (this.selectedBookmarks_.length === 1) {
      return this.selectedBookmarks_[0]!.title;
    }
    return '';
  }

  private getBookmarkUrl_(): string {
    if (this.selectedBookmarks_.length === 1) {
      return this.selectedBookmarks_[0]!.url!;
    }
    return '';
  }

  private getActiveFolder_(): chrome.bookmarks.BookmarkTreeNode|undefined {
    if (this.activeFolderPath_.length) {
      return this.activeFolderPath_[this.activeFolderPath_.length - 1];
    }
    return undefined;
  }

  private getActiveFolderTitle_(): string {
    return this.getFolderTitle_(this.getActiveFolder_());
  }

  private getFolderTitle_(folder: chrome.bookmarks.BookmarkTreeNode|
                          undefined): string {
    if (folder && folder.id !== loadTimeData.getString('otherBookmarksId') &&
        folder.id !== loadTimeData.getString('mobileBookmarksId')) {
      return folder!.title;
    } else {
      return loadTimeData.getString('allBookmarks');
    }
  }

  private getShownFolders_(): chrome.bookmarks.BookmarkTreeNode[] {
    const activeFolder = this.getActiveFolder_();
    if (activeFolder && activeFolder.children) {
      return activeFolder.children!.filter(this.isAvailableFolder_, this);
    } else if (!activeFolder && this.topLevelBookmarks_) {
      return this.topLevelBookmarks_.filter(this.isAvailableFolder_, this);
    }
    assertNotReached('No bookmarks to display in edit menu');
  }

  private getBackButtonLabel_(): string {
    let activeFolderParent: chrome.bookmarks.BookmarkTreeNode|undefined;
    if (this.activeFolderPath_.length > 1) {
      activeFolderParent =
          this.activeFolderPath_[this.activeFolderPath_.length - 2];
    }
    return loadTimeData.getStringF(
        'backButtonLabel', this.getFolderTitle_(activeFolderParent));
  }

  private getForwardButtonTooltip_(folder: chrome.bookmarks.BookmarkTreeNode):
      string {
    return loadTimeData.getStringF(
        'openBookmarkLabel', this.getFolderTitle_(folder));
  }

  private getForwardButtonLabel_(folder: chrome.bookmarks.BookmarkTreeNode):
      string {
    return loadTimeData.getStringF(
        'forwardButtonLabel', this.getFolderTitle_(folder));
  }

  private hasAvailableChildFolders_(folder: chrome.bookmarks.BookmarkTreeNode):
      boolean {
    return folder.children!.filter(this.isAvailableFolder_, this).length > 0;
  }

  private validateUrl_(): boolean {
    const urlInput = this.$.urlInput;

    if (urlInput.validate()) {
      return true;
    }

    const originalValue = urlInput.inputElement.value;
    urlInput.inputElement.value = 'http://' + originalValue;

    if (urlInput.validate()) {
      return true;
    }

    urlInput.inputElement.value = originalValue;
    return false;
  }

  private isSelected_(folder: chrome.bookmarks.BookmarkTreeNode): boolean {
    return folder === this.selectedFolder_;
  }

  private onBack_() {
    this.selectedFolder_ = undefined;
    this.pop('activeFolderPath_');
  }

  private onForward_(event: DomRepeatEvent<chrome.bookmarks.BookmarkTreeNode>) {
    this.selectedFolder_ = undefined;
    this.push('activeFolderPath_', event.model.item);
  }

  private onFolderSelected_(
      event: DomRepeatEvent<chrome.bookmarks.BookmarkTreeNode>) {
    if (this.selectedFolder_ === event.model.item) {
      this.selectedFolder_ = undefined;
    } else {
      this.selectedFolder_ = event.model.item;
    }
  }

  private onNewFolder_() {
    const parent =
        this.selectedFolder_ ? this.selectedFolder_ : this.getActiveFolder_();
    const parentId =
        parent ? parent.id : loadTimeData.getString('otherBookmarksId');
    const newFolder: chrome.bookmarks.BookmarkTreeNode = {
      id: TEMP_FOLDER_ID_PREFIX + this.newFolders_.length,
      title: loadTimeData.getString('newFolderTitle'),
      children: [],
      parentId: parentId,
    };
    if (parent) {
      parent.children!.unshift(newFolder);
    } else {
      this.topLevelBookmarks_.unshift(newFolder);
    }
    this.push('newFolders_', newFolder);
    if (parent !== this.getActiveFolder_()) {
      this.push('activeFolderPath_', parent);
    }
    this.selectedFolder_ = newFolder;
  }

  private onCancel_() {
    this.close_();
  }

  private onSave_() {
    if (!this.moveOnly_ && !this.validateUrl_()) {
      return;
    }
    const activeFolder = this.getActiveFolder_();
    let folderId;
    if (this.selectedFolder_) {
      folderId = this.selectedFolder_.id;
    } else if (activeFolder) {
      folderId = activeFolder.id;
    } else {
      folderId = loadTimeData.getString('otherBookmarksId');
    }
    this.dispatchEvent(new CustomEvent('save', {
      bubbles: true,
      composed: true,
      detail: {
        bookmarks: this.selectedBookmarks_,
        name: this.moveOnly_ ? undefined : this.$.nameInput.inputElement.value,
        url: this.moveOnly_ ? undefined : this.$.urlInput.inputElement.value,
        folderId: folderId,
        newFolders: this.newFolders_,
      },
    }));
    this.close_();
  }

  private close_() {
    this.selectedFolder_ = undefined;
    this.newFolders_ = [];
    this.$.dialog.close();
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'power-bookmarks-edit-dialog': PowerBookmarksEditDialogElement;
  }
}

customElements.define(
    PowerBookmarksEditDialogElement.is, PowerBookmarksEditDialogElement);