blob: ab4b3574f16961b19d9989c50bff1d190723b3bd [file] [log] [blame]
// 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 'chrome://resources/cr_elements/cr_hidden_style.css.js';
import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
import 'chrome://resources/cr_elements/cr_button/cr_button.js';
import 'chrome://resources/cr_elements/icons.html.js';
import 'chrome://user-notes-side-panel.top-chrome/shared/sp_empty_state.js';
import 'chrome://user-notes-side-panel.top-chrome/shared/sp_heading.js';
import 'chrome://user-notes-side-panel.top-chrome/shared/sp_footer.js';
import '//resources/cr_elements/cr_button/cr_button.js';
import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
import '../strings.m.js';
import './user_note_overviews_list.js';
import '../user_notes_list.js';
import {loadTimeData} from '//resources/js/load_time_data.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {getTemplate} from './app.html.js';
import {Note, NoteOverview} from './user_notes.mojom-webui.js';
import {UserNotesApiProxy, UserNotesApiProxyImpl} from './user_notes_api_proxy.js';
/** Enumeration of the possible states of the user notes side panel ui. */
export enum State {
GUEST = 0,
EMPTY = 1,
OVERVIEWS = 2,
CURRENT_PAGE_NOTES = 3,
}
export interface UserNotesAppElement {
$: {
pageNotesList: HTMLElement,
};
}
export class UserNotesAppElement extends PolymerElement {
static get is() {
return 'user-notes-app';
}
static get template() {
return getTemplate();
}
static get properties() {
return {
notes_: {
type: Array,
value: () => [],
},
overviews_: {
type: Array,
value: () => [],
},
hasNotesInAnyPages_: {
type: Boolean,
value: false,
},
state_: {
type: State,
computed: 'computeState_(hasNotesInAnyPages_, viewingOverviews_)',
},
/**
* This boolean is used indicate the user's preference in viewing the
* overviews list or the list of notes for the current page.
*/
viewingOverviews_: {
type: Boolean,
reflectToAttribute: true,
value: true,
},
startNoteCreation_: {
type: Boolean,
value: false,
},
};
}
private notes_: Array<(Note | null)>;
private overviews_: NoteOverview[];
private hasNotesInAnyPages_: boolean;
private state_: State;
private viewingOverviews_: boolean;
private startNoteCreation_: boolean;
private userNotesApi_: UserNotesApiProxy =
UserNotesApiProxyImpl.getInstance();
private listenerIds_: number[] = [];
override connectedCallback() {
super.connectedCallback();
if (this.isGuestMode_()) {
return;
}
const callbackRouter = this.userNotesApi_.getCallbackRouter();
this.listenerIds_.push(
callbackRouter.notesChanged.addListener(() => {
this.fetchAndUpdateNotesUi_(
/*viewOverviews=*/ this.viewingOverviews_,
/*startCreation=*/ false);
}),
callbackRouter.currentTabUrlChanged.addListener(
async (startNoteCreation: boolean) => {
await this.fetchAndUpdateNotesUi_(
/*viewOverviews=*/ false,
/*startCreation=*/ startNoteCreation);
this.startNoteCreation_ = startNoteCreation;
}),
callbackRouter.startNoteCreation.addListener(async () => {
if (this.viewingOverviews_) {
await this.fetchAndUpdateNotesUi_(
/*viewOverviews=*/ false, /*startCreation=*/ true);
}
this.startNoteCreation_ = true;
}),
);
this.fetchAndUpdateNotesUi_(
/*viewOverviews=*/ false, /*startCreation=*/ false);
}
override disconnectedCallback() {
super.disconnectedCallback();
this.listenerIds_.forEach(
id => this.userNotesApi_.getCallbackRouter().removeListener(id));
this.listenerIds_ = [];
}
private isGuestMode_() {
return loadTimeData.getBoolean('guestMode');
}
private computeState_() {
if (this.isGuestMode_()) {
return State.GUEST;
}
if (!this.hasNotesInAnyPages_ && this.viewingOverviews_) {
return State.EMPTY;
}
return this.viewingOverviews_ ? State.OVERVIEWS : State.CURRENT_PAGE_NOTES;
}
private async fetchAndUpdateNotesUi_(
viewOverviews: boolean, startCreation: boolean) {
// Check if there are user notes for any page. If not, clear notes_ and
// overviews_ data.
const {hasNotes} = await this.userNotesApi_.hasNotesInAnyPages();
this.hasNotesInAnyPages_ = hasNotes;
if (!hasNotes && !startCreation) {
this.viewingOverviews_ = true;
this.notes_ = [];
this.overviews_ = [];
return;
}
if (viewOverviews) {
const {overviews} = await this.userNotesApi_.getNoteOverviews('');
this.overviews_ = overviews;
this.viewingOverviews_ = true;
} else {
const {notes} = await this.userNotesApi_.getNotesForCurrentTab();
this.notes_ = notes;
// If there are notes for the current tab or we should be starting the
// creation flow then notes for the current page should be
// shown. Otherwise, fall back to showing the overviews.
if (notes.length > 0 || startCreation) {
// Add a null note which becomes the UserNoteElement that is the
// persistent entry point for creating a new note.
this.notes_.push(null);
this.viewingOverviews_ = false;
} else {
this.viewingOverviews_ = true;
const {overviews} = await this.userNotesApi_.getNoteOverviews('');
this.overviews_ = overviews;
}
}
}
private hasNotesForCurrentPage_() {
// This also accounts for a null placeholder that is the persistent entry
// point for creating a new note.
return this.notes_.length > 1;
}
private shouldShowEmptyOrGuestState() {
return this.state_ === State.EMPTY || this.state_ === State.GUEST;
}
private shouldShowAddButton_() {
// The add button should be visible if we are in the empty state or if
// viewing overviews and there are no notes for the current tab.
return this.state_ === State.EMPTY ||
(this.state_ === State.OVERVIEWS && !this.hasNotesForCurrentPage_());
}
private onAddNoteButtonClick_() {
this.viewingOverviews_ = false;
this.notes_ = [null];
this.startNoteCreation_ = true;
}
private onCurrentTabOverviewSelected_() {
this.viewingOverviews_ = false;
}
private onStartNoteCreation_() {
this.startNoteCreation_ = true;
}
private onAllNotesClick_() {
this.fetchAndUpdateNotesUi_(
/*viewOverviews=*/ true, /*startCreation=*/ false);
}
private shouldShowOverviews_(): boolean {
return this.state_ === State.OVERVIEWS;
}
private shouldShowPageNotes_(): boolean {
return this.state_ === State.CURRENT_PAGE_NOTES;
}
private getEmptyTitle_(): string {
if (this.isGuestMode_()) {
return loadTimeData.getString('emptyTitleGuest');
} else {
return loadTimeData.getString('emptyTitle');
}
}
private getEmptyBody_(): string {
if (this.isGuestMode_()) {
return loadTimeData.getString('emptyBodyGuest');
} else {
return loadTimeData.getString('emptyBody');
}
}
private sortByModificationTime_(note1: Note, note2: Note): number {
return Number(
note1.lastModificationTime.internalValue -
note2.lastModificationTime.internalValue);
}
}
declare global {
interface HTMLElementTagNameMap {
'user-notes-app': UserNotesAppElement;
}
}
customElements.define(UserNotesAppElement.is, UserNotesAppElement);