mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Save the treeview state periodically, and restore it automatically when reconnecting. Fixes #1253
This commit is contained in:
committed by
Dave Page
parent
44ef501283
commit
528ea88dec
331
web/pgadmin/static/js/tree/pgadmin_tree_save_state.js
Normal file
331
web/pgadmin/static/js/tree/pgadmin_tree_save_state.js
Normal file
@@ -0,0 +1,331 @@
|
||||
import _ from 'underscore';
|
||||
import $ from 'jquery';
|
||||
import url_for from 'sources/url_for';
|
||||
import gettext from 'sources/gettext';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
|
||||
const pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {};
|
||||
|
||||
|
||||
const browserTreeState = pgBrowser.browserTreeState = pgBrowser.browserTreeState || {};
|
||||
|
||||
_.extend(pgBrowser.browserTreeState, {
|
||||
|
||||
// Parent node to start saving / reloading the tree state
|
||||
parent: 'server',
|
||||
|
||||
// The original parent of the browser tree
|
||||
orig_parent: 'server_group',
|
||||
|
||||
// Stored tree state
|
||||
// Sample Object
|
||||
// {1:
|
||||
// 'paths': [
|
||||
// server_group/1,/server/1,/coll-database/1,/database/1,
|
||||
// server_group/1,/server/1,/coll-database/1,/database/2,
|
||||
// ],
|
||||
// 'selected': {
|
||||
// 'server/1': 'database/2',
|
||||
// 'database/1': 'table/1',
|
||||
// },
|
||||
// 'conn_status': {
|
||||
// 'database/1': 1,
|
||||
// 'database/2': 0 ,
|
||||
// },
|
||||
// }
|
||||
// Here key is server ID
|
||||
|
||||
stored_state: {},
|
||||
|
||||
// Previous tree state
|
||||
last_state: {},
|
||||
|
||||
// Current tree state
|
||||
current_state: {},
|
||||
|
||||
init: function() {
|
||||
|
||||
const save_tree_state_period = pgBrowser.get_preference('browser', 'browser_tree_state_save_interval');
|
||||
|
||||
if (!_.isUndefined(save_tree_state_period) && save_tree_state_period.value !== -1) {
|
||||
// Save the tree state every 30 seconds
|
||||
setInterval(this.save_state, (save_tree_state_period.value) * 1000);
|
||||
|
||||
// Fetch the tree last state while loading the browser tree
|
||||
this.fetch_state.apply(this);
|
||||
|
||||
pgBrowser.Events.on('pgadmin:browser:tree:expand-from-previous-tree-state',
|
||||
this.expand_from_previous_state, this);
|
||||
pgBrowser.Events.on('pgadmin:browser:tree:remove-from-tree-state',
|
||||
this.remove_from_cache, this);
|
||||
pgBrowser.Events.on('pgadmin:browser:tree:update-tree-state',
|
||||
this.update_cache, this);
|
||||
}
|
||||
},
|
||||
save_state: function() {
|
||||
|
||||
var self = pgBrowser.browserTreeState;
|
||||
if(self.last_state == JSON.stringify(self.current_state))
|
||||
return;
|
||||
|
||||
$.ajax({
|
||||
url: url_for('settings.save_tree_state'),
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(self.current_state),
|
||||
})
|
||||
.done(function() {
|
||||
self.last_state = JSON.stringify(self.current_state);
|
||||
})
|
||||
.fail(function(jqx) {
|
||||
var msg = jqx.responseText;
|
||||
/* Error from the server */
|
||||
if (jqx.status == 417 || jqx.status == 410 || jqx.status == 500) {
|
||||
try {
|
||||
var data = JSON.parse(jqx.responseText);
|
||||
msg = data.errormsg;
|
||||
} catch (e) {
|
||||
console.warn(e.stack || e);
|
||||
}
|
||||
}
|
||||
console.warn(
|
||||
gettext('Error saving the tree state."'), msg);
|
||||
});
|
||||
|
||||
},
|
||||
fetch_state: function() {
|
||||
|
||||
var self = this;
|
||||
$.ajax({
|
||||
url: url_for('settings.get_tree_state'),
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
})
|
||||
.done(function(res) {
|
||||
self.stored_state = res;
|
||||
})
|
||||
.fail(function(jqx) {
|
||||
var msg = jqx.responseText;
|
||||
/* Error from the server */
|
||||
if (jqx.status == 417 || jqx.status == 410 || jqx.status == 500) {
|
||||
try {
|
||||
var data = JSON.parse(jqx.responseText);
|
||||
msg = data.errormsg;
|
||||
} catch (e) {
|
||||
console.warn(e.stack || e);
|
||||
}
|
||||
}
|
||||
console.warn(
|
||||
gettext('Error fetching the tree state."'), msg);
|
||||
});
|
||||
},
|
||||
update_cache: function(item) {
|
||||
|
||||
let data = item && pgBrowser.tree.itemData(item),
|
||||
node = data && pgBrowser.Nodes[data._type],
|
||||
treeHierarchy = node.getTreeNodeHierarchy(item),
|
||||
topParent = undefined,
|
||||
pathIDs = pgBrowser.tree.pathId(item),
|
||||
oldPath = pathIDs.join(),
|
||||
path = [],
|
||||
tmpIndex = -1;
|
||||
|
||||
// If no parent or the server not in tree hierarchy then return
|
||||
if (!pgBrowser.tree.hasParent(item) || !(this.parent in treeHierarchy))
|
||||
return;
|
||||
|
||||
topParent = treeHierarchy[this.parent]['_id'];
|
||||
|
||||
|
||||
if (pgBrowser.tree.isOpen(item)) {
|
||||
// Store paths
|
||||
|
||||
pathIDs.push(data.id);
|
||||
path = pathIDs.join();
|
||||
|
||||
if (!(topParent in this.current_state)) {
|
||||
this.current_state = {};
|
||||
this.current_state[topParent] = {'paths': [], 'selected': {}, 'conn_status': {}};
|
||||
}
|
||||
|
||||
// IF the current path is already saved then return
|
||||
let index = _.find(this.current_state[topParent]['paths'], function(tData) {
|
||||
return (tData.search(path) !== -1);
|
||||
});
|
||||
if(!_.isUndefined(index))
|
||||
return;
|
||||
|
||||
// Add / Update the current item into the tree path
|
||||
if (!_.isUndefined(this.current_state[topParent]['paths'])) {
|
||||
tmpIndex = this.current_state[topParent]['paths'].indexOf(oldPath);
|
||||
} else {
|
||||
this.current_state[topParent]['paths'] = [];
|
||||
}
|
||||
if (tmpIndex !== -1) {
|
||||
this.current_state[topParent]['paths'][tmpIndex] = path;
|
||||
}
|
||||
else {
|
||||
this.current_state[topParent]['paths'].push(path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Store current selected item and database connection status
|
||||
this.update_database_conn_status(treeHierarchy);
|
||||
this.update_current_selected_item(treeHierarchy);
|
||||
|
||||
},
|
||||
remove_from_cache: function(item) {
|
||||
let self= this,
|
||||
treeData = self.stored_state || {},
|
||||
data = item && pgBrowser.tree.itemData(item),
|
||||
node = data && pgBrowser.Nodes[data._type],
|
||||
treeHierarchy = node && node.getTreeNodeHierarchy(item);
|
||||
|
||||
if (!pgBrowser.tree.hasParent(item) || !(self.parent in treeHierarchy))
|
||||
return;
|
||||
|
||||
let topParent = treeHierarchy && treeHierarchy[self.parent]['_id'],
|
||||
origParent = treeHierarchy && treeHierarchy[self.orig_parent]['id'];
|
||||
|
||||
this.update_database_conn_status(treeHierarchy);
|
||||
|
||||
if (data._type == self.parent || data._type == 'database') {
|
||||
if (topParent in treeData && 'paths' in treeData[topParent]) {
|
||||
treeData[topParent]['paths'] = self.current_state[topParent]['paths'];
|
||||
self.save_state();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (pgBrowser.tree.isClosed(item)) {
|
||||
let tmpTreeData = self.current_state[topParent]['paths'];
|
||||
if (!_.isUndefined(tmpTreeData) && !_.isUndefined(tmpTreeData.length)) {
|
||||
let tcnt = 0,
|
||||
tmpItemDataStr = undefined;
|
||||
_.each(tmpTreeData, function(tData) {
|
||||
if (_.isUndefined(tData))
|
||||
return;
|
||||
|
||||
let tmpItemData = tData.split(',');
|
||||
|
||||
if (tmpItemData.indexOf(data.id) !== -1 ) {
|
||||
let index = tmpItemData.indexOf(data.id);
|
||||
tmpItemData.splice(index);
|
||||
tmpItemDataStr = tmpItemData.join();
|
||||
|
||||
if (tmpItemDataStr == origParent)
|
||||
self.current_state[topParent]['paths'].splice(tData, 1);
|
||||
else
|
||||
self.current_state[topParent]['paths'][tcnt] = tmpItemDataStr;
|
||||
}
|
||||
tcnt ++;
|
||||
});
|
||||
|
||||
if (tmpItemDataStr !== undefined) {
|
||||
let tmpIndex = _.find(tmpTreeData, function(tData) {
|
||||
return (tData.search(tmpItemDataStr) !== -1);
|
||||
});
|
||||
|
||||
if(tmpIndex && tmpIndex !== tmpTreeData.indexOf(tmpItemDataStr))
|
||||
self.current_state[topParent]['paths'].splice(tmpIndex, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
expand_from_previous_state: function(item) {
|
||||
let self = this,
|
||||
treeData = self.stored_state || {},
|
||||
data = item && pgBrowser.tree.itemData(item),
|
||||
node = data && pgBrowser.Nodes[data._type],
|
||||
treeHierarchy = node && node.getTreeNodeHierarchy(item);
|
||||
|
||||
if (!pgBrowser.tree.hasParent(item) || !(self.parent in treeHierarchy))
|
||||
return;
|
||||
|
||||
// If the server node is open then only we should populate the tree
|
||||
if (data['_type'] == self.parent && (pgBrowser.tree.isOpen(item) === false))
|
||||
return;
|
||||
|
||||
let tmpTreeData = treeData[treeHierarchy[self.parent]['_id']];
|
||||
|
||||
|
||||
// If the server node is open then only we should populate the tree
|
||||
if (data['_type'] == 'database' && tmpTreeData && 'conn_status' in tmpTreeData &&
|
||||
tmpTreeData['conn_status'][data['id']] === 0 )
|
||||
return;
|
||||
|
||||
|
||||
if (!_.isUndefined(tmpTreeData) && ('paths' in tmpTreeData) && !_.isUndefined(tmpTreeData['paths'].length)) {
|
||||
_.each(tmpTreeData['paths'], function(tData) {
|
||||
if (_.isUndefined(tData))
|
||||
return;
|
||||
|
||||
let tmpItemData = tData.split(',');
|
||||
|
||||
// If the item is in the lastTreeState then open it
|
||||
if (tmpItemData.indexOf(data.id) !== -1 ) {
|
||||
let index = tmpItemData.indexOf(data.id);
|
||||
pgBrowser.tree.toggle(item);
|
||||
|
||||
if (index == (tmpItemData.length - 1 )) {
|
||||
let tIndex = treeData[treeHierarchy[self.parent]['_id']]['paths'].indexOf(tData);
|
||||
treeData[treeHierarchy[self.parent]['_id']]['paths'].splice(tIndex, 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Select the previously selected item
|
||||
this.select_tree_item(item);
|
||||
|
||||
},
|
||||
update_database_conn_status: function(treeHierarchy) {
|
||||
if ('database' in treeHierarchy) {
|
||||
let databaseItem = treeHierarchy['database']['id'],
|
||||
topParent = treeHierarchy && treeHierarchy[this.parent]['_id'];
|
||||
|
||||
if (treeHierarchy['database'].connected) {
|
||||
this.current_state[topParent]['conn_status'][databaseItem] = 1;
|
||||
}
|
||||
else {
|
||||
this.current_state[topParent]['conn_status'][databaseItem] = 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
update_current_selected_item(treeHierarchy) {
|
||||
if ('database' in treeHierarchy) {
|
||||
let databaseItem = treeHierarchy['database']['id'],
|
||||
topParent = treeHierarchy && treeHierarchy[this.parent]['_id'],
|
||||
selectedItem = pgBrowser.tree.itemData(pgBrowser.tree.selected());
|
||||
|
||||
selectedItem = selectedItem ? selectedItem.id : undefined;
|
||||
|
||||
if (!_.isUndefined(selectedItem)) {
|
||||
this.current_state[topParent]['selected'][treeHierarchy[this.parent]['id']] = selectedItem;
|
||||
this.current_state[topParent]['selected'][databaseItem] = selectedItem;
|
||||
}
|
||||
}
|
||||
},
|
||||
select_tree_item(item) {
|
||||
let treeData = this.stored_state || {},
|
||||
data = item && pgBrowser.tree.itemData(item),
|
||||
node = data && pgBrowser.Nodes[data._type],
|
||||
treeHierarchy = node && node.getTreeNodeHierarchy(item),
|
||||
tmpTreeData = treeData[treeHierarchy[this.parent]['_id']];
|
||||
|
||||
|
||||
if ('database' in treeHierarchy) {
|
||||
let databaseItem = treeHierarchy['database']['id'];
|
||||
|
||||
if (tmpTreeData && 'selected' in tmpTreeData && databaseItem in tmpTreeData['selected']) {
|
||||
if (tmpTreeData['selected'][databaseItem] == data.id) {
|
||||
pgBrowser.tree.select(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export {pgBrowser, browserTreeState};
|
||||
Reference in New Issue
Block a user