Unload tabs (#2500)
- Keep all tabs unloaded on Zotero opening. - Keep loaded only the last five selected tabs. - Keep loaded only in the last 24h selected tabs. Fixes #2383
This commit is contained in:
parent
578986ab1e
commit
142e3b09f8
3 changed files with 82 additions and 18 deletions
|
@ -181,13 +181,13 @@ var ZoteroContextPane = new function () {
|
||||||
// It seems that changing `hidden` or `collapsed` values might
|
// It seems that changing `hidden` or `collapsed` values might
|
||||||
// be related with significant slow down when there are too many
|
// be related with significant slow down when there are too many
|
||||||
// DOM nodes (i.e. 10k notes)
|
// DOM nodes (i.e. 10k notes)
|
||||||
if (Zotero_Tabs.selectedIndex == 0) {
|
if (Zotero_Tabs.selectedType == 'library') {
|
||||||
_contextPaneSplitter.setAttribute('hidden', true);
|
_contextPaneSplitter.setAttribute('hidden', true);
|
||||||
_contextPane.setAttribute('collapsed', true);
|
_contextPane.setAttribute('collapsed', true);
|
||||||
_tabToolbar.hidden = true;
|
_tabToolbar.hidden = true;
|
||||||
_tabCover.hidden = true;
|
_tabCover.hidden = true;
|
||||||
}
|
}
|
||||||
else {
|
else if (Zotero_Tabs.selectedType == 'reader') {
|
||||||
var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
|
var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
|
||||||
if (reader) {
|
if (reader) {
|
||||||
_tabCover.hidden = false;
|
_tabCover.hidden = false;
|
||||||
|
|
|
@ -30,11 +30,18 @@ var React = require('react');
|
||||||
var ReactDOM = require('react-dom');
|
var ReactDOM = require('react-dom');
|
||||||
import TabBar from 'components/tabBar';
|
import TabBar from 'components/tabBar';
|
||||||
|
|
||||||
|
const MAX_LOADED_TABS = 5;
|
||||||
|
const UNLOAD_UNUSED_AFTER = 86400; // 24h
|
||||||
|
|
||||||
var Zotero_Tabs = new function () {
|
var Zotero_Tabs = new function () {
|
||||||
Object.defineProperty(this, 'selectedID', {
|
Object.defineProperty(this, 'selectedID', {
|
||||||
get: () => this._selectedID
|
get: () => this._selectedID
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'selectedType', {
|
||||||
|
get: () => this._getTab(this._selectedID).tab.type
|
||||||
|
});
|
||||||
|
|
||||||
Object.defineProperty(this, 'selectedIndex', {
|
Object.defineProperty(this, 'selectedIndex', {
|
||||||
get: () => this._getTab(this._selectedID).tabIndex
|
get: () => this._getTab(this._selectedID).tabIndex
|
||||||
});
|
});
|
||||||
|
@ -53,6 +60,10 @@ var Zotero_Tabs = new function () {
|
||||||
this._prevSelectedID = null;
|
this._prevSelectedID = null;
|
||||||
this._history = [];
|
this._history = [];
|
||||||
|
|
||||||
|
this._unloadInterval = setInterval(() => {
|
||||||
|
this.unloadUnusedTabs();
|
||||||
|
}, 60000); // Trigger every minute
|
||||||
|
|
||||||
this._getTab = function (id) {
|
this._getTab = function (id) {
|
||||||
var tabIndex = this._tabs.findIndex(tab => tab.id == id);
|
var tabIndex = this._tabs.findIndex(tab => tab.id == id);
|
||||||
return { tab: this._tabs[tabIndex], tabIndex };
|
return { tab: this._tabs[tabIndex], tabIndex };
|
||||||
|
@ -90,8 +101,12 @@ var Zotero_Tabs = new function () {
|
||||||
|
|
||||||
this.getState = function () {
|
this.getState = function () {
|
||||||
return this._tabs.map((tab) => {
|
return this._tabs.map((tab) => {
|
||||||
|
let type = tab.type;
|
||||||
|
if (type === 'reader-unloaded') {
|
||||||
|
type = 'reader';
|
||||||
|
}
|
||||||
var o = {
|
var o = {
|
||||||
type: tab.type,
|
type,
|
||||||
title: tab.title,
|
title: tab.title,
|
||||||
};
|
};
|
||||||
if (tab.data) {
|
if (tab.data) {
|
||||||
|
@ -104,20 +119,20 @@ var Zotero_Tabs = new function () {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
this.restoreState = function(tabs) {
|
this.restoreState = function (tabs) {
|
||||||
for (let tab of tabs) {
|
for (let i = 0; i < tabs.length; i++) {
|
||||||
|
let tab = tabs[i];
|
||||||
if (tab.type === 'library') {
|
if (tab.type === 'library') {
|
||||||
this.rename('zotero-pane', tab.title);
|
this.rename('zotero-pane', tab.title);
|
||||||
}
|
}
|
||||||
else if (tab.type === 'reader') {
|
else if (tab.type === 'reader') {
|
||||||
if (Zotero.Items.exists(tab.data.itemID)) {
|
if (Zotero.Items.exists(tab.data.itemID)) {
|
||||||
Zotero.Reader.open(tab.data.itemID,
|
this.add({
|
||||||
null,
|
type: 'reader-unloaded',
|
||||||
{
|
|
||||||
title: tab.title,
|
title: tab.title,
|
||||||
openInBackground: !tab.selected
|
index: i,
|
||||||
}
|
data: tab.data
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,10 +307,22 @@ var Zotero_Tabs = new function () {
|
||||||
* @param {Boolean} reopening
|
* @param {Boolean} reopening
|
||||||
*/
|
*/
|
||||||
this.select = function (id, reopening) {
|
this.select = function (id, reopening) {
|
||||||
var { tab } = this._getTab(id);
|
var { tab, tabIndex } = this._getTab(id);
|
||||||
if (!tab || tab.id === this._selectedID) {
|
if (!tab || tab.id === this._selectedID) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (this._selectedID) {
|
||||||
|
let { tab: selectedTab } = this._getTab(this._selectedID);
|
||||||
|
selectedTab.timeUnselected = Zotero.Date.getUnixTimestamp();
|
||||||
|
}
|
||||||
|
if (tab.type === 'reader-unloaded') {
|
||||||
|
this.close(tab.id);
|
||||||
|
Zotero.Reader.open(tab.data.itemID, null, {
|
||||||
|
title: tab.title,
|
||||||
|
tabIndex
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this._selectedID === 'zotero-pane') {
|
if (this._selectedID === 'zotero-pane') {
|
||||||
var { tab: selectedTab } = this._getTab(this._selectedID);
|
var { tab: selectedTab } = this._getTab(this._selectedID);
|
||||||
selectedTab.lastFocusedElement = document.activeElement;
|
selectedTab.lastFocusedElement = document.activeElement;
|
||||||
|
@ -312,12 +339,18 @@ var Zotero_Tabs = new function () {
|
||||||
}
|
}
|
||||||
tab.lastFocusedElement = null;
|
tab.lastFocusedElement = null;
|
||||||
}
|
}
|
||||||
let tabNode = document.querySelector(`.tab[data-id="${tab.id}"]`);
|
// Allow React to create a tab node
|
||||||
let tabsContainerNode = document.querySelector('#tab-bar-container .tabs');
|
setTimeout(() => {
|
||||||
document.querySelector(`.tab[data-id="${tab.id}"]`).scrollIntoView({ behavior: 'smooth' });
|
document.querySelector(`.tab[data-id="${tab.id}"]`).scrollIntoView({ behavior: 'smooth' });
|
||||||
|
});
|
||||||
// Border is not included when scrolling element node into view, therefore we do it manually.
|
// Border is not included when scrolling element node into view, therefore we do it manually.
|
||||||
// TODO: `scroll-padding` since Firefox 68 can probably be used instead
|
// TODO: `scroll-padding` since Firefox 68 can probably be used instead
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
let tabNode = document.querySelector(`.tab[data-id="${tab.id}"]`);
|
||||||
|
if (!tabNode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let tabsContainerNode = document.querySelector('#tab-bar-container .tabs');
|
||||||
if (tabNode.offsetLeft + tabNode.offsetWidth - tabsContainerNode.offsetWidth + 1 >= tabsContainerNode.scrollLeft) {
|
if (tabNode.offsetLeft + tabNode.offsetWidth - tabsContainerNode.offsetWidth + 1 >= tabsContainerNode.scrollLeft) {
|
||||||
document.querySelector('#tab-bar-container .tabs').scrollLeft += 1;
|
document.querySelector('#tab-bar-container .tabs').scrollLeft += 1;
|
||||||
}
|
}
|
||||||
|
@ -325,6 +358,36 @@ var Zotero_Tabs = new function () {
|
||||||
document.querySelector('#tab-bar-container .tabs').scrollLeft -= 1;
|
document.querySelector('#tab-bar-container .tabs').scrollLeft -= 1;
|
||||||
}
|
}
|
||||||
}, 500);
|
}, 500);
|
||||||
|
tab.timeSelected = Zotero.Date.getUnixTimestamp();
|
||||||
|
this.unloadUnusedTabs();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.unload = function (id) {
|
||||||
|
var { tab, tabIndex } = this._getTab(id);
|
||||||
|
if (!tab || tab.id === this._selectedID || tab.type !== 'reader') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.close(tab.id);
|
||||||
|
this.add({
|
||||||
|
type: 'reader-unloaded',
|
||||||
|
title: tab.title,
|
||||||
|
index: tabIndex,
|
||||||
|
data: tab.data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.unloadUnusedTabs = function () {
|
||||||
|
for (let tab of this._tabs) {
|
||||||
|
if (Zotero.Date.getUnixTimestamp() - tab.timeUnselected > UNLOAD_UNUSED_AFTER) {
|
||||||
|
this.unload(tab.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let tabs = this._tabs.slice().filter(x => x.type === 'reader');
|
||||||
|
tabs.sort((a, b) => b.timeUnselected - a.timeUnselected);
|
||||||
|
tabs = tabs.slice(MAX_LOADED_TABS);
|
||||||
|
for (let tab of tabs) {
|
||||||
|
this.unload(tab.id);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -969,6 +969,7 @@ class ReaderTab extends ReaderInstance {
|
||||||
// i.e. note-editor. There should be a better way to solve this
|
// i.e. note-editor. There should be a better way to solve this
|
||||||
this._window.addEventListener('pointerup', (event) => {
|
this._window.addEventListener('pointerup', (event) => {
|
||||||
if (this._window.Zotero_Tabs.selectedID === this.tabID
|
if (this._window.Zotero_Tabs.selectedID === this.tabID
|
||||||
|
&& this._iframeWindow
|
||||||
&& event.target
|
&& event.target
|
||||||
&& event.target.closest
|
&& event.target.closest
|
||||||
&& !event.target.closest('#outerContainer')) {
|
&& !event.target.closest('#outerContainer')) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue