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:
Martynas Bagdonas 2022-04-20 14:14:50 +07:00 committed by GitHub
parent 578986ab1e
commit 142e3b09f8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 82 additions and 18 deletions

View file

@ -181,13 +181,13 @@ var ZoteroContextPane = new function () {
// It seems that changing `hidden` or `collapsed` values might
// be related with significant slow down when there are too many
// DOM nodes (i.e. 10k notes)
if (Zotero_Tabs.selectedIndex == 0) {
if (Zotero_Tabs.selectedType == 'library') {
_contextPaneSplitter.setAttribute('hidden', true);
_contextPane.setAttribute('collapsed', true);
_tabToolbar.hidden = true;
_tabCover.hidden = true;
}
else {
else if (Zotero_Tabs.selectedType == 'reader') {
var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
if (reader) {
_tabCover.hidden = false;
@ -217,7 +217,7 @@ var ZoteroContextPane = new function () {
});
_tabToolbar.hidden = false;
}
_selectItemContext(ids[0]);
_update();
}

View file

@ -30,11 +30,18 @@ var React = require('react');
var ReactDOM = require('react-dom');
import TabBar from 'components/tabBar';
const MAX_LOADED_TABS = 5;
const UNLOAD_UNUSED_AFTER = 86400; // 24h
var Zotero_Tabs = new function () {
Object.defineProperty(this, 'selectedID', {
get: () => this._selectedID
});
Object.defineProperty(this, 'selectedType', {
get: () => this._getTab(this._selectedID).tab.type
});
Object.defineProperty(this, 'selectedIndex', {
get: () => this._getTab(this._selectedID).tabIndex
});
@ -53,6 +60,10 @@ var Zotero_Tabs = new function () {
this._prevSelectedID = null;
this._history = [];
this._unloadInterval = setInterval(() => {
this.unloadUnusedTabs();
}, 60000); // Trigger every minute
this._getTab = function (id) {
var tabIndex = this._tabs.findIndex(tab => tab.id == id);
return { tab: this._tabs[tabIndex], tabIndex };
@ -90,8 +101,12 @@ var Zotero_Tabs = new function () {
this.getState = function () {
return this._tabs.map((tab) => {
let type = tab.type;
if (type === 'reader-unloaded') {
type = 'reader';
}
var o = {
type: tab.type,
type,
title: tab.title,
};
if (tab.data) {
@ -103,21 +118,21 @@ var Zotero_Tabs = new function () {
return o;
});
};
this.restoreState = function(tabs) {
for (let tab of tabs) {
this.restoreState = function (tabs) {
for (let i = 0; i < tabs.length; i++) {
let tab = tabs[i];
if (tab.type === 'library') {
this.rename('zotero-pane', tab.title);
}
else if (tab.type === 'reader') {
if (Zotero.Items.exists(tab.data.itemID)) {
Zotero.Reader.open(tab.data.itemID,
null,
{
title: tab.title,
openInBackground: !tab.selected
}
);
this.add({
type: 'reader-unloaded',
title: tab.title,
index: i,
data: tab.data
});
}
}
}
@ -292,10 +307,22 @@ var Zotero_Tabs = new function () {
* @param {Boolean} reopening
*/
this.select = function (id, reopening) {
var { tab } = this._getTab(id);
var { tab, tabIndex } = this._getTab(id);
if (!tab || tab.id === this._selectedID) {
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') {
var { tab: selectedTab } = this._getTab(this._selectedID);
selectedTab.lastFocusedElement = document.activeElement;
@ -312,12 +339,18 @@ var Zotero_Tabs = new function () {
}
tab.lastFocusedElement = null;
}
let tabNode = document.querySelector(`.tab[data-id="${tab.id}"]`);
let tabsContainerNode = document.querySelector('#tab-bar-container .tabs');
document.querySelector(`.tab[data-id="${tab.id}"]`).scrollIntoView({ behavior: 'smooth' });
// Allow React to create a tab node
setTimeout(() => {
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.
// TODO: `scroll-padding` since Firefox 68 can probably be used instead
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) {
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;
}
}, 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);
}
};
/**

View file

@ -969,6 +969,7 @@ class ReaderTab extends ReaderInstance {
// i.e. note-editor. There should be a better way to solve this
this._window.addEventListener('pointerup', (event) => {
if (this._window.Zotero_Tabs.selectedID === this.tabID
&& this._iframeWindow
&& event.target
&& event.target.closest
&& !event.target.closest('#outerContainer')) {