-
Notifications
You must be signed in to change notification settings - Fork 50
/
contributed-action-parent-view-model.ts
158 lines (135 loc) · 6.47 KB
/
contributed-action-parent-view-model.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import * as Ro from '@nakedobjects/restful-objects';
import {
ContextService,
ErrorCategory,
ErrorService,
ErrorWrapper,
HttpStatusCode,
Pane,
PaneRouteData,
UrlManagerService
} from '@nakedobjects/services';
import { Dictionary } from 'lodash';
import clone from 'lodash-es/clone';
import each from 'lodash-es/each';
import every from 'lodash-es/every';
import filter from 'lodash-es/filter';
import find from 'lodash-es/find';
import first from 'lodash-es/first';
import forEach from 'lodash-es/forEach';
import keys from 'lodash-es/keys';
import map from 'lodash-es/map';
import some from 'lodash-es/some';
import toArray from 'lodash-es/toArray';
import values from 'lodash-es/values';
import { ActionViewModel } from './action-view-model';
import * as Helpers from './helpers-view-models';
import { ItemViewModel } from './item-view-model';
import { MenuItemViewModel } from './menu-item-view-model';
import { MessageViewModel } from './message-view-model';
import { ParameterViewModel } from './parameter-view-model';
import * as Msg from './user-messages';
import { ViewModelFactoryService } from './view-model-factory.service';
export abstract class ContributedActionParentViewModel extends MessageViewModel {
protected constructor(
protected readonly context: ContextService,
protected readonly viewModelFactory: ViewModelFactoryService,
protected readonly urlManager: UrlManagerService,
protected readonly error: ErrorService,
public readonly onPaneId: Pane
) {
super();
}
items: ItemViewModel[];
actions: ActionViewModel[];
menuItems: MenuItemViewModel[];
readonly allSelected = () => every(this.items, item => item.selected);
private isLocallyContributed(action: Ro.ActionRepresentation | Ro.InvokableActionMember) {
return some(action.parameters(), p => p.isCollectionContributed());
}
setActions(actions: Dictionary<Ro.ActionMember>, routeData: PaneRouteData) {
this.actions = map(actions, action => this.viewModelFactory.actionViewModel(action, this, routeData)).filter(avm => !avm.returnsScalar());
this.menuItems = Helpers.createMenuItems(this.actions);
forEach(this.actions, a => this.decorate(a));
}
protected collectionContributedActionDecorator(actionViewModel: ActionViewModel) {
const wrappedInvoke = actionViewModel.execute;
actionViewModel.execute = (pps: ParameterViewModel[], right?: boolean) => {
const selected = filter(this.items, i => i.selected);
const rejectAsNeedSelection = (action: Ro.ActionRepresentation | Ro.InvokableActionMember): ErrorWrapper | null => {
if (this.isLocallyContributed(action)) {
if (selected.length === 0) {
const em = new Ro.ErrorMap({}, 0, Msg.noItemsSelected);
const rp = new ErrorWrapper(ErrorCategory.HttpClientError, HttpStatusCode.UnprocessableEntity, em);
return rp;
}
}
return null;
};
const getParms = (action: Ro.ActionRepresentation | Ro.InvokableActionMember) => {
const parms = values(action.parameters()) as Ro.Parameter[];
const contribParm = find(parms, p => p.isCollectionContributed());
if (contribParm) {
const parmValue = new Ro.Value(map(selected, i => i.link));
const collectionParmVm = this.viewModelFactory.parameterViewModel(contribParm, parmValue, this.onPaneId);
const allpps = clone(pps);
allpps.push(collectionParmVm);
return allpps;
}
return pps;
};
const detailsPromise = actionViewModel.invokableActionRep
? Promise.resolve(actionViewModel.invokableActionRep)
: this.context.getActionDetails(actionViewModel.actionRep as Ro.ActionMember);
return detailsPromise.
then(details => {
const rp = rejectAsNeedSelection(details);
return rp ? Promise.reject(rp) : wrappedInvoke(getParms(details), right);
}).
then(result => {
// clear selected items on void actions
this.clearSelected(result);
return result;
});
};
}
protected collectionContributedInvokeDecorator(actionViewModel: ActionViewModel) {
const showDialog = () =>
this.context.getInvokableAction(actionViewModel.actionRep).
then(invokableAction => {
actionViewModel.makeInvokable(invokableAction);
const keyCount = keys(invokableAction.parameters()).length;
return keyCount > 1 || keyCount === 1 && !toArray(invokableAction.parameters())[0].isCollectionContributed();
});
// make sure not invokable while waiting for promise to assign correct function
actionViewModel.doInvoke = () => { };
const invokeWithoutDialog = (right?: boolean) =>
actionViewModel.invokeWithoutDialogWithParameters(Promise.resolve([]), right).then((actionResult: Ro.ActionResultRepresentation) => {
this.setMessage(actionResult.shouldExpectResult() ? actionResult.warningsOrMessages() || Msg.noResultMessage : '');
// clear selected items on void actions
this.clearSelected(actionResult);
});
showDialog().
then(show => actionViewModel.doInvoke = show ? actionViewModel.invokeWithDialog : invokeWithoutDialog).
catch((reject: ErrorWrapper) => this.error.handleError(reject));
}
protected decorate(actionViewModel: ActionViewModel) {
this.collectionContributedActionDecorator(actionViewModel);
this.collectionContributedInvokeDecorator(actionViewModel);
}
private setItems(newValue: boolean) {
each(this.items, item => item.silentSelect(newValue));
// TODO fix "!"
const id = first(this.items)!.id;
this.urlManager.setAllItemsSelected(newValue, id, this.onPaneId);
}
protected clearSelected(result: Ro.ActionResultRepresentation) {
if (result.resultType() === 'void') {
this.setItems(false);
}
}
readonly selectAll = () => {
const newState = !this.allSelected();
this.setItems(newState);
}
}