@xh/hoist 71.0.0-SNAPSHOT.1736263796812 → 71.0.0-SNAPSHOT.1736264155874
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/types/cmp/viewmanager/DataAccess.d.ts +1 -1
- package/build/types/cmp/viewmanager/ViewManagerModel.d.ts +1 -1
- package/build/types/desktop/cmp/viewmanager/ViewManagerLocalModel.d.ts +0 -2
- package/cmp/viewmanager/DataAccess.ts +4 -4
- package/cmp/viewmanager/ViewInfo.ts +0 -3
- package/cmp/viewmanager/ViewManagerModel.ts +23 -24
- package/desktop/cmp/viewmanager/ViewManager.ts +5 -3
- package/desktop/cmp/viewmanager/ViewManagerLocalModel.ts +0 -18
- package/desktop/cmp/viewmanager/ViewMenu.ts +4 -4
- package/desktop/cmp/viewmanager/dialog/ManageDialogModel.ts +1 -1
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -15,7 +15,7 @@ export declare class DataAccess<T> {
|
|
|
15
15
|
state: ViewUserState;
|
|
16
16
|
}>;
|
|
17
17
|
/** Fetch the latest version of a view. */
|
|
18
|
-
fetchViewAsync(
|
|
18
|
+
fetchViewAsync(info: ViewInfo): Promise<View<T>>;
|
|
19
19
|
/** Create a new view, owned by the current user.*/
|
|
20
20
|
createViewAsync(spec: ViewCreateSpec): Promise<View<T>>;
|
|
21
21
|
/** Update all aspects of a view's metadata.*/
|
|
@@ -183,7 +183,7 @@ export declare class ViewManagerModel<T = PlainObject> extends HoistModel {
|
|
|
183
183
|
*/
|
|
184
184
|
private constructor();
|
|
185
185
|
doLoadAsync(loadSpec: LoadSpec): Promise<void>;
|
|
186
|
-
selectViewAsync(
|
|
186
|
+
selectViewAsync(info: ViewInfo, opts?: {
|
|
187
187
|
alertUnsavedChanges: boolean;
|
|
188
188
|
}): Promise<void>;
|
|
189
189
|
saveAsAsync(spec: ViewCreateSpec): Promise<void>;
|
|
@@ -7,7 +7,5 @@ export declare class ViewManagerLocalModel extends HoistModel {
|
|
|
7
7
|
readonly manageDialogModel: ManageDialogModel;
|
|
8
8
|
readonly saveAsDialogModel: SaveAsDialogModel;
|
|
9
9
|
isVisible: boolean;
|
|
10
|
-
saveAsync(): Promise<void>;
|
|
11
|
-
revertAsync(): Promise<void>;
|
|
12
10
|
constructor(parent: ViewManagerModel);
|
|
13
11
|
}
|
|
@@ -48,14 +48,14 @@ export class DataAccess<T> {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/** Fetch the latest version of a view. */
|
|
51
|
-
async fetchViewAsync(
|
|
51
|
+
async fetchViewAsync(info: ViewInfo): Promise<View<T>> {
|
|
52
52
|
const {model} = this;
|
|
53
|
-
if (!
|
|
53
|
+
if (!info) return View.createDefault(model);
|
|
54
54
|
try {
|
|
55
|
-
const raw = await XH.fetchJson({url: 'xhView/get', params: {token}});
|
|
55
|
+
const raw = await XH.fetchJson({url: 'xhView/get', params: {token: info.token}});
|
|
56
56
|
return View.fromBlob(raw, model);
|
|
57
57
|
} catch (e) {
|
|
58
|
-
throw XH.exception({message: `Unable to fetch
|
|
58
|
+
throw XH.exception({message: `Unable to fetch ${info.typedName}`, cause: e});
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
import {SECONDS} from '@xh/hoist/utils/datetime';
|
|
8
|
-
import {throwIf} from '@xh/hoist/utils/js';
|
|
9
8
|
import {ViewManagerModel} from './ViewManagerModel';
|
|
10
9
|
import {JsonBlob} from '@xh/hoist/svc';
|
|
11
10
|
import {PlainObject, XH} from '@xh/hoist/core';
|
|
@@ -58,8 +57,6 @@ export class ViewInfo {
|
|
|
58
57
|
readonly model: ViewManagerModel;
|
|
59
58
|
|
|
60
59
|
constructor(blob: JsonBlob, model: ViewManagerModel) {
|
|
61
|
-
throwIf(blob.type !== model.type, 'View type does not match ViewManager type.');
|
|
62
|
-
|
|
63
60
|
this.token = blob.token;
|
|
64
61
|
this.type = blob.type;
|
|
65
62
|
this.name = blob.name;
|
|
@@ -21,7 +21,7 @@ import {fmtDateTime} from '@xh/hoist/format';
|
|
|
21
21
|
import {action, bindable, makeObservable, observable, comparer, runInAction} from '@xh/hoist/mobx';
|
|
22
22
|
import {olderThan, SECONDS} from '@xh/hoist/utils/datetime';
|
|
23
23
|
import {executeIfFunction, pluralize, throwIf} from '@xh/hoist/utils/js';
|
|
24
|
-
import {find, isEqual, isNil, isNull,
|
|
24
|
+
import {find, isEqual, isNil, isNull, isUndefined, lowerCase, uniqBy} from 'lodash';
|
|
25
25
|
import {ReactNode} from 'react';
|
|
26
26
|
import {ViewInfo} from './ViewInfo';
|
|
27
27
|
import {View} from './View';
|
|
@@ -337,7 +337,7 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
|
|
|
337
337
|
if (!view.isDefault) {
|
|
338
338
|
const latestInfo = find(views, {token: view.token});
|
|
339
339
|
if (latestInfo && latestInfo.lastUpdated > view.lastUpdated) {
|
|
340
|
-
this.loadViewAsync(
|
|
340
|
+
this.loadViewAsync(latestInfo, this.pendingValue);
|
|
341
341
|
}
|
|
342
342
|
}
|
|
343
343
|
} catch (e) {
|
|
@@ -346,12 +346,7 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
|
|
|
346
346
|
}
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
-
async selectViewAsync(
|
|
350
|
-
view: string | ViewInfo,
|
|
351
|
-
opts = {alertUnsavedChanges: true}
|
|
352
|
-
): Promise<void> {
|
|
353
|
-
const token = isObject(view) ? view.token : view;
|
|
354
|
-
|
|
349
|
+
async selectViewAsync(info: ViewInfo, opts = {alertUnsavedChanges: true}): Promise<void> {
|
|
355
350
|
// ensure any pending auto-save gets completed
|
|
356
351
|
if (this.isValueDirty && this.isViewAutoSavable) {
|
|
357
352
|
await this.maybeAutoSaveAsync();
|
|
@@ -367,7 +362,7 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
|
|
|
367
362
|
return;
|
|
368
363
|
}
|
|
369
364
|
|
|
370
|
-
|
|
365
|
+
await this.loadViewAsync(info).catch(e => this.handleException(e));
|
|
371
366
|
}
|
|
372
367
|
|
|
373
368
|
async saveAsAsync(spec: ViewCreateSpec): Promise<void> {
|
|
@@ -385,22 +380,26 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
|
|
|
385
380
|
return;
|
|
386
381
|
}
|
|
387
382
|
const {pendingValue, view, dataAccess} = this;
|
|
383
|
+
try {
|
|
384
|
+
if (!(await this.maybeConfirmSaveAsync(view, pendingValue))) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
const updated = await dataAccess
|
|
388
|
+
.updateViewValueAsync(view, pendingValue.value)
|
|
389
|
+
.linkTo(this.saveTask);
|
|
388
390
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
+
this.setAsView(updated);
|
|
392
|
+
this.noteSuccess(`Saved ${view.typedName}`);
|
|
393
|
+
} catch (e) {
|
|
394
|
+
this.handleException(e, {
|
|
395
|
+
message: `Failed to save ${view.typedName}. If this persists consider \`Save As...\`.`
|
|
396
|
+
});
|
|
391
397
|
}
|
|
392
|
-
const updated = await dataAccess
|
|
393
|
-
.updateViewValueAsync(view, pendingValue.value)
|
|
394
|
-
.linkTo(this.saveTask);
|
|
395
|
-
|
|
396
|
-
this.setAsView(updated);
|
|
397
|
-
this.noteSuccess(`Saved ${view.typedName}`);
|
|
398
|
-
|
|
399
398
|
this.refreshAsync();
|
|
400
399
|
}
|
|
401
400
|
|
|
402
401
|
async resetAsync(): Promise<void> {
|
|
403
|
-
|
|
402
|
+
await this.loadViewAsync(this.view.info).catch(e => this.handleException(e));
|
|
404
403
|
}
|
|
405
404
|
|
|
406
405
|
//--------------------------------
|
|
@@ -484,7 +483,7 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
|
|
|
484
483
|
const {views} = this;
|
|
485
484
|
|
|
486
485
|
if (toDelete.some(view => view.isCurrentView) && !views.some(view => view.isCurrentView)) {
|
|
487
|
-
await this.loadViewAsync(this.initialViewSpec?.(views)
|
|
486
|
+
await this.loadViewAsync(this.initialViewSpec?.(views));
|
|
488
487
|
}
|
|
489
488
|
|
|
490
489
|
if (exception) throw exception;
|
|
@@ -570,13 +569,13 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
|
|
|
570
569
|
}
|
|
571
570
|
|
|
572
571
|
private async loadViewAsync(
|
|
573
|
-
|
|
572
|
+
info: ViewInfo,
|
|
574
573
|
pendingValue: PendingValue<T> = null
|
|
575
574
|
): Promise<void> {
|
|
576
575
|
return this.dataAccess
|
|
577
|
-
.fetchViewAsync(
|
|
576
|
+
.fetchViewAsync(info)
|
|
578
577
|
.thenAction(latest => {
|
|
579
|
-
this.setAsView(latest, pendingValue?.token == token ? pendingValue : null);
|
|
578
|
+
this.setAsView(latest, pendingValue?.token == info?.token ? pendingValue : null);
|
|
580
579
|
this.providers.forEach(it => it.pushStateToTarget());
|
|
581
580
|
this.lastPushed = Date.now();
|
|
582
581
|
})
|
|
@@ -650,7 +649,7 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
|
|
|
650
649
|
|
|
651
650
|
private async maybeConfirmSaveAsync(view: View, pendingValue: PendingValue<T>) {
|
|
652
651
|
// Get latest from server for reference
|
|
653
|
-
const latest = await this.dataAccess.fetchViewAsync(view.
|
|
652
|
+
const latest = await this.dataAccess.fetchViewAsync(view.info),
|
|
654
653
|
isGlobal = latest.isGlobal,
|
|
655
654
|
isStale = latest.lastUpdated > pendingValue.baseUpdated;
|
|
656
655
|
if (!isStale && !isGlobal) return true;
|
|
@@ -118,7 +118,7 @@ const menuButton = hoistCmp.factory<ViewManagerLocalModel>({
|
|
|
118
118
|
const saveButton = hoistCmp.factory<ViewManagerLocalModel>({
|
|
119
119
|
render({model, mode, ...rest}) {
|
|
120
120
|
if (hideStateButton(model, mode)) return null;
|
|
121
|
-
const {parent} = model,
|
|
121
|
+
const {parent, saveAsDialogModel} = model,
|
|
122
122
|
{typeDisplayName, isLoading, isValueDirty} = parent;
|
|
123
123
|
return button({
|
|
124
124
|
className: 'xh-view-manager__save-button',
|
|
@@ -126,7 +126,9 @@ const saveButton = hoistCmp.factory<ViewManagerLocalModel>({
|
|
|
126
126
|
tooltip: `Save changes to this ${typeDisplayName}`,
|
|
127
127
|
intent: 'primary',
|
|
128
128
|
disabled: !isValueDirty || isLoading,
|
|
129
|
-
onClick: () =>
|
|
129
|
+
onClick: () => {
|
|
130
|
+
parent.isViewSavable ? parent.saveAsync() : saveAsDialogModel.open();
|
|
131
|
+
},
|
|
130
132
|
...rest
|
|
131
133
|
});
|
|
132
134
|
}
|
|
@@ -142,7 +144,7 @@ const revertButton = hoistCmp.factory<ViewManagerLocalModel>({
|
|
|
142
144
|
tooltip: `Revert changes to this ${typeDisplayName}`,
|
|
143
145
|
intent: 'danger',
|
|
144
146
|
disabled: !isValueDirty || isLoading,
|
|
145
|
-
onClick: () => model.
|
|
147
|
+
onClick: () => model.parent.resetAsync(),
|
|
146
148
|
...rest
|
|
147
149
|
});
|
|
148
150
|
}
|
|
@@ -23,24 +23,6 @@ export class ViewManagerLocalModel extends HoistModel {
|
|
|
23
23
|
@bindable
|
|
24
24
|
isVisible = true;
|
|
25
25
|
|
|
26
|
-
async saveAsync() {
|
|
27
|
-
const {parent} = this,
|
|
28
|
-
{view} = parent;
|
|
29
|
-
|
|
30
|
-
if (!parent.isViewSavable) {
|
|
31
|
-
this.saveAsDialogModel.open();
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return parent.saveAsync().catchDefault({
|
|
36
|
-
message: `Failed to save ${view.typedName}. If this persists consider \`Save As...\`.`
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async revertAsync() {
|
|
41
|
-
return this.parent.resetAsync().catchDefault();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
26
|
constructor(parent: ViewManagerModel) {
|
|
45
27
|
super();
|
|
46
28
|
makeObservable(this);
|
|
@@ -63,7 +63,7 @@ function getNavMenuItems(model: ViewManagerModel): ReactNode[] {
|
|
|
63
63
|
className: 'xh-view-manager__menu-item',
|
|
64
64
|
icon: view.isDefault ? Icon.check() : Icon.placeholder(),
|
|
65
65
|
text: `Default ${startCase(typeDisplayName)}`,
|
|
66
|
-
onClick: () => model.selectViewAsync(null)
|
|
66
|
+
onClick: () => model.selectViewAsync(null)
|
|
67
67
|
})
|
|
68
68
|
);
|
|
69
69
|
}
|
|
@@ -89,7 +89,7 @@ function getOtherMenuItems(model: ViewManagerLocalModel): ReactNode[] {
|
|
|
89
89
|
icon: Icon.save(),
|
|
90
90
|
text: 'Save',
|
|
91
91
|
disabled: !isViewSavable || !isValueDirty,
|
|
92
|
-
onClick: () =>
|
|
92
|
+
onClick: () => parent.saveAsync()
|
|
93
93
|
}),
|
|
94
94
|
menuItem({
|
|
95
95
|
icon: Icon.placeholder(),
|
|
@@ -100,7 +100,7 @@ function getOtherMenuItems(model: ViewManagerLocalModel): ReactNode[] {
|
|
|
100
100
|
icon: Icon.reset(),
|
|
101
101
|
text: `Revert`,
|
|
102
102
|
disabled: !isValueDirty,
|
|
103
|
-
onClick: () =>
|
|
103
|
+
onClick: () => parent.resetAsync()
|
|
104
104
|
}),
|
|
105
105
|
menuDivider({omit: !enableAutoSave}),
|
|
106
106
|
menuItem({
|
|
@@ -168,6 +168,6 @@ function viewMenuItem(view: ViewInfo, model: ViewManagerModel): ReactNode {
|
|
|
168
168
|
text: view.name,
|
|
169
169
|
title: title.join(' | '),
|
|
170
170
|
icon,
|
|
171
|
-
onClick: () => model.selectViewAsync(view)
|
|
171
|
+
onClick: () => model.selectViewAsync(view)
|
|
172
172
|
});
|
|
173
173
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xh/hoist",
|
|
3
|
-
"version": "71.0.0-SNAPSHOT.
|
|
3
|
+
"version": "71.0.0-SNAPSHOT.1736264155874",
|
|
4
4
|
"description": "Hoist add-on for building and deploying React Applications.",
|
|
5
5
|
"repository": "github:xh/hoist-react",
|
|
6
6
|
"homepage": "https://xh.io",
|