From 17018a7a2440a81cf232db6cec69c4bc2775f62b Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 15 Mar 2023 10:15:25 -0600 Subject: [PATCH] Home: Better initial load of new favorites Less server requests, shows data without reload, favorite select populated immediately --- api/js/etemplate/Et2Portlet/Et2Portlet.ts | 14 ++- home/inc/class.home_favorite_portlet.inc.php | 8 +- home/inc/class.home_ui.inc.php | 1 + home/js/Et2PortletFavorite.ts | 101 +++++++++++++++++-- home/js/app.ts | 32 +++--- 5 files changed, 120 insertions(+), 36 deletions(-) diff --git a/api/js/etemplate/Et2Portlet/Et2Portlet.ts b/api/js/etemplate/Et2Portlet/Et2Portlet.ts index e3a74ab5ba..84b681f403 100644 --- a/api/js/etemplate/Et2Portlet/Et2Portlet.ts +++ b/api/js/etemplate/Et2Portlet/Et2Portlet.ts @@ -418,7 +418,7 @@ export class Et2Portlet extends Et2Widget(SlCard) let content = this.portletProperties; // Add values, but skip any duplicate properties - Object.keys(this.settings).forEach(k => + Object.keys(this.settings || {}).forEach(k => { if(typeof k == "string" && isNaN(parseInt(k)) || content.filter(v => v.name == this.settings[k].name).length == 0) { @@ -488,17 +488,17 @@ export class Et2Portlet extends Et2Widget(SlCard) public update_settings(settings) { // Skip any updates during loading - if(!this.getInstanceManager().isReady) + if(this.getInstanceManager() && !this.getInstanceManager().isReady) { - return; + return Promise.resolve(); } // Save settings - server might reply with new content if the portlet needs an update, // but ideally it doesn't this.classList.add("loading"); - return this.egw().jsonq("home.home_ui.ajax_set_properties", [this.id, [], settings, this.settings ? this.settings.group : false], - function(data) + return this.egw().request("home.home_ui.ajax_set_properties", [this.id, [], settings, this.settings ? this.settings.group : false]) + .then((data) => { // This section not for us if(!data || typeof data.attributes == 'undefined') @@ -536,9 +536,7 @@ export class Et2Portlet extends Et2Widget(SlCard) this.egw().debug('warn', e, this); } } - }, - this); - + }); } render() diff --git a/home/inc/class.home_favorite_portlet.inc.php b/home/inc/class.home_favorite_portlet.inc.php index 14eb15125f..28430ef450 100644 --- a/home/inc/class.home_favorite_portlet.inc.php +++ b/home/inc/class.home_favorite_portlet.inc.php @@ -85,20 +85,18 @@ class home_favorite_portlet extends home_portlet } // Load and copy favorite - if($context['favorite'] && !is_array($context['favorite'])) + if($context['favorite']) { $favorites = Framework\Favorites::get_favorites($context['appname']); - $context['favorite'] = $favorites[$context['favorite']]; - $need_reload = true; } - $this->favorite = (array)$context['favorite']; + $this->favorite = (array)$favorites[$context['favorite'] ?: 'blank']; $this->title = lang($context['appname']) . ': ' . $this->favorite['name']; $this->context = $context; if($this->favorite) { $this->nm_settings['favorite'] = $this->context['favorite']; - if(is_array($this->favorite['state'])) + if(is_array($favorites[$context['favorite']]['state'])) { $this->nm_settings += $this->favorite['state']; } diff --git a/home/inc/class.home_ui.inc.php b/home/inc/class.home_ui.inc.php index cfa6f68502..2fbf8bd526 100644 --- a/home/inc/class.home_ui.inc.php +++ b/home/inc/class.home_ui.inc.php @@ -427,6 +427,7 @@ class home_ui 'id' => $portlet, 'caption' => $desc['displayName'], 'hint' => $desc['description'], + 'appname' => $appname, 'onExecute' => 'javaScript:app.home.add', 'acceptedTypes' => $instance->accept_drop(), 'allowOnMultiple' => $instance->accept_multiple() diff --git a/home/js/Et2PortletFavorite.ts b/home/js/Et2PortletFavorite.ts index 5851729d73..a01a52a79a 100644 --- a/home/js/Et2PortletFavorite.ts +++ b/home/js/Et2PortletFavorite.ts @@ -4,6 +4,7 @@ import shoelace from "../../api/js/etemplate/Styles/shoelace"; import {etemplate2} from "../../api/js/etemplate/etemplate2"; import type {SelectOption} from "../../api/js/etemplate/Et2Select/FindSelectOptions"; import {Et2Favorites} from "../../api/js/etemplate/Et2Favorites/Et2Favorites"; +import {Et2Dialog} from "../../api/js/etemplate/Et2Dialog/Et2Dialog"; export class Et2PortletFavorite extends Et2Portlet { @@ -41,26 +42,99 @@ export class Et2PortletFavorite extends Et2Portlet * @returns {[{name : string, type : string, select_options? : [SelectOption]}]} */ get portletProperties() : { name : string, type : string, label : string, select_options? : SelectOption[] }[] + { + return [ + ...super.portletProperties, + {name: "favorite", type: "et2-select", label: "Favorite", select_options: this.favorites} + ] + } + + public get favorites() { // Default blank filter let favorites = [ - {value: 'blank', label: this.egw().lang("No filters")} + {value: 'blank', label: this.egw().lang("No filters"), favorite: {}} ]; // Load favorites - let preferences : any = this.egw().preference("*", this.settings.appname); - for(let pref_name in preferences) + if(this.settings?.appname) { - if(pref_name.indexOf(Et2Favorites.PREFIX) == 0 && typeof preferences[pref_name] == 'object') + let preferences : any = this.egw().preference("*", this.settings.appname); + for(let pref_name in preferences) { - let name = pref_name.substr(Et2Favorites.PREFIX.length); - favorites.push({value: name, label: preferences[pref_name]['name']}); + if(pref_name.indexOf(Et2Favorites.PREFIX) == 0 && typeof preferences[pref_name] == 'object') + { + let name = pref_name.substr(Et2Favorites.PREFIX.length); + favorites.push({ + value: name, + label: preferences[pref_name]['name'], + favorite: preferences[pref_name] + }); + } } } - return [ - ...super.portletProperties, - {name: "favorite", type: "et2-select", label: "Favorite", select_options: favorites} - ] + return favorites; + } + + /** + * Overridden so we can just apply the favorite to the nm + * + * @param button_id + * @param value + */ + _process_edit(button_id, value) + { + if(button_id == Et2Dialog.OK_BUTTON && value.favorite != this.settings.favorite) + { + const state = this.favorites.find(f => f.value == value.favorite)?.favorite || {}; + if(this.nm && typeof state == "object") + { + // Firefox has trouble with spaces in search + if(state.state && state.state.search) + { + state.state.search = unescape(state.state.search); + } + + // Apply + if(state.state && state.state.sort && state.state.sort.id) + { + this.nm.sortBy(state.state.sort.id, state.state.sort.asc, false); + } + else + { + // Not using resetSort() to avoid the extra applyFilters() call + this.nm.sortBy(undefined, undefined, false); + } + if(state.state && state.state.selectcols) + { + // Make sure it's a real array, not an object, then set cols + this.nm.set_columns(jQuery.extend([], state.state.selectcols)); + } + this.nm.applyFilters(state.state || state.filter || {}); + } + } + super._process_edit(button_id, value); + } + + + /** + * Override parent to force resize on initial load + * @param settings + * @returns {Promise | Promise} + */ + update_settings(settings) : Promise | Promise + { + return super.update_settings(settings) + .then(result => + { + // Response did not ask for settings, and was not understandable for us + if(result == false && !this.nm) + { + // If child was added recently (not loaded in normal reload), resize them all + etemplate2.getByTemplate("home.favorite").forEach(et => (et).resize(undefined)) + } + return result; + }); } headerTemplate() @@ -78,11 +152,16 @@ export class Et2PortletFavorite extends Et2Portlet `; } + protected get nm() + { + return this.getWidgetById('nm') || etemplate2.getById(this.id) && etemplate2.getById(this.id).widgetContainer.getWidgetById('nm') || false; + } + public toggleHeader() { //widget.set_class(widget.class == 'opened' ? 'closed' : 'opened'); // We operate on the DOM here, nm should be unaware of our fiddling - let nm = this.getWidgetById('nm') || etemplate2.getById(this.id) && etemplate2.getById(this.id).widgetContainer.getWidgetById('nm') || false; + let nm = this.nm if(!nm) { return; diff --git a/home/js/app.ts b/home/js/app.ts index 4403bbb65d..b277f5a823 100644 --- a/home/js/app.ts +++ b/home/js/app.ts @@ -178,9 +178,6 @@ export class HomeApp extends EgwApp et2.DOMContainer.id = et2.uniqueId; } - // Instanciate custom code for this portlet - this._get_portlet_code(portlet); - // Ordering of portlets // Only needs to be done once, but its hard to tell when everything is loaded this._do_ordering(); @@ -241,9 +238,18 @@ export class HomeApp extends EgwApp { // Basic portlet attributes let attrs = { - id: this._create_id(), - class: action.data.class + id: this._create_id(), + class: action.data.class, + settings: {} }; + // Add extra data from action + Object.keys(action.data).forEach(k => + { + if(["id", "type", "acceptedTypes", "class"].indexOf(k) == -1) + { + attrs["settings"][k] = action.data[k]; + } + }) // Try to put it about where the menu was opened if(action.menu_context) @@ -258,10 +264,15 @@ export class HomeApp extends EgwApp portlet.loadingFinished(); // Get actual attributes & settings, since they're not available client side yet - portlet.update_settings(attrs); - - // Instanciate custom code for this portlet - this._get_portlet_code(portlet); + portlet.update_settings(attrs).then((result) => + { + // Initial add needs to wait for the update to come back, then ask about settings + // Etemplate can conflict with portlet asking for settings + if(result === false) + { + portlet.edit_settings(); + } + }); } /** @@ -311,9 +322,6 @@ export class HomeApp extends EgwApp // Get actual attributes & settings, since they're not available client side yet portlet.update_settings(attrs); - - // Instanciate custom code for this portlet - this._get_portlet_code(portlet); } /**