diff --git a/api/js/etemplate/et2_extension_nextmatch.js b/api/js/etemplate/et2_extension_nextmatch.js index 69713f1c79..7295596d2b 100644 --- a/api/js/etemplate/et2_extension_nextmatch.js +++ b/api/js/etemplate/et2_extension_nextmatch.js @@ -453,7 +453,8 @@ var et2_nextmatch = /** @class */ (function (_super) { jQuery(this.getInstanceManager().DOMContainer.parentNode).one('show.et2_nextmatch', // Important to use anonymous function instead of just 'this.refresh' because // of the parameters passed - jQuery.proxy(function () { this.refresh(); }, this)); + function () { this.nm.refresh(this.ids, this.type); } + .bind({ nm: this, ids: _row_ids, type: _type })); return; } if (typeof _type == 'undefined') @@ -528,14 +529,20 @@ var et2_nextmatch = /** @class */ (function (_super) { * @param uid */ et2_nextmatch.prototype.refresh_add = function (uid) { - var entry = this.controller._selectionMgr._getRegisteredRowsEntry(uid); - // Insert at the top of the list - entry.idx = 0; - this.controller._insertDataRow(entry, true); - if (this.onadd && !this.onadd(entry)) { - this.controller._grid.deleteRow(entry.idx); + var index = 0; + var appname = this._get_appname(); + if (appname && this.egw().window.app[appname] && typeof this.egw().window.app[appname].nm_refresh_add == "function") { + var sort = Object.values(this.controller._indexMap).map(function (e) { return ({ index: e.idx, uid: e.uid }); }); + index = this.egw().window.app[appname].nm_refresh_add(this, uid, sort); + } + // App cancelled the add + if (index === false) { return; } + // Insert at the top of the list, or where app said + var entry = this.controller._selectionMgr._getRegisteredRowsEntry(uid); + entry.idx = typeof index == "number" ? index : 0; + this.controller._insertDataRow(entry, true); // Set "new entry" class - but it has to stay so register and re-add it after the data is there entry.row.tr.addClass("new_entry"); var callback = function (data) { @@ -544,6 +551,18 @@ var et2_nextmatch = /** @class */ (function (_super) { }; this.egw().dataRegisterUID(uid, callback, this, this.getInstanceManager().etemplate_exec_id, this.id); }; + et2_nextmatch.prototype._get_appname = function () { + var app = ''; + var list = []; + list = et2_csvSplit(this.options.settings.columnselection_pref, 2, "."); + if (this.options.settings.columnselection_pref.indexOf('nextmatch') == 0) { + app = list[0].substring('nextmatch'.length + 1); + } + else { + app = list[0]; + } + return app; + }; /** * Gets the selection * @@ -1385,9 +1404,9 @@ var et2_nextmatch = /** @class */ (function (_super) { et2_nextmatch.prototype._set_autorefresh = function (time) { // Store preference var refresh_preference = "nextmatch-" + this.options.settings.columnselection_pref + "-autorefresh"; - var app = this.options.template.split("."); + var app = this._get_appname(); if (this._get_autorefresh() != time) { - this.egw().set_preference(app[0], refresh_preference, time); + this.egw().set_preference(app, refresh_preference, time); } // Start / update timer if (this._autorefresh_timer) { @@ -1427,8 +1446,7 @@ var et2_nextmatch = /** @class */ (function (_super) { */ et2_nextmatch.prototype._get_autorefresh = function () { var refresh_preference = "nextmatch-" + this.options.settings.columnselection_pref + "-autorefresh"; - var app = this.options.template.split("."); - return this.egw().preference(refresh_preference, app[0]); + return this.egw().preference(refresh_preference, this._get_appname()); }; /** * When the template attribute is set, the nextmatch widget tries to load diff --git a/api/js/etemplate/et2_extension_nextmatch.ts b/api/js/etemplate/et2_extension_nextmatch.ts index 8e877b9f47..d170924827 100644 --- a/api/js/etemplate/et2_extension_nextmatch.ts +++ b/api/js/etemplate/et2_extension_nextmatch.ts @@ -703,7 +703,8 @@ export class et2_nextmatch extends et2_DOMWidget implements et2_IResizeable, et2 jQuery(this.getInstanceManager().DOMContainer.parentNode).one('show.et2_nextmatch', // Important to use anonymous function instead of just 'this.refresh' because // of the parameters passed - jQuery.proxy(function() {this.refresh();},this) + function() {this.nm.refresh(this.ids, this.type);} + .bind({nm: this, ids: _row_ids, type: _type}) ); return; } @@ -796,16 +797,24 @@ export class et2_nextmatch extends et2_DOMWidget implements et2_IResizeable, et2 */ protected refresh_add(uid:string) { - var entry = this.controller._selectionMgr._getRegisteredRowsEntry(uid); - // Insert at the top of the list - entry.idx = 0; - this.controller._insertDataRow(entry,true); - - if(this.onadd && !this.onadd(entry)) + let index = 0; + let appname = this._get_appname(); + if(appname && this.egw().window.app[appname] && typeof this.egw().window.app[appname].nm_refresh_add == "function") + { + let sort = Object.values(this.controller._indexMap).map(e => ({index:e.idx, uid:e.uid})); + index = this.egw().window.app[appname].nm_refresh_add(this, uid, sort) + } + + // App cancelled the add + if(index === false) { - this.controller._grid.deleteRow(entry.idx); return; } + + // Insert at the top of the list, or where app said + var entry = this.controller._selectionMgr._getRegisteredRowsEntry(uid); + entry.idx = typeof index == "number" ? index : 0; + this.controller._insertDataRow(entry,true); // Set "new entry" class - but it has to stay so register and re-add it after the data is there entry.row.tr.addClass("new_entry"); @@ -816,6 +825,23 @@ export class et2_nextmatch extends et2_DOMWidget implements et2_IResizeable, et2 this.egw().dataRegisterUID(uid, callback, this, this.getInstanceManager().etemplate_exec_id, this.id); } + private _get_appname() + { + let app = ''; + let list = []; + + list = et2_csvSplit(this.options.settings.columnselection_pref, 2, "."); + if(this.options.settings.columnselection_pref.indexOf('nextmatch') == 0) + { + app = list[0].substring('nextmatch'.length + 1); + } + else + { + app = list[0]; + } + return app; + } + /** * Gets the selection * @@ -1902,10 +1928,10 @@ export class et2_nextmatch extends et2_DOMWidget implements et2_IResizeable, et2 { // Store preference const refresh_preference = "nextmatch-" + this.options.settings.columnselection_pref + "-autorefresh"; - const app = this.options.template.split("."); + const app = this._get_appname(); if(this._get_autorefresh() != time) { - this.egw().set_preference(app[0],refresh_preference,time); + this.egw().set_preference(app,refresh_preference,time); } // Start / update timer @@ -1953,8 +1979,7 @@ export class et2_nextmatch extends et2_DOMWidget implements et2_IResizeable, et2 _get_autorefresh( ) { const refresh_preference = "nextmatch-" + this.options.settings.columnselection_pref + "-autorefresh"; - const app = this.options.template.split("."); - return this.egw().preference(refresh_preference,app[0]); + return this.egw().preference(refresh_preference,this._get_appname()); } /** diff --git a/api/js/jsapi/app_base.js b/api/js/jsapi/app_base.js index 9076c95267..a6ee02ed07 100644 --- a/api/js/jsapi/app_base.js +++ b/api/js/jsapi/app_base.js @@ -239,38 +239,41 @@ var AppJS = (function(){ "use strict"; return Class.extend( } }, + /** + * Callback from nextmatch so application can have some control over + * where new rows (added via push) are added. This is only called when + * the type is "add". + * + * @param nm Nextmatch the entry is going to be added to + * @param uid + * @param current_order + */ + nm_refresh_add: function(nm, uid, current_order) + { + // Do we have a modified field so we can check nm sort order? + if(this.modification_field_name) + { + let value = nm.getValue(); + let sort = value.sort || {}; + + if(sort && sort.id == this.modification_field_name && sort.asc == false) + { + // Sorting by modification time, DESC. Put it at the top. + return 0; + } + } + // Don't actually add it in. + return false; + }, + /** * Get (possible) app-specific uid * * @param {object} pushData see push method for individual attributes */ - uid(pushData) + uid: function(pushData) { - return pushData.app + '::' + pushData.id; - }, - - /** - * Method called after apps push implementation checked visibility - * - * @param {et2_nextmatch} nm - * @param pushData see push method for individual attributes - * @todo implement better way to update nextmatch widget without disturbing the user / state - * @todo show indicator that an update has happend - * @todo rate-limit update frequency - */ - updateList: function(nm, pushData) - { - switch (pushData.type) - { - case 'add': - case 'unknown': - nm.applyFilters(); - break; - - default: - egw.dataRefreshUID(this.uid(pushData)); - break; - } + return pushData.app + "::" + pushData.id; }, /** diff --git a/api/js/jsapi/egw_app.js b/api/js/jsapi/egw_app.js index f275f9b103..f0a54884a4 100644 --- a/api/js/jsapi/egw_app.js +++ b/api/js/jsapi/egw_app.js @@ -15,6 +15,9 @@ require("jquery"); require("jqueryui"); require("../jsapi/egw_global"); var etemplate2_1 = require("../etemplate/etemplate2"); +var et2_extension_nextmatch_1 = require("../etemplate/et2_extension_nextmatch"); +var et2_widget_dialog_1 = require("../etemplate/et2_widget_dialog"); +var et2_core_widget_1 = require("../etemplate/et2_core_widget"); /** * Common base class for application javascript * Each app should extend as needed. @@ -50,12 +53,14 @@ var EgwApp = /** @class */ (function () { * Initialization and setup goes here, but the etemplate2 object * is not yet ready. */ - function EgwApp(appname) { + function EgwApp(appname, modified_field) { + if (modified_field === void 0) { modified_field = ""; } /** * Mailvelope "egroupware" Keyring */ this.mailvelope_keyring = undefined; this.appname = appname; + this.modification_field_name = modified_field; this.egw = egw(this.appname, window); // Initialize sidebox for non-popups. // ID set server side @@ -172,26 +177,29 @@ var EgwApp = /** @class */ (function () { return pushData.app + '::' + pushData.id; }; /** - * Method called after apps push implementation checked visibility + * Callback from nextmatch so application can have some control over + * where new rows (added via push) are added. This is only called when + * the type is "add". * - * @param {et2_nextmatch} nm - * @param pushData see push method for individual attributes - * @todo implement better way to update nextmatch widget without disturbing the user / state - * @todo show indicator that an update has happend - * @todo rate-limit update frequency + * @param nm Nextmatch the entry is going to be added to + * @param uid + * @param current_order */ - EgwApp.prototype.updateList = function (nm, pushData) { - switch (pushData.type) { - case 'add': - nm.refresh(this.uid(pushData), 'add'); - break; - case 'unknown': - nm.applyFilters(); - break; - default: - egw.dataRefreshUID(this.uid(pushData)); - break; + EgwApp.prototype.nm_refresh_add = function (nm, uid, current_order) { + var _a; + // Do we have a modified field so we can check nm sort order? + if (this.modification_field_name) { + var value = nm.getValue(); + var sort = ((_a = value) === null || _a === void 0 ? void 0 : _a.sort) || {}; + if (sort && sort.id == this.modification_field_name && sort.asc == false) { + // Sorting by modification time, DESC. Put it at the top. + return 0; + } + // Don't actually add it in. + return false; } + // Just put it in at the top + return 0; }; /** * Open an entry. @@ -228,11 +236,11 @@ var EgwApp = /** @class */ (function () { if (typeof confirm_msg != 'undefined') { var that = this; var action_id = _action.id; - et2_dialog.show_dialog(function (button_id, value) { - if (button_id != et2_dialog.NO_BUTTON) { + et2_widget_dialog_1.et2_dialog.show_dialog(function (button_id, value) { + if (button_id != et2_widget_dialog_1.et2_dialog.NO_BUTTON) { that._do_action(action_id, _elems); } - }, confirm_msg, egw.lang('Confirmation required'), et2_dialog.BUTTONS_YES_NO, et2_dialog.QUESTION_MESSAGE); + }, confirm_msg, egw.lang('Confirmation required'), et2_widget_dialog_1.et2_dialog.BUTTONS_YES_NO, et2_widget_dialog_1.et2_dialog.QUESTION_MESSAGE); } else if (typeof this._do_action == 'function') { this._do_action(_action.id, _elems); @@ -305,7 +313,7 @@ var EgwApp = /** @class */ (function () { } _widget.applyFilters(state.state || state.filter || {}); nextmatched = true; - }, this, et2_nextmatch); + }, this, et2_extension_nextmatch_1.et2_nextmatch); if (nextmatched) return false; } @@ -342,7 +350,7 @@ var EgwApp = /** @class */ (function () { for (var i = 0; i < et2.length; i++) { et2[i].widgetContainer.iterateOver(function (_widget) { state = _widget.getValue(); - }, this, et2_nextmatch); + }, this, et2_extension_nextmatch_1.et2_nextmatch); } return state; }; @@ -646,7 +654,7 @@ var EgwApp = /** @class */ (function () { var apps = egw().user('apps'); var is_admin = (typeof apps['admin'] != "undefined"); if (is_admin) { - this.favorite_popup.group = et2_createWidget("select-account", { + this.favorite_popup.group = et2_core_widget_1.et2_createWidget("select-account", { id: "favorite[group]", account_type: "groups", empty_label: "Groups", @@ -762,7 +770,7 @@ var EgwApp = /** @class */ (function () { line.addClass('loading'); // Make sure first var do_delete = function (button_id) { - if (button_id != et2_dialog.YES_BUTTON) { + if (button_id != et2_widget_dialog_1.et2_dialog.YES_BUTTON) { line.removeClass('loading'); return; } @@ -785,7 +793,7 @@ var EgwApp = /** @class */ (function () { }, jQuery(trash).parentsUntil("li").parent(), true, jQuery(trash).parentsUntil("li").parent()); request.sendRequest(true); }; - et2_dialog.show_dialog(do_delete, (egw.lang("Delete") + " " + name + "?"), egw.lang("Delete"), et2_dialog.YES_NO, et2_dialog.QUESTION_MESSAGE); + et2_widget_dialog_1.et2_dialog.show_dialog(do_delete, (egw.lang("Delete") + " " + name + "?"), egw.lang("Delete"), et2_widget_dialog_1.et2_dialog.YES_NO, et2_widget_dialog_1.et2_dialog.QUESTION_MESSAGE); return false; }; /** @@ -1271,15 +1279,15 @@ var EgwApp = /** @class */ (function () { */ EgwApp.prototype.mailvelopeDeleteBackup = function () { var self = this; - et2_dialog.show_dialog(function (_button_id) { - if (_button_id == et2_dialog.YES_BUTTON) { + et2_widget_dialog_1.et2_dialog.show_dialog(function (_button_id) { + if (_button_id == et2_widget_dialog_1.et2_dialog.YES_BUTTON) { self._mailvelopeBackupFileOperator(undefined, 'DELETE', function () { self.egw.message(self.egw.lang('The backup key has been deleted.')); }, function (_err) { self.egw.message(self.egw.lang('Was not able to delete the backup key because %1', _err)); }); } - }, self.egw.lang('Are you sure, you would like to delete the backup key?'), self.egw.lang('Delete backup key'), {}, et2_dialog.BUTTONS_YES_CANCEL, et2_dialog.QUESTION_MESSAGE, undefined, self.egw); + }, self.egw.lang('Are you sure, you would like to delete the backup key?'), self.egw.lang('Delete backup key'), {}, et2_widget_dialog_1.et2_dialog.BUTTONS_YES_CANCEL, et2_widget_dialog_1.et2_dialog.QUESTION_MESSAGE, undefined, self.egw); }; /** * Create mailvelope restore dialog @@ -1335,7 +1343,7 @@ var EgwApp = /** @class */ (function () { { label: "Backup Key", image: "save", onclick: "app." + appname + ".mailvelopeCreateBackupDialog('#_mvelo', false)" } ]; var dialog = function (_content, _callback) { - return et2_createWidget("dialog", { + return et2_core_widget_1.et2_createWidget("dialog", { callback: function (_button_id, _value) { if (typeof _callback == "function") { _callback.call(this, _button_id, _value.value); @@ -1381,7 +1389,7 @@ var EgwApp = /** @class */ (function () { { "text": egw.lang('Close'), id: 'close', image: 'cancelled' } ]; var dialog = function (_content, _callback) { - return et2_createWidget("dialog", { + return et2_core_widget_1.et2_createWidget("dialog", { callback: function (_button_id, _value) { if (typeof _callback == "function") { _callback.call(this, _button_id, _value.value); @@ -1416,11 +1424,11 @@ var EgwApp = /** @class */ (function () { else if (typeof InstallTrigger != 'undefined' && InstallTrigger.enabled()) { InstallTrigger.install({ mailvelope: "https://download.mailvelope.com/releases/latest/mailvelope.firefox.xpi" }, function (_url, _status) { if (_status == 0) { - et2_dialog.alert(egw.lang('Mailvelope addon installation succeded. Now you may configure the options.')); + et2_widget_dialog_1.et2_dialog.alert(egw.lang('Mailvelope addon installation succeded. Now you may configure the options.')); return; } else { - et2_dialog.alert(egw.lang('Mailvelope addon installation failed! Please try again.')); + et2_widget_dialog_1.et2_dialog.alert(egw.lang('Mailvelope addon installation failed! Please try again.')); } }); } @@ -1483,16 +1491,16 @@ var EgwApp = /** @class */ (function () { }); delete buttons[1].default; } - et2_dialog.show_dialog(function (_button_id) { - if (_button_id != et2_dialog.NO_BUTTON) { + et2_widget_dialog_1.et2_dialog.show_dialog(function (_button_id) { + if (_button_id != et2_widget_dialog_1.et2_dialog.NO_BUTTON) { var keys = {}; keys[self.egw.user('account_id')] = _pubKey; - self.egw.json('addressbook.addressbook_bo.ajax_set_pgp_keys', [keys, _button_id != et2_dialog.YES_BUTTON ? true : undefined]).sendRequest() + self.egw.json('addressbook.addressbook_bo.ajax_set_pgp_keys', [keys, _button_id != et2_widget_dialog_1.et2_dialog.YES_BUTTON ? true : undefined]).sendRequest() .then(function (_data) { self.egw.message(_data.response['0'].data); }); } - }, self.egw.lang('It is recommended to store your public key in addressbook, so other users can write you encrypted mails.'), self.egw.lang('Store your public key in Addressbook?'), {}, buttons, et2_dialog.QUESTION_MESSAGE, undefined, self.egw); + }, self.egw.lang('It is recommended to store your public key in addressbook, so other users can write you encrypted mails.'), self.egw.lang('Store your public key in Addressbook?'), {}, buttons, et2_widget_dialog_1.et2_dialog.QUESTION_MESSAGE, undefined, self.egw); }, function (_err) { self.egw.message(_err.message + "\n\n" + self.egw.lang("You will NOT be able to send or receive encrypted mails before completing that step!"), 'error'); @@ -1665,7 +1673,7 @@ var EgwApp = /** @class */ (function () { egw.message('Failed to copy the link!'); }; jQuery("body").on("click", "[name=share_link]", copy_link_to_clipboard); - et2_createWidget("dialog", { + et2_core_widget_1.et2_createWidget("dialog", { callback: function (button_id, value) { jQuery("body").off("click", "[name=share_link]", copy_link_to_clipboard); return true; diff --git a/api/js/jsapi/egw_app.ts b/api/js/jsapi/egw_app.ts index 9e5a87da8b..efc5c58d6b 100644 --- a/api/js/jsapi/egw_app.ts +++ b/api/js/jsapi/egw_app.ts @@ -15,6 +15,9 @@ import 'jqueryui'; import '../jsapi/egw_global'; import {etemplate2} from "../etemplate/etemplate2"; import {et2_container} from "../etemplate/et2_core_baseWidget"; +import {et2_nextmatch} from "../etemplate/et2_extension_nextmatch"; +import {et2_dialog} from "../etemplate/et2_widget_dialog"; +import {et2_createWidget} from "../etemplate/et2_core_widget"; /** * Type for push-message @@ -62,10 +65,15 @@ export interface PushData export abstract class EgwApp { /** - * Internal application name - override this + * Internal application name - pass this in constructor */ readonly appname: string; + /** + * Name of the modification timestamp in entry data + */ + readonly modification_field_name : string; + /** * Internal reference to the most recently loaded etemplate2 widget tree * @@ -91,7 +99,7 @@ export abstract class EgwApp * * @example Access via etemplate2 object * // Instead of this.et2, using it's unique ID - * var et2 = etemplate2.getById('myapp-index) + * var et2 = etemplate2.getById("myapp-index") * if(et2) * { * et2.widgetContainer. ... @@ -132,9 +140,10 @@ export abstract class EgwApp * Initialization and setup goes here, but the etemplate2 object * is not yet ready. */ - constructor(appname: string) + constructor(appname: string, modified_field:string = "") { this.appname = appname; + this.modification_field_name = modified_field; this.egw = egw(this.appname, window); // Initialize sidebox for non-popups. @@ -271,29 +280,34 @@ export abstract class EgwApp } /** - * Method called after apps push implementation checked visibility + * Callback from nextmatch so application can have some control over + * where new rows (added via push) are added. This is only called when + * the type is "add". * - * @param {et2_nextmatch} nm - * @param pushData see push method for individual attributes - * @todo implement better way to update nextmatch widget without disturbing the user / state - * @todo show indicator that an update has happend - * @todo rate-limit update frequency + * @param nm Nextmatch the entry is going to be added to + * @param uid + * @param current_order */ - updateList(nm, pushData : PushData) + nm_refresh_add(nm: et2_nextmatch, uid: string, current_order: string[]) : number|boolean { - switch (pushData.type) + // Do we have a modified field so we can check nm sort order? + if(this.modification_field_name) { - case 'add': - nm.refresh(this.uid(pushData), 'add'); - break; - case 'unknown': - nm.applyFilters(); - break; + let value = nm.getValue(); + let sort = value?.sort || {}; - default: - egw.dataRefreshUID(this.uid(pushData)); - break; + if(sort && sort.id == this.modification_field_name && sort.asc == false) + { + // Sorting by modification time, DESC. Put it at the top. + return 0; + } + + // Don't actually add it in. + return false; } + + // Just put it in at the top + return 0; } /** diff --git a/mail/js/app.js b/mail/js/app.js index a700c983ba..eb88260611 100644 --- a/mail/js/app.js +++ b/mail/js/app.js @@ -14,6 +14,8 @@ /api/js/jquery/jquery.base64.js; */ +import {etemplate2} from "../../api/js/etemplate/etemplate2"; + /** * UI for mail * @@ -406,7 +408,8 @@ app.classes.mail = AppJS.extend( let profile_id = pushData.id.split('::')[1]; if (nm_value && nm_value.col_filter && nm_value.selectedFolder.split("::")[0] == profile_id) { - this.updateList(nm, pushData); + // Just update the nm + nm.refresh(pushData.id, pushData.type); } // update unseen counter in folder-tree if (pushData.type === 'add' && pushData.acl.folder && pushData.acl.unseen) diff --git a/timesheet/js/app.js b/timesheet/js/app.js index 5ba7e00788..b4520bd2ff 100644 --- a/timesheet/js/app.js +++ b/timesheet/js/app.js @@ -30,6 +30,7 @@ require("jqueryui"); require("../jsapi/egw_global"); require("../etemplate/et2_types"); var egw_app_1 = require("../../api/js/jsapi/egw_app"); +var etemplate2_1 = require("../../api/js/etemplate/etemplate2"); /** * UI for timesheet * @@ -38,7 +39,7 @@ var egw_app_1 = require("../../api/js/jsapi/egw_app"); var TimesheetApp = /** @class */ (function (_super) { __extends(TimesheetApp, _super); function TimesheetApp() { - return _super.call(this, 'timesheet') || this; + return _super.call(this, 'timesheet', "ts_start") || this; } /** * This function is called when the etemplate2 object is loaded @@ -206,7 +207,7 @@ var TimesheetApp = /** @class */ (function (_super) { if (nm && nm_value && ((_c = nm_value.col_filter) === null || _c === void 0 ? void 0 : _c.ts_owner) && nm_value.col_filter.ts_owner != pushData.acl) { return; } - this.updateList(nm, pushData); + etemplate2_1.etemplate2.app_refresh("", pushData.app, pushData.id, pushData.type); }; return TimesheetApp; }(egw_app_1.EgwApp)); diff --git a/timesheet/js/app.ts b/timesheet/js/app.ts index c601f3b5c4..1ab90ccded 100644 --- a/timesheet/js/app.ts +++ b/timesheet/js/app.ts @@ -19,6 +19,7 @@ import '../etemplate/et2_types'; import {EgwApp} from '../../api/js/jsapi/egw_app'; import {et2_nextmatch} from "../../api/js/etemplate/et2_extension_nextmatch"; +import {etemplate2} from "../../api/js/etemplate/etemplate2"; /** * UI for timesheet @@ -30,7 +31,7 @@ class TimesheetApp extends EgwApp constructor() { - super('timesheet'); + super('timesheet',"ts_start"); } /** @@ -239,7 +240,7 @@ class TimesheetApp extends EgwApp { return; } - this.updateList(nm, pushData); + etemplate2.app_refresh("",pushData.app, pushData.id, pushData.type); } }