@@ -28,8 +28,8 @@ import {Et2VfsUpload} from "../Et2Vfs/Et2VfsUpload";
2828import {
2929 applyLegacyNextmatchColumnPreferences ,
3030 datagridColumnPreferenceValue ,
31- legacyColumnSelectionCsv ,
32- type Et2NextmatchResolvedColumn
31+ type Et2NextmatchResolvedColumn ,
32+ legacyColumnSelectionCsv
3333} from "./Et2NextmatchColumnPreferences" ;
3434import "./Headers/Header" ;
3535import "./Headers/SortableHeader" ;
@@ -227,6 +227,16 @@ export class Et2Nextmatch extends Et2Widget(LitElement) implements et2_IInput
227227 @property ( { type : String , attribute : "column-preference-name" } )
228228 columnPreferenceName : string = "" ;
229229
230+ /**
231+ * App that owns this nextmatch's rows - used for sort/refresh/lettersearch preference
232+ * persistence, row-stylesheet loading and legacy action-manager registration (see
233+ * `_getAppName()`). Set this explicitly when a nextmatch is embedded in another app's
234+ * page (eg. InfoLog's CRM view inside addressbook) and the owning app can't be inferred
235+ * from `template`. Leave unset to fall back to `_getAppName()`'s own resolution.
236+ */
237+ @property ( { type : String , attribute : "app" } )
238+ appName : string = "" ;
239+
230240 private _view : Et2DatagridView = "row" ;
231241
232242 /**
@@ -970,6 +980,28 @@ export class Et2Nextmatch extends Et2Widget(LitElement) implements et2_IInput
970980 return this . _actionController . findActionTarget ( event ) ;
971981 }
972982
983+ /**
984+ * Resolves once `firstUpdated()` has applied the template and seeded sort settings -
985+ * deliberately NOT once initial rows have loaded.
986+ *
987+ * Row loading (`_datagrid.reload()`) and the row stylesheet fetch happen after this
988+ * resolves and are intentionally excluded: they're a network round-trip, and nothing
989+ * that reacts to overall readiness (focus management, `resize()`, the "load" event)
990+ * should be held up waiting for rows to arrive.
991+ */
992+ private _resolveFirstUpdatedComplete : ( ) => void = ( ) => { } ;
993+ private _firstUpdatedComplete : Promise < void > = new Promise ( ( resolve ) =>
994+ {
995+ this . _resolveFirstUpdatedComplete = resolve ;
996+ } ) ;
997+
998+ async getUpdateComplete ( ) : Promise < boolean >
999+ {
1000+ const result = await super . getUpdateComplete ( ) ;
1001+ await this . _firstUpdatedComplete ;
1002+ return result ;
1003+ }
1004+
9731005 /**
9741006 * Initialize the widget from attributes/template and trigger first load.
9751007 * We prefer showing provided rows immediately to keep first paint fast.
@@ -981,6 +1013,21 @@ export class Et2Nextmatch extends Et2Widget(LitElement) implements et2_IInput
9811013 this . _syncPlaceholderActionAvailability ( ) ;
9821014 this . _initializeExtraAttributeFilters ( ) ;
9831015
1016+ try
1017+ {
1018+ // Seed sort from already-known `settings` (populated from attrs/content well
1019+ // before firstUpdated() runs) before awaiting anything below. This needs no
1020+ // template/row data, so there's no reason to delay it
1021+ this . _initializeSettingsSort ( ) ;
1022+ }
1023+ finally
1024+ {
1025+ // Resolve as soon as the minimum is ready, not waiting for template/row loading below.
1026+ this . _resolveFirstUpdatedComplete ( ) ;
1027+ }
1028+
1029+ // Everything from here we don't wait for, it will finish on its own. If needed, you can wait for
1030+ // whenColumnsReady() or listen for the appropriate event.
9841031 if ( this . template )
9851032 {
9861033 await this . _applyTemplateFromName ( this . template ) ;
@@ -989,7 +1036,8 @@ export class Et2Nextmatch extends Et2Widget(LitElement) implements et2_IInput
9891036 {
9901037 await this . _applyTemplateFromSlots ( ) ;
9911038 }
992- this . _initializeSettingsSort ( ) ;
1039+ // Sync any sort headers that just rendered with the sort state seeded above.
1040+ this . _updateSortHeaderState ( ) ;
9931041
9941042 if ( this . rows . length )
9951043 {
@@ -2684,9 +2732,30 @@ export class Et2Nextmatch extends Et2Widget(LitElement) implements et2_IInput
26842732
26852733 /**
26862734 * Resolve app-name used for sort preference persistence.
2735+ *
2736+ * Preference order: the explicit `appName` property, then the app that owns this
2737+ * nextmatch's rows (the first segment of `template`, eg. "infolog" from
2738+ * "infolog.index.rows"), then the instance manager's `app` as a last resort.
2739+ *
2740+ * Server-side, the preference is always read back under the app resolved from
2741+ * `get_rows` (Nextmatch.php: `explode('.', $value['get_rows'])[0]`), which is the
2742+ * same owning app - not necessarily the surrounding page/tab's app. Views that embed
2743+ * one app's nextmatch inside another (eg. InfoLog's CRM view inside addressbook, which
2744+ * forces currentapp to "addressbook") would otherwise save the sort preference under
2745+ * the wrong namespace and never see it applied again. Set `appName` explicitly for
2746+ * cases where `template` doesn't carry the owning app as its first segment.
26872747 */
26882748 _getAppName ( ) : string
26892749 {
2750+ if ( this . appName )
2751+ {
2752+ return this . appName ;
2753+ }
2754+ const template = this . template ;
2755+ if ( typeof template === "string" && template . includes ( "." ) )
2756+ {
2757+ return template . split ( "." ) [ 0 ] ;
2758+ }
26902759 return String ( this . getInstanceManager ?.( ) ?. app || this . egw ( ) ?. app_name ?.( ) || "" ) ;
26912760 }
26922761
0 commit comments