blob: d3fc770fd24bdd51e7f120d702faaefbf6123608 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {assert} from 'chrome://resources/js/assert.js';
import {CrRouter} from 'chrome://resources/js/cr_router.js';
import {EventTracker} from 'chrome://resources/js/event_tracker.js';
import type {StoreObserver} from 'chrome://resources/js/store.js';
import {selectFolder, setSearchTerm} from './actions.js';
import {ROOT_NODE_ID} from './constants.js';
import {Store} from './store.js';
import type {BookmarksPageState} from './types.js';
/**
* This element is a one way bound interface that routes the page URL to
* the searchTerm and selectedId. Clients must initialize themselves by
* reading the router's fields after attach.
*/
export class BookmarksRouter implements StoreObserver<BookmarksPageState> {
private searchTerm_: string = '';
private selectedId_: string = '';
private defaultId_: string = '';
private updateStateTimeout_: number|null = null;
private tracker_: EventTracker = new EventTracker();
initialize() {
const store = Store.getInstance();
store.addObserver(this);
if (store.isInitialized()) {
this.onStateChanged(store.data);
}
const router = CrRouter.getInstance();
router.setDwellTime(200);
this.onQueryParamsChanged_(router.getQueryParams());
this.tracker_.add(
router, 'cr-router-query-params-changed',
(e: Event) => this.onQueryParamsChanged_(
(e as CustomEvent<URLSearchParams>).detail));
}
teardown() {
if (this.updateStateTimeout_) {
clearTimeout(this.updateStateTimeout_);
this.updateStateTimeout_ = null;
}
this.tracker_.removeAll();
Store.getInstance().removeObserver(this);
}
private onQueryParamsChanged_(newParams: URLSearchParams) {
const searchTerm = newParams.get('q') || '';
let selectedId = newParams.get('id');
if (!selectedId && !searchTerm) {
selectedId = this.defaultId_;
}
if (searchTerm !== this.searchTerm_) {
this.searchTerm_ = searchTerm;
Store.getInstance().dispatch(setSearchTerm(searchTerm));
}
if (selectedId && selectedId !== this.selectedId_) {
this.selectedId_ = selectedId;
// Need to dispatch a deferred action so that during page load
// `Store.getInstance().data` will only evaluate after the Store is
// initialized.
Store.getInstance().dispatchAsync((dispatch) => {
dispatch(selectFolder(selectedId, Store.getInstance().data.nodes));
});
}
}
onStateChanged(state: BookmarksPageState) {
this.selectedId_ = state.selectedFolder;
this.searchTerm_ = state.search.term;
// Default to the first child of root, which could be
// ACCOUNT_HEADING_NODE_ID, BOOKMARKS_BAR_ID (local), or the id of the
// account bookmark bar.
this.defaultId_ = state.nodes[ROOT_NODE_ID]!.children![0]!;
if (this.updateStateTimeout_) {
clearTimeout(this.updateStateTimeout_);
}
// Debounce to microtask timing.
this.updateStateTimeout_ = setTimeout(() => this.updateQueryParams_(), 0);
}
private updateQueryParams_() {
assert(this.updateStateTimeout_);
clearTimeout(this.updateStateTimeout_);
this.updateStateTimeout_ = null;
const queryParams = new URLSearchParams();
if (this.searchTerm_) {
queryParams.set('q', this.searchTerm_);
} else if (this.selectedId_ !== this.defaultId_) {
queryParams.set('id', this.selectedId_);
}
CrRouter.getInstance().setQueryParams(queryParams);
}
}