@xh/hoist 71.0.0-SNAPSHOT.1736200636969 → 71.0.0-SNAPSHOT.1736263796812
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 +2 -0
- package/cmp/viewmanager/DataAccess.ts +4 -4
- package/cmp/viewmanager/ViewInfo.ts +3 -0
- package/cmp/viewmanager/ViewManagerModel.ts +24 -23
- package/desktop/cmp/viewmanager/ViewManager.ts +3 -5
- package/desktop/cmp/viewmanager/ViewManagerLocalModel.ts +18 -0
- 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(token: string): 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(view: string | ViewInfo, opts?: {
|
|
187
187
|
alertUnsavedChanges: boolean;
|
|
188
188
|
}): Promise<void>;
|
|
189
189
|
saveAsAsync(spec: ViewCreateSpec): Promise<void>;
|
|
@@ -7,5 +7,7 @@ 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>;
|
|
10
12
|
constructor(parent: ViewManagerModel);
|
|
11
13
|
}
|
|
@@ -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(token: string): Promise<View<T>> {
|
|
52
52
|
const {model} = this;
|
|
53
|
-
if (!
|
|
53
|
+
if (!token) 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}});
|
|
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 view with token ${token}`, cause: e});
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -5,6 +5,7 @@
|
|
|
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';
|
|
8
9
|
import {ViewManagerModel} from './ViewManagerModel';
|
|
9
10
|
import {JsonBlob} from '@xh/hoist/svc';
|
|
10
11
|
import {PlainObject, XH} from '@xh/hoist/core';
|
|
@@ -57,6 +58,8 @@ export class ViewInfo {
|
|
|
57
58
|
readonly model: ViewManagerModel;
|
|
58
59
|
|
|
59
60
|
constructor(blob: JsonBlob, model: ViewManagerModel) {
|
|
61
|
+
throwIf(blob.type !== model.type, 'View type does not match ViewManager type.');
|
|
62
|
+
|
|
60
63
|
this.token = blob.token;
|
|
61
64
|
this.type = blob.type;
|
|
62
65
|
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, isUndefined, lowerCase, uniqBy} from 'lodash';
|
|
24
|
+
import {find, isEqual, isNil, isNull, isObject, 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(view.token, this.pendingValue);
|
|
341
341
|
}
|
|
342
342
|
}
|
|
343
343
|
} catch (e) {
|
|
@@ -346,7 +346,12 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
|
|
|
346
346
|
}
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
-
async selectViewAsync(
|
|
349
|
+
async selectViewAsync(
|
|
350
|
+
view: string | ViewInfo,
|
|
351
|
+
opts = {alertUnsavedChanges: true}
|
|
352
|
+
): Promise<void> {
|
|
353
|
+
const token = isObject(view) ? view.token : view;
|
|
354
|
+
|
|
350
355
|
// ensure any pending auto-save gets completed
|
|
351
356
|
if (this.isValueDirty && this.isViewAutoSavable) {
|
|
352
357
|
await this.maybeAutoSaveAsync();
|
|
@@ -362,7 +367,7 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
|
|
|
362
367
|
return;
|
|
363
368
|
}
|
|
364
369
|
|
|
365
|
-
|
|
370
|
+
return this.loadViewAsync(token);
|
|
366
371
|
}
|
|
367
372
|
|
|
368
373
|
async saveAsAsync(spec: ViewCreateSpec): Promise<void> {
|
|
@@ -380,26 +385,22 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
|
|
|
380
385
|
return;
|
|
381
386
|
}
|
|
382
387
|
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);
|
|
390
388
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
} catch (e) {
|
|
394
|
-
this.handleException(e, {
|
|
395
|
-
message: `Failed to save ${view.typedName}. If this persists consider \`Save As...\`.`
|
|
396
|
-
});
|
|
389
|
+
if (!(await this.maybeConfirmSaveAsync(view, pendingValue))) {
|
|
390
|
+
return;
|
|
397
391
|
}
|
|
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
|
+
|
|
398
399
|
this.refreshAsync();
|
|
399
400
|
}
|
|
400
401
|
|
|
401
402
|
async resetAsync(): Promise<void> {
|
|
402
|
-
|
|
403
|
+
return this.loadViewAsync(this.view.token);
|
|
403
404
|
}
|
|
404
405
|
|
|
405
406
|
//--------------------------------
|
|
@@ -483,7 +484,7 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
|
|
|
483
484
|
const {views} = this;
|
|
484
485
|
|
|
485
486
|
if (toDelete.some(view => view.isCurrentView) && !views.some(view => view.isCurrentView)) {
|
|
486
|
-
await this.loadViewAsync(this.initialViewSpec?.(views));
|
|
487
|
+
await this.loadViewAsync(this.initialViewSpec?.(views)?.token);
|
|
487
488
|
}
|
|
488
489
|
|
|
489
490
|
if (exception) throw exception;
|
|
@@ -569,13 +570,13 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
|
|
|
569
570
|
}
|
|
570
571
|
|
|
571
572
|
private async loadViewAsync(
|
|
572
|
-
|
|
573
|
+
token: string,
|
|
573
574
|
pendingValue: PendingValue<T> = null
|
|
574
575
|
): Promise<void> {
|
|
575
576
|
return this.dataAccess
|
|
576
|
-
.fetchViewAsync(
|
|
577
|
+
.fetchViewAsync(token)
|
|
577
578
|
.thenAction(latest => {
|
|
578
|
-
this.setAsView(latest, pendingValue?.token ==
|
|
579
|
+
this.setAsView(latest, pendingValue?.token == token ? pendingValue : null);
|
|
579
580
|
this.providers.forEach(it => it.pushStateToTarget());
|
|
580
581
|
this.lastPushed = Date.now();
|
|
581
582
|
})
|
|
@@ -649,7 +650,7 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
|
|
|
649
650
|
|
|
650
651
|
private async maybeConfirmSaveAsync(view: View, pendingValue: PendingValue<T>) {
|
|
651
652
|
// Get latest from server for reference
|
|
652
|
-
const latest = await this.dataAccess.fetchViewAsync(view.
|
|
653
|
+
const latest = await this.dataAccess.fetchViewAsync(view.token),
|
|
653
654
|
isGlobal = latest.isGlobal,
|
|
654
655
|
isStale = latest.lastUpdated > pendingValue.baseUpdated;
|
|
655
656
|
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
|
|
121
|
+
const {parent} = model,
|
|
122
122
|
{typeDisplayName, isLoading, isValueDirty} = parent;
|
|
123
123
|
return button({
|
|
124
124
|
className: 'xh-view-manager__save-button',
|
|
@@ -126,9 +126,7 @@ const saveButton = hoistCmp.factory<ViewManagerLocalModel>({
|
|
|
126
126
|
tooltip: `Save changes to this ${typeDisplayName}`,
|
|
127
127
|
intent: 'primary',
|
|
128
128
|
disabled: !isValueDirty || isLoading,
|
|
129
|
-
onClick: () =>
|
|
130
|
-
parent.isViewSavable ? parent.saveAsync() : saveAsDialogModel.open();
|
|
131
|
-
},
|
|
129
|
+
onClick: () => model.saveAsync(),
|
|
132
130
|
...rest
|
|
133
131
|
});
|
|
134
132
|
}
|
|
@@ -144,7 +142,7 @@ const revertButton = hoistCmp.factory<ViewManagerLocalModel>({
|
|
|
144
142
|
tooltip: `Revert changes to this ${typeDisplayName}`,
|
|
145
143
|
intent: 'danger',
|
|
146
144
|
disabled: !isValueDirty || isLoading,
|
|
147
|
-
onClick: () => model.
|
|
145
|
+
onClick: () => model.revertAsync(),
|
|
148
146
|
...rest
|
|
149
147
|
});
|
|
150
148
|
}
|
|
@@ -23,6 +23,24 @@ 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
|
+
|
|
26
44
|
constructor(parent: ViewManagerModel) {
|
|
27
45
|
super();
|
|
28
46
|
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).catchDefault()
|
|
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: () => model.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: () => model.revertAsync()
|
|
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).catchDefault()
|
|
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.1736263796812",
|
|
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",
|