rework panel/LogView

use the view, viewmodel, controller style code, which makes the logic
much more understandable

this adds two features to it, namely a viewBuffer range, which controls
the distance to the end of the data on which the panel loads the next data
and an optional failCallback, which will be used for the cephlog

this also fixes #2151

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
This commit is contained in:
Dominik Csapak 2019-04-02 12:16:49 +02:00 committed by Thomas Lamprecht
parent f2a630d4c6
commit 65bb3b678f
2 changed files with 237 additions and 251 deletions

View File

@ -5,270 +5,256 @@
*/
Ext.define('Proxmox.panel.LogView', {
extend: 'Ext.panel.Panel',
alias: ['widget.proxmoxLogView'],
xtype: 'proxmoxLogView',
pageSize: 500,
viewBuffer: 50,
lineHeight: 16,
viewInfo: undefined,
scrollToEnd: true,
autoScroll: true,
// callback for load failure, used for ceph
failCallback: undefined,
layout: 'auto',
controller: {
xclass: 'Ext.app.ViewController',
bodyPadding: 5,
getMaxDown: function(scrollToEnd) {
updateParams: function() {
var me = this;
var target = me.getTargetEl();
var dom = target.dom;
if (scrollToEnd) {
dom.scrollTop = dom.scrollHeight - dom.clientHeight;
}
var maxDown = dom.scrollHeight - dom.clientHeight -
dom.scrollTop;
return maxDown;
},
updateView: function(start, end, total, text) {
var me = this;
if (me.destroyed) { // return if element is not there anymore
var viewModel = me.getViewModel();
var since = viewModel.get('since');
var until = viewModel.get('until');
if (viewModel.get('hide_timespan')) {
return;
}
var el = me.dataCmp.el;
if (me.viewInfo && me.viewInfo.start === start &&
me.viewInfo.end === end && me.viewInfo.total === total &&
me.viewInfo.textLength === text.length) {
return; // same content
if (since > until) {
Ext.Msg.alert('Error', 'Since date must be less equal than Until date.');
return;
}
var maxDown = me.getMaxDown();
var scrollToEnd = (maxDown <= 0) && me.scrollToEnd;
el.setStyle('padding-top', (start*me.lineHeight).toString() + 'px');
el.update(text);
me.dataCmp.setHeight(total*me.lineHeight);
if (scrollToEnd) {
me.getMaxDown(true);
}
me.viewInfo = {
start: start,
end: end,
total: total,
textLength: text.length
};
viewModel.set('params.since', Ext.Date.format(since, 'Y-m-d'));
viewModel.set('params.until', Ext.Date.format(until, 'Y-m-d') + ' 23:59:59');
me.getView().loadTask.delay(200);
},
doAttemptLoad: function(start) {
scrollPosBottom: function() {
var view = this.getView();
var pos = view.getScrollY();
var maxPos = view.getScrollable().getMaxPosition().y;
return maxPos - pos;
},
updateView: function(text, first, total) {
var me = this;
var view = me.getView();
var viewModel = me.getViewModel();
var content = me.lookup('content');
var data = viewModel.get('data');
var req_params = {
start: start,
limit: me.pageSize
};
if (me.log_select_timespan) {
// always show log until the end of the selected day
req_params.until = Ext.Date.format(me.until_date, 'Y-m-d') + ' 23:59:59';
req_params.since = Ext.Date.format(me.since_date, 'Y-m-d');
if (first === data.first && total === data.total && text.length === data.textlen) {
return; // same content, skip setting and scrolling
}
viewModel.set('data', {
first: first,
total: total,
textlen: text.length
});
var scrollPos = me.scrollPosBottom();
content.update(text);
if (view.scrollToEnd && scrollPos <= 0) {
// we use setTimeout to work around scroll handling on touchscreens
setTimeout(function() { view.scrollTo(0, Infinity); }, 10);
}
},
doLoad: function() {
var me = this;
var view = me.getView();
var viewModel = me.getViewModel();
Proxmox.Utils.API2Request({
url: me.url,
params: req_params,
url: me.getView().url,
params: viewModel.get('params'),
method: 'GET',
success: function(response) {
Proxmox.Utils.setErrorMask(me, false);
var list = response.result.data;
var total = response.result.total;
var first = 0, last = 0;
var text = '';
Ext.Array.each(list, function(item) {
if (!first|| item.n < first) {
first = item.n;
var lines = new Array();
var first = Infinity;
Ext.Array.each(response.result.data, function(line) {
if (first > line.n) {
first = line.n;
}
if (!last || item.n > last) {
last = item.n;
}
text = text + Ext.htmlEncode(item.t) + "<br>";
lines[line.n - 1] = Ext.htmlEncode(line.t);
});
if (first && last && total) {
me.updateView(first -1 , last -1, total, text);
} else {
me.updateView(0, 0, 0, '');
}
lines.length = total;
me.updateView(lines.join('<br>'), first - 1, total);
},
failure: function(response) {
if (view.failCallback) {
view.failCallback(response);
} else {
var msg = response.htmlStatus;
Proxmox.Utils.setErrorMask(me, msg);
}
}
});
},
attemptLoad: function(start) {
onScroll: function(x, y) {
var me = this;
if (!me.loadTask) {
me.loadTask = Ext.create('Ext.util.DelayedTask', me.doAttemptLoad, me, []);
var view = me.getView();
var viewModel = me.getViewModel();
var lineHeight = view.lineHeight;
var line = view.getScrollY()/lineHeight;
var start = viewModel.get('params.start');
var limit = viewModel.get('params.limit');
var viewLines = view.getHeight()/lineHeight;
var viewStart = Math.max(parseInt(line - 1 - view.viewBuffer, 10), 0);
var viewEnd = parseInt(line + viewLines + 1 + view.viewBuffer, 10);
if (viewStart < start || viewEnd > (start+limit)) {
viewModel.set('params.start',
Math.max(parseInt(line - limit/2 + 10, 10), 0));
view.loadTask.delay(200);
}
me.loadTask.delay(200, me.doAttemptLoad, me, [start]);
},
requestUpdate: function(top, force) {
init: function(view) {
var me = this;
if (top === undefined) {
var target = me.getTargetEl();
top = target.dom.scrollTop;
}
var viewStart = parseInt((top / me.lineHeight) - 1, 10);
if (viewStart < 0) {
viewStart = 0;
}
var viewEnd = parseInt(((top + me.getHeight())/ me.lineHeight) + 1, 10);
var info = me.viewInfo;
if (info && !force) {
if (viewStart >= info.start && viewEnd <= info.end) {
return;
}
}
var line = parseInt((top / me.lineHeight) - (me.pageSize / 2) + 10, 10);
if (line < 0) {
line = 0;
}
me.attemptLoad(line);
},
afterRender: function() {
var me = this;
me.callParent(arguments);
Ext.Function.defer(function() {
var target = me.getTargetEl();
target.on('scroll', function(e) {
me.requestUpdate();
});
me.requestUpdate(0);
}, 20);
},
initComponent : function() {
/*jslint confusion: true */
var me = this;
if (!me.url) {
if (!view.url) {
throw "no url specified";
}
// show logs from today back to 3 days ago per default
me.until_date = new Date();
me.since_date = new Date();
me.since_date.setDate(me.until_date.getDate() - 3);
var viewModel = this.getViewModel();
var since = new Date();
since.setDate(since.getDate() - 3);
viewModel.set('until', new Date());
viewModel.set('since', since);
viewModel.set('params.limit', view.pageSize);
viewModel.set('hide_timespan', !view.log_select_timespan);
me.lookup('content').setStyle('line-height', view.lineHeight + 'px');
me.dataCmp = Ext.create('Ext.Component', {
style: 'font:normal 11px tahoma, arial, verdana, sans-serif;' +
'line-height: ' + me.lineHeight.toString() + 'px; white-space: pre;'
});
view.loadTask = new Ext.util.DelayedTask(me.doLoad, me);
me.task = Ext.TaskManager.start({
me.updateParams();
view.task = Ext.TaskManager.start({
run: function() {
if (!me.isVisible() || !me.scrollToEnd || !me.viewInfo) {
if (!view.isVisible() || !view.scrollToEnd) {
return;
}
var maxDown = me.getMaxDown();
if (maxDown > 0) {
return;
if (me.scrollPosBottom() <= 1) {
view.loadTask.delay(200);
}
me.requestUpdate(undefined, true);
},
interval: 1000
});
}
},
Ext.apply(me, {
items: me.dataCmp,
listeners: {
destroy: function() {
onDestroy: function() {
var me = this;
me.loadTask.cancel();
Ext.TaskManager.stop(me.task);
}
}
});
},
if (me.log_select_timespan) {
me.tbar = ['->','Since: ',
// for user to initiate a load from outside
requestUpdate: function() {
var me = this;
me.loadTask.delay(200);
},
viewModel: {
data: {
until: null,
since: null,
hide_timespan: false,
data: {
start: 0,
total: 0,
textlen: 0
},
params: {
start: 0,
limit: 500,
}
}
},
layout: 'auto',
bodyPadding: 5,
scrollable: {
x: 'auto',
y: 'auto',
listeners: {
// we have to have this here, since we cannot listen to events
// of the scroller in the viewcontroller (extjs bug?), nor does
// the panel have a 'scroll' event'
scroll: {
fn: function(scroller, x, y) {
var controller = this.component.getController();
if (controller) { // on destroy, controller can be gone
controller.onScroll(x,y);
}
},
buffer: 200
},
}
},
tbar: {
bind: {
hidden: '{hide_timespan}'
},
items: [
'->',
'Since: ',
{
xtype: 'datefield',
maxValue: me.until_date,
value: me.since_date,
name: 'since_date',
reference: 'since',
format: 'Y-m-d',
listeners: {
select: function(field, date) {
me.since_date_selected = date;
var until_field = field.up().down('field[name=until_date]');
if (date > until_field.getValue()) {
until_field.setValue(date);
}
}
bind: {
value: '{since}',
maxValue: '{until}'
}
},
'Until: ',
{
xtype: 'datefield',
maxValue: me.until_date,
value: me.until_date,
name: 'until_date',
reference: 'until',
format: 'Y-m-d',
listeners: {
select: function(field, date) {
var since_field = field.up().down('field[name=since_date]');
if (date < since_field.getValue()) {
since_field.setValue(date);
}
}
bind: {
value: '{until}',
minValue: '{since}'
}
},
{
xtype: 'button',
text: 'Update',
handler: function() {
var until_field = me.down('field[name=until_date]');
var since_field = me.down('field[name=since_date]');
if (until_field.getValue() < since_field.getValue()) {
Ext.Msg.alert('Error',
'Since date must be less equal than Until date.');
until_field.setValue(me.until_date);
since_field.setValue(me.since_date);
} else {
me.until_date = until_field.getValue();
me.since_date = since_field.getValue();
me.requestUpdate();
}
}
}
];
handler: 'updateParams'
}
],
},
me.callParent();
items: [
{
xtype: 'box',
reference: 'content',
style: {
font: 'normal 11px tahoma, arial, verdana, sans-serif',
'white-space': 'pre'
},
}
]
});

View File

@ -198,8 +198,8 @@ Ext.define('Proxmox.window.TaskViewer', {
var status = statgrid.getObjectValue('status');
if (status === 'stopped') {
logView.requestUpdate(undefined, true);
logView.scrollToEnd = false;
logView.requestUpdate();
statstore.stopUpdate();
me.taskDone(statgrid.getObjectValue('exitstatus') == 'OK');
}