blob: ef84de1f36a0ff50196dee6b7f665cd6c3f29e41 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @typedef {{device: string,
* lastUpdateTime: string,
* opened: boolean,
* separatorIndexes: !Array<number>,
* timestamp: number,
* tabs: !Array<!ForeignSessionTab>,
* tag: string}}
*/
let ForeignDeviceInternal;
Polymer({
is: 'history-synced-device-manager',
properties: {
/**
* @type {?Array<!ForeignSession>}
*/
sessionList: {
type: Array,
observer: 'updateSyncedDevices',
},
searchTerm: {
type: String,
observer: 'searchTermChanged',
},
/**
* An array of synced devices with synced tab data.
* @type {!Array<!ForeignDeviceInternal>}
*/
syncedDevices_: {
type: Array,
value: function() {
return [];
},
},
/** @private */
signInState: {
type: Boolean,
observer: 'signInStateChanged_',
},
/** @private */
guestSession_: {
type: Boolean,
value: loadTimeData.getBoolean('isGuestSession'),
},
/** @private */
fetchingSyncedTabs_: {
type: Boolean,
value: false,
},
/** @private */
hasSeenForeignData_: Boolean,
/**
* The session ID referring to the currently active action menu.
* @private {?string}
*/
actionMenuModel_: String,
},
listeners: {
'open-menu': 'onOpenMenu_',
'update-focus-grid': 'updateFocusGrid_',
},
/** @type {?cr.ui.FocusGrid} */
focusGrid_: null,
/** @override */
attached: function() {
this.focusGrid_ = new cr.ui.FocusGrid();
// Update the sign in state.
chrome.send('otherDevicesInitialized');
md_history.BrowserService.getInstance().recordHistogram(
SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.INITIALIZED,
SyncedTabsHistogram.LIMIT);
},
/** @override */
detached: function() {
this.focusGrid_.destroy();
},
/** @return {HTMLElement} */
getContentScrollTarget: function() {
return this;
},
/**
* @param {!ForeignSession} session
* @return {!ForeignDeviceInternal}
* @private
*/
createInternalDevice_: function(session) {
let tabs = [];
const separatorIndexes = [];
for (let i = 0; i < session.windows.length; i++) {
const windowId = session.windows[i].sessionId;
const newTabs = session.windows[i].tabs;
if (newTabs.length == 0)
continue;
newTabs.forEach(function(tab) {
tab.windowId = windowId;
});
let windowAdded = false;
if (!this.searchTerm) {
// Add all the tabs if there is no search term.
tabs = tabs.concat(newTabs);
windowAdded = true;
} else {
const searchText = this.searchTerm.toLowerCase();
for (let j = 0; j < newTabs.length; j++) {
const tab = newTabs[j];
if (tab.title.toLowerCase().indexOf(searchText) != -1) {
tabs.push(tab);
windowAdded = true;
}
}
}
if (windowAdded && i != session.windows.length - 1)
separatorIndexes.push(tabs.length - 1);
}
return {
device: session.name,
lastUpdateTime: '– ' + session.modifiedTime,
opened: true,
separatorIndexes: separatorIndexes,
timestamp: session.timestamp,
tabs: tabs,
tag: session.tag,
};
},
/** @private */
onSignInTap_: function() {
chrome.send('startSignInFlow');
},
/** @private */
onOpenMenu_: function(e) {
const menu = /** @type {CrActionMenuElement} */ (this.$.menu.get());
this.actionMenuModel_ = e.detail.tag;
menu.showAt(e.detail.target);
md_history.BrowserService.getInstance().recordHistogram(
SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.SHOW_SESSION_MENU,
SyncedTabsHistogram.LIMIT);
},
/** @private */
onOpenAllTap_: function() {
const menu = assert(this.$.menu.getIfExists());
const browserService = md_history.BrowserService.getInstance();
browserService.recordHistogram(
SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.OPEN_ALL,
SyncedTabsHistogram.LIMIT);
browserService.openForeignSessionAllTabs(assert(this.actionMenuModel_));
this.actionMenuModel_ = null;
menu.close();
},
/** @private */
updateFocusGrid_: function() {
if (!this.focusGrid_)
return;
this.focusGrid_.destroy();
this.debounce('updateFocusGrid', function() {
Polymer.dom(this.root)
.querySelectorAll('history-synced-device-card')
.reduce(
function(prev, cur) {
return prev.concat(cur.createFocusRows());
},
[])
.forEach((row) => {
this.focusGrid_.addRow(row);
});
this.focusGrid_.ensureRowActive(1);
});
},
/** @private */
onDeleteSessionTap_: function() {
const menu = assert(this.$.menu.getIfExists());
const browserService = md_history.BrowserService.getInstance();
browserService.recordHistogram(
SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.HIDE_FOR_NOW,
SyncedTabsHistogram.LIMIT);
browserService.deleteForeignSession(assert(this.actionMenuModel_));
this.actionMenuModel_ = null;
menu.close();
},
/** @private */
clearDisplayedSyncedDevices_: function() {
this.syncedDevices_ = [];
},
/**
* Decide whether or not should display no synced tabs message.
* @param {boolean} signInState
* @param {number} syncedDevicesLength
* @param {boolean} guestSession
* @return {boolean}
*/
showNoSyncedMessage: function(
signInState, syncedDevicesLength, guestSession) {
if (guestSession)
return true;
return signInState && syncedDevicesLength == 0;
},
/**
* Shows the signin guide when the user is not signed in and not in a guest
* session.
* @param {boolean} signInState
* @param {boolean} guestSession
* @return {boolean}
*/
showSignInGuide: function(signInState, guestSession) {
const show = !signInState && !guestSession;
if (show) {
md_history.BrowserService.getInstance().recordAction(
'Signin_Impression_FromRecentTabs');
}
return show;
},
/**
* Decide what message should be displayed when user is logged in and there
* are no synced tabs.
* @return {string}
*/
noSyncedTabsMessage: function() {
let stringName = this.fetchingSyncedTabs_ ? 'loading' : 'noSyncedResults';
if (this.searchTerm !== '')
stringName = 'noSearchResults';
return loadTimeData.getString(stringName);
},
/**
* Replaces the currently displayed synced tabs with |sessionList|. It is
* common for only a single session within the list to have changed, We try to
* avoid doing extra work in this case. The logic could be more intelligent
* about updating individual tabs rather than replacing whole sessions, but
* this approach seems to have acceptable performance.
* @param {?Array<!ForeignSession>} sessionList
*/
updateSyncedDevices: function(sessionList) {
this.fetchingSyncedTabs_ = false;
if (!sessionList)
return;
if (sessionList.length > 0 && !this.hasSeenForeignData_) {
this.hasSeenForeignData_ = true;
md_history.BrowserService.getInstance().recordHistogram(
SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.HAS_FOREIGN_DATA,
SyncedTabsHistogram.LIMIT);
}
const devices = [];
sessionList.forEach((session) => {
const device = this.createInternalDevice_(session);
if (device.tabs.length != 0)
devices.push(device);
});
this.syncedDevices_ = devices;
},
/**
* Get called when user's sign in state changes, this will affect UI of synced
* tabs page. Sign in promo gets displayed when user is signed out, and
* different messages are shown when there are no synced tabs.
*/
signInStateChanged_: function() {
this.fire('history-view-changed');
// User signed out, clear synced device list and show the sign in promo.
if (!this.signInState) {
this.clearDisplayedSyncedDevices_();
return;
}
// User signed in, show the loading message when querying for synced
// devices.
this.fetchingSyncedTabs_ = true;
},
searchTermChanged: function(searchTerm) {
this.clearDisplayedSyncedDevices_();
this.updateSyncedDevices(this.sessionList);
}
});