-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhandleHostStoreChange.ts
More file actions
211 lines (197 loc) · 6.46 KB
/
handleHostStoreChange.ts
File metadata and controls
211 lines (197 loc) · 6.46 KB
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
import memoize from "micro-memoize";
import type {
Callback,
CallbackRef,
CallbackRequest,
ContribRef,
InputRef,
} from "@/types/model/callback";
import { getInputValues } from "@/actions/helpers/getInputValues";
import { formatObjPath } from "@/utils/objPath";
import { invokeCallbacks } from "@/actions/helpers/invokeCallbacks";
import type { ContributionState } from "@/types/state/contribution";
import type { HostStore } from "@/types/state/host";
import { store } from "@/store";
import { shallowEqualArrays } from "@/utils/shallowEqualArrays";
import type { ContribPoint } from "@/types/model/extension";
/**
* A reference to a property of an input of a callback of a contribution.
*/
export interface PropertyRef extends ContribRef, CallbackRef, InputRef {
/** The property. */
property: string;
}
/**
* This action is called once a host store change has been detected.
* Note this will only create callback requests for callbacks whose input
* value are affected by the change.
*
* @returns The array of callback requests made or `undefined`,
* if no callback requests have been made.
*/
export function handleHostStoreChange(): CallbackRequest[] | undefined {
const { extensions, configuration, contributionsRecord } = store.getState();
const { hostStore } = configuration;
if (!hostStore) {
// Exit if no host store configured.
// Actually, we should not come here.
return undefined;
}
synchronizeThemeMode(hostStore);
if (extensions.length === 0) {
// Exit if there are no extensions (yet)
return undefined;
}
const propertyRefs = getPropertyRefsForContribPoints(contributionsRecord);
if (!propertyRefs || propertyRefs.length === 0) {
// Exit if there are is nothing to be changed
return undefined;
}
const callbackRequests = getCallbackRequests(
propertyRefs,
contributionsRecord,
hostStore,
);
const filteredCallbackRequests = callbackRequests.filter(
(callbackRequest): callbackRequest is CallbackRequest =>
callbackRequest !== undefined,
);
if (!filteredCallbackRequests || !filteredCallbackRequests.length) {
return undefined;
}
invokeCallbacks(filteredCallbackRequests);
return filteredCallbackRequests;
}
// Exporting for testing only
export function getCallbackRequests(
propertyRefs: PropertyRef[],
contributionsRecord: Record<string, ContributionState[]>,
hostStore: HostStore,
): (CallbackRequest | undefined)[] {
const { lastCallbackInputValues } = store.getState();
return propertyRefs.map((propertyRef) =>
getCallbackRequest(
propertyRef,
lastCallbackInputValues,
contributionsRecord,
hostStore,
),
);
}
const getCallbackRequest = (
propertyRef: PropertyRef,
lastCallbackInputValues: Record<string, unknown[]>,
contributionsRecord: Record<string, ContributionState[]>,
hostStore: HostStore,
) => {
const contribPoint: string = propertyRef.contribPoint;
const contribIndex: number = propertyRef.contribIndex;
const callbackIndex: number = propertyRef.callbackIndex;
const contributions = contributionsRecord[contribPoint];
const contribution = contributions[contribIndex];
const callback = contribution.callbacks![callbackIndex];
const inputValues = getInputValues(callback.inputs!, contribution, hostStore);
const callbackId = `${contribPoint}-${contribIndex}-${callbackIndex}`;
const lastInputValues = lastCallbackInputValues[callbackId];
if (shallowEqualArrays(lastInputValues, inputValues)) {
// We no longer log, as the situation is quite common
// Enable error logging for debugging only:
// console.groupCollapsed("Skipping callback request");
// console.debug("inputValues", inputValues);
// console.groupEnd();
return undefined;
}
store.setState({
lastCallbackInputValues: {
...lastCallbackInputValues,
[callbackId]: inputValues,
},
});
// Collect output IDs for updating their respective loading states
const outputs = contribution.callbacks?.[callbackIndex]["outputs"];
const outputIds: string[] =
outputs?.map((output) => output.id as string) ?? [];
return { ...propertyRef, inputValues, outputIds };
};
/**
* Get the static list of host state property references
* for given contribution points.
* Note: the export exists only for testing.
*/
export const getPropertyRefsForContribPoints = memoize(
_getPropertyRefsForContribPoints,
);
function _getPropertyRefsForContribPoints(
contributionsRecord: Record<ContribPoint, ContributionState[]>,
): PropertyRef[] {
const propertyRefs: PropertyRef[] = [];
Object.getOwnPropertyNames(contributionsRecord).forEach((contribPoint) => {
const contributions = contributionsRecord[contribPoint];
propertyRefs.push(
...getPropertyRefsForContributions(contribPoint, contributions),
);
});
return propertyRefs;
}
/**
* Get the static list of host state property references
* for given contributions.
*/
const getPropertyRefsForContributions = memoize(
_getPropertyRefsForContributions,
);
function _getPropertyRefsForContributions(
contribPoint: string,
contributions: ContributionState[],
): PropertyRef[] {
const propertyRefs: PropertyRef[] = [];
contributions.forEach((contribution, contribIndex) => {
propertyRefs.push(
...getPropertyRefsForCallbacks(
contribPoint,
contribIndex,
contribution.callbacks,
),
);
});
return propertyRefs;
}
/**
* Get the static list of host state property references
* for given callbacks.
*/
const getPropertyRefsForCallbacks = memoize(_getPropertyRefsForCallbacks);
function _getPropertyRefsForCallbacks(
contribPoint: string,
contribIndex: number,
callbacks: Callback[] | undefined,
) {
const propertyRefs: PropertyRef[] = [];
(callbacks || []).forEach((callback, callbackIndex) => {
const inputs = callback.inputs || [];
inputs.forEach((input, inputIndex) => {
if (!input.noTrigger && input.id === "@app" && input.property) {
propertyRefs.push({
contribPoint,
contribIndex,
callbackIndex,
inputIndex,
property: formatObjPath(input.property),
});
}
});
});
return propertyRefs;
}
function synchronizeThemeMode(hostStore: HostStore) {
const newThemeMode = hostStore.get("themeMode");
const oldThemeMode = store.getState().themeMode;
if (
(newThemeMode === "dark" ||
newThemeMode === "light" ||
newThemeMode === "system") &&
newThemeMode !== oldThemeMode
) {
store.setState({ themeMode: newThemeMode });
}
}