@nordicsemiconductor/pc-nrfconnect-shared 102.0.0 → 104.0.0
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/Changelog.md +27 -0
- package/coverage/cobertura-coverage.xml +334 -395
- package/nrfutil/device/batch.ts +147 -53
- package/nrfutil/device/batchTypes.ts +2 -2
- package/nrfutil/device/common.ts +5 -10
- package/nrfutil/device/device.ts +3 -4
- package/nrfutil/device/list.ts +1 -2
- package/nrfutil/device/program.ts +1 -1
- package/nrfutil/sandbox.ts +20 -0
- package/nrfutil/sandboxTypes.ts +6 -4
- package/package.json +1 -1
- package/src/App/App.tsx +1 -1
- package/src/Button/Button.tsx +30 -31
- package/src/Dialog/Dialog.tsx +3 -1
- package/src/Dialog/dialog.scss +0 -8
- package/src/{Panes → Feedback}/FeedbackPane.tsx +5 -36
- package/src/Feedback/sendFeedback.ts +42 -0
- package/src/index.ts +2 -0
- package/typings/generated/nrfutil/device/batch.d.ts +5 -7
- package/typings/generated/nrfutil/device/batch.d.ts.map +1 -1
- package/typings/generated/nrfutil/device/batchTypes.d.ts +2 -2
- package/typings/generated/nrfutil/device/batchTypes.d.ts.map +1 -1
- package/typings/generated/nrfutil/device/common.d.ts.map +1 -1
- package/typings/generated/nrfutil/device/device.d.ts.map +1 -1
- package/typings/generated/nrfutil/device/list.d.ts.map +1 -1
- package/typings/generated/nrfutil/device/program.d.ts +1 -0
- package/typings/generated/nrfutil/device/program.d.ts.map +1 -1
- package/typings/generated/nrfutil/sandbox.d.ts +1 -0
- package/typings/generated/nrfutil/sandbox.d.ts.map +1 -1
- package/typings/generated/nrfutil/sandboxTypes.d.ts +5 -4
- package/typings/generated/nrfutil/sandboxTypes.d.ts.map +1 -1
- package/typings/generated/src/App/App.d.ts +1 -1
- package/typings/generated/src/App/App.d.ts.map +1 -1
- package/typings/generated/src/Button/Button.d.ts.map +1 -1
- package/typings/generated/src/Dialog/Dialog.d.ts.map +1 -1
- package/typings/generated/src/Feedback/FeedbackPane.d.ts.map +1 -0
- package/typings/generated/src/Feedback/sendFeedback.d.ts +3 -0
- package/typings/generated/src/Feedback/sendFeedback.d.ts.map +1 -0
- package/typings/generated/src/index.d.ts +1 -0
- package/typings/generated/src/index.d.ts.map +1 -1
- package/nrfutil/device/eraseBatch.ts +0 -28
- package/nrfutil/device/firmwareReadBatch.ts +0 -42
- package/nrfutil/device/getCoreInfoBatch.ts +0 -29
- package/nrfutil/device/getFwInfoBatch.ts +0 -29
- package/nrfutil/device/getProtectionStatusBatch.ts +0 -32
- package/nrfutil/device/programBatch.ts +0 -69
- package/nrfutil/device/recoverBatch.ts +0 -28
- package/nrfutil/device/resetBatch.ts +0 -30
- package/typings/generated/nrfutil/device/eraseBatch.d.ts +0 -7
- package/typings/generated/nrfutil/device/eraseBatch.d.ts.map +0 -1
- package/typings/generated/nrfutil/device/firmwareReadBatch.d.ts +0 -9
- package/typings/generated/nrfutil/device/firmwareReadBatch.d.ts.map +0 -1
- package/typings/generated/nrfutil/device/getCoreInfoBatch.d.ts +0 -8
- package/typings/generated/nrfutil/device/getCoreInfoBatch.d.ts.map +0 -1
- package/typings/generated/nrfutil/device/getFwInfoBatch.d.ts +0 -8
- package/typings/generated/nrfutil/device/getFwInfoBatch.d.ts.map +0 -1
- package/typings/generated/nrfutil/device/getProtectionStatusBatch.d.ts +0 -8
- package/typings/generated/nrfutil/device/getProtectionStatusBatch.d.ts.map +0 -1
- package/typings/generated/nrfutil/device/programBatch.d.ts +0 -9
- package/typings/generated/nrfutil/device/programBatch.d.ts.map +0 -1
- package/typings/generated/nrfutil/device/recoverBatch.d.ts +0 -7
- package/typings/generated/nrfutil/device/recoverBatch.d.ts.map +0 -1
- package/typings/generated/nrfutil/device/resetBatch.d.ts +0 -8
- package/typings/generated/nrfutil/device/resetBatch.d.ts.map +0 -1
- package/typings/generated/src/Panes/FeedbackPane.d.ts.map +0 -1
- /package/typings/generated/src/{Panes → Feedback}/FeedbackPane.d.ts +0 -0
package/nrfutil/device/batch.ts
CHANGED
|
@@ -4,31 +4,37 @@
|
|
|
4
4
|
* SPDX-License-Identifier: LicenseRef-Nordic-4-Clause
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import os from 'os';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { v4 as uuid } from 'uuid';
|
|
11
|
+
|
|
7
12
|
import { TaskEnd } from '../sandboxTypes';
|
|
8
13
|
import { BatchOperationWrapper, Callbacks } from './batchTypes';
|
|
9
14
|
import {
|
|
10
15
|
DeviceCore,
|
|
16
|
+
DeviceTraits,
|
|
17
|
+
deviceTraitsToArgs,
|
|
11
18
|
getDeviceSandbox,
|
|
12
19
|
NrfutilDeviceWithSerialnumber,
|
|
13
20
|
ResetKind,
|
|
14
21
|
} from './common';
|
|
15
|
-
import
|
|
16
|
-
import firmwareReadBatch from './firmwareReadBatch';
|
|
22
|
+
import { DeviceBuffer } from './firmwareRead';
|
|
17
23
|
import { DeviceCoreInfo } from './getCoreInfo';
|
|
18
|
-
import getCoreInfoBatch from './getCoreInfoBatch';
|
|
19
24
|
import { FWInfo } from './getFwInfo';
|
|
20
|
-
import getFwInfoBatch from './getFwInfoBatch';
|
|
21
25
|
import { GetProtectionStatusResult } from './getProtectionStatus';
|
|
22
|
-
import
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
import {
|
|
27
|
+
FirmwareType,
|
|
28
|
+
ProgrammingOptions,
|
|
29
|
+
programmingOptionsToArgs,
|
|
30
|
+
} from './program';
|
|
27
31
|
|
|
28
|
-
type BatchOperationWrapperUnknown = BatchOperationWrapper<unknown
|
|
32
|
+
type BatchOperationWrapperUnknown = BatchOperationWrapper<unknown>;
|
|
33
|
+
type CallbacksUnknown = Callbacks<unknown>;
|
|
29
34
|
|
|
30
35
|
export class Batch {
|
|
31
|
-
private
|
|
36
|
+
private operationBatchGeneration: Promise<BatchOperationWrapperUnknown>[] =
|
|
37
|
+
[];
|
|
32
38
|
|
|
33
39
|
private collectOperations: {
|
|
34
40
|
callback: (completedTasks: TaskEnd<unknown>[]) => void;
|
|
@@ -36,24 +42,63 @@ export class Batch {
|
|
|
36
42
|
count: number;
|
|
37
43
|
}[] = [];
|
|
38
44
|
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
private enqueueBatchOperationObject(
|
|
46
|
+
command: string,
|
|
47
|
+
core: DeviceCore,
|
|
48
|
+
callbacks?: Callbacks<unknown>,
|
|
49
|
+
args: string[] = []
|
|
50
|
+
) {
|
|
51
|
+
const getPromise = async () => {
|
|
52
|
+
const box = await getDeviceSandbox();
|
|
53
|
+
|
|
54
|
+
const batchOperation =
|
|
55
|
+
await box.singleInfoOperationOptionalData<object>(
|
|
56
|
+
command,
|
|
57
|
+
undefined,
|
|
58
|
+
[
|
|
59
|
+
'--serial-number', // this is a workaround this param should now be needed with --generate
|
|
60
|
+
'123',
|
|
61
|
+
'--generate',
|
|
62
|
+
'--core',
|
|
63
|
+
core,
|
|
64
|
+
].concat(args)
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
operation: {
|
|
69
|
+
...batchOperation,
|
|
70
|
+
},
|
|
71
|
+
...callbacks,
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
this.operationBatchGeneration.push(getPromise());
|
|
41
76
|
}
|
|
42
77
|
|
|
43
78
|
public erase(core: DeviceCore, callbacks?: Callbacks) {
|
|
44
|
-
this.
|
|
45
|
-
|
|
79
|
+
this.enqueueBatchOperationObject(
|
|
80
|
+
'erase',
|
|
81
|
+
core,
|
|
82
|
+
callbacks as CallbacksUnknown
|
|
46
83
|
);
|
|
47
84
|
|
|
48
85
|
return this;
|
|
49
86
|
}
|
|
50
87
|
|
|
51
88
|
public firmwareRead(core: DeviceCore, callbacks?: Callbacks<Buffer>) {
|
|
52
|
-
this.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
89
|
+
this.enqueueBatchOperationObject('fw-read', core, {
|
|
90
|
+
...callbacks,
|
|
91
|
+
onTaskEnd: (taskEnd: TaskEnd<DeviceBuffer>) => {
|
|
92
|
+
if (taskEnd.result === 'success' && taskEnd.data)
|
|
93
|
+
callbacks?.onTaskEnd?.({
|
|
94
|
+
...taskEnd,
|
|
95
|
+
data: Buffer.from(taskEnd.data.buffer, 'base64'),
|
|
96
|
+
});
|
|
97
|
+
else {
|
|
98
|
+
callbacks?.onException?.(new Error('Read failed'));
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
} as CallbacksUnknown);
|
|
57
102
|
|
|
58
103
|
return this;
|
|
59
104
|
}
|
|
@@ -62,20 +107,20 @@ export class Batch {
|
|
|
62
107
|
core: DeviceCore,
|
|
63
108
|
callbacks?: Callbacks<DeviceCoreInfo>
|
|
64
109
|
) {
|
|
65
|
-
this.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
110
|
+
this.enqueueBatchOperationObject(
|
|
111
|
+
'core-info',
|
|
112
|
+
core,
|
|
113
|
+
callbacks as CallbacksUnknown
|
|
69
114
|
);
|
|
70
115
|
|
|
71
116
|
return this;
|
|
72
117
|
}
|
|
73
118
|
|
|
74
119
|
public getFwInfo(core: DeviceCore, callbacks?: Callbacks<FWInfo>) {
|
|
75
|
-
this.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
120
|
+
this.enqueueBatchOperationObject(
|
|
121
|
+
'fw-info',
|
|
122
|
+
core,
|
|
123
|
+
callbacks as CallbacksUnknown
|
|
79
124
|
);
|
|
80
125
|
|
|
81
126
|
return this;
|
|
@@ -85,10 +130,10 @@ export class Batch {
|
|
|
85
130
|
core: DeviceCore,
|
|
86
131
|
callbacks?: Callbacks<GetProtectionStatusResult>
|
|
87
132
|
) {
|
|
88
|
-
this.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
133
|
+
this.enqueueBatchOperationObject(
|
|
134
|
+
'protection-get',
|
|
135
|
+
core,
|
|
136
|
+
callbacks as CallbacksUnknown
|
|
92
137
|
);
|
|
93
138
|
|
|
94
139
|
return this;
|
|
@@ -98,32 +143,69 @@ export class Batch {
|
|
|
98
143
|
firmware: FirmwareType,
|
|
99
144
|
core: DeviceCore,
|
|
100
145
|
programmingOptions?: ProgrammingOptions,
|
|
146
|
+
deviceTraits?: DeviceTraits,
|
|
101
147
|
callbacks?: Callbacks
|
|
102
148
|
) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
149
|
+
let args = [
|
|
150
|
+
...(deviceTraits ? deviceTraitsToArgs(deviceTraits) : []),
|
|
151
|
+
...programmingOptionsToArgs(programmingOptions),
|
|
152
|
+
];
|
|
153
|
+
let newCallbacks = { ...callbacks };
|
|
154
|
+
|
|
155
|
+
if (typeof firmware === 'string') {
|
|
156
|
+
args = ['--firmware', firmware].concat(args);
|
|
157
|
+
} else {
|
|
158
|
+
const saveTemp = (): string => {
|
|
159
|
+
let tempFilePath;
|
|
160
|
+
do {
|
|
161
|
+
tempFilePath = path.join(
|
|
162
|
+
os.tmpdir(),
|
|
163
|
+
`${uuid()}.${firmware.type}`
|
|
164
|
+
);
|
|
165
|
+
} while (fs.existsSync(tempFilePath));
|
|
166
|
+
|
|
167
|
+
fs.writeFileSync(tempFilePath, firmware.buffer);
|
|
168
|
+
|
|
169
|
+
return tempFilePath;
|
|
170
|
+
};
|
|
171
|
+
const tempFilePath = saveTemp();
|
|
172
|
+
args = ['--firmware', tempFilePath].concat(args);
|
|
173
|
+
|
|
174
|
+
newCallbacks = {
|
|
175
|
+
...callbacks,
|
|
176
|
+
onTaskEnd: (taskEnd: TaskEnd<void>) => {
|
|
177
|
+
fs.unlinkSync(tempFilePath);
|
|
178
|
+
callbacks?.onTaskEnd?.(taskEnd);
|
|
179
|
+
},
|
|
180
|
+
} as CallbacksUnknown;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
this.enqueueBatchOperationObject(
|
|
184
|
+
'program',
|
|
185
|
+
core,
|
|
186
|
+
newCallbacks as CallbacksUnknown,
|
|
187
|
+
args
|
|
108
188
|
);
|
|
109
189
|
|
|
110
190
|
return this;
|
|
111
191
|
}
|
|
112
192
|
|
|
113
193
|
public recover(core: DeviceCore, callbacks?: Callbacks) {
|
|
114
|
-
this.
|
|
115
|
-
|
|
194
|
+
this.enqueueBatchOperationObject(
|
|
195
|
+
'recover',
|
|
196
|
+
core,
|
|
197
|
+
callbacks as CallbacksUnknown
|
|
116
198
|
);
|
|
117
199
|
|
|
118
200
|
return this;
|
|
119
201
|
}
|
|
120
202
|
|
|
121
203
|
public reset(core: DeviceCore, reset?: ResetKind, callbacks?: Callbacks) {
|
|
122
|
-
this.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
204
|
+
this.enqueueBatchOperationObject(
|
|
205
|
+
'reset',
|
|
206
|
+
core,
|
|
207
|
+
callbacks as CallbacksUnknown,
|
|
208
|
+
reset ? ['--reset-kind', reset] : undefined
|
|
127
209
|
);
|
|
128
210
|
|
|
129
211
|
return this;
|
|
@@ -135,7 +217,7 @@ export class Batch {
|
|
|
135
217
|
) {
|
|
136
218
|
this.collectOperations.push({
|
|
137
219
|
callback,
|
|
138
|
-
operationId: this.
|
|
220
|
+
operationId: this.operationBatchGeneration.length - 1,
|
|
139
221
|
count,
|
|
140
222
|
});
|
|
141
223
|
|
|
@@ -149,9 +231,21 @@ export class Batch {
|
|
|
149
231
|
let beginId = 0;
|
|
150
232
|
let endId = 0;
|
|
151
233
|
const results: TaskEnd<unknown>[] = [];
|
|
234
|
+
const operations: BatchOperationWrapperUnknown[] = [];
|
|
235
|
+
|
|
236
|
+
const promiseResults =
|
|
237
|
+
await Promise.allSettled<BatchOperationWrapperUnknown>(
|
|
238
|
+
this.operationBatchGeneration
|
|
239
|
+
);
|
|
240
|
+
promiseResults.forEach(r => {
|
|
241
|
+
if (r.status === 'rejected') {
|
|
242
|
+
throw r.reason;
|
|
243
|
+
}
|
|
244
|
+
operations.push(r.value);
|
|
245
|
+
});
|
|
152
246
|
|
|
153
|
-
const
|
|
154
|
-
operations:
|
|
247
|
+
const batchOperation = {
|
|
248
|
+
operations: operations.map((operation, index) => ({
|
|
155
249
|
operationId: index.toString(),
|
|
156
250
|
...operation.operation,
|
|
157
251
|
})),
|
|
@@ -165,21 +259,21 @@ export class Batch {
|
|
|
165
259
|
'--serial-number',
|
|
166
260
|
device.serialNumber,
|
|
167
261
|
'--batch-json',
|
|
168
|
-
JSON.stringify(
|
|
262
|
+
JSON.stringify(batchOperation),
|
|
169
263
|
],
|
|
170
264
|
(progress, task) => {
|
|
171
265
|
if (task) {
|
|
172
|
-
|
|
266
|
+
operations[endId].onProgress?.(progress, task);
|
|
173
267
|
}
|
|
174
268
|
},
|
|
175
269
|
onTaskBegin => {
|
|
176
270
|
beginId += 1;
|
|
177
|
-
|
|
271
|
+
operations[endId].onTaskBegin?.(onTaskBegin);
|
|
178
272
|
},
|
|
179
273
|
taskEnd => {
|
|
180
274
|
results.push(taskEnd);
|
|
181
275
|
|
|
182
|
-
|
|
276
|
+
operations[endId].onTaskEnd?.(taskEnd);
|
|
183
277
|
|
|
184
278
|
this.collectOperations
|
|
185
279
|
.filter(operation => operation.operationId === endId)
|
|
@@ -204,12 +298,12 @@ export class Batch {
|
|
|
204
298
|
.map(e => `error: ${e.error}, message: ${e.message}`)
|
|
205
299
|
.join('\n')}`
|
|
206
300
|
);
|
|
207
|
-
|
|
301
|
+
operations[endId].onException?.(error);
|
|
208
302
|
throw error;
|
|
209
303
|
}
|
|
210
304
|
} catch (error) {
|
|
211
305
|
if (beginId !== endId) {
|
|
212
|
-
|
|
306
|
+
operations[beginId].onException?.(error as Error);
|
|
213
307
|
}
|
|
214
308
|
throw error;
|
|
215
309
|
}
|
|
@@ -13,8 +13,8 @@ import {
|
|
|
13
13
|
ProgrammingOptions,
|
|
14
14
|
} from './program';
|
|
15
15
|
|
|
16
|
-
export interface BatchOperationWrapper<
|
|
17
|
-
operation:
|
|
16
|
+
export interface BatchOperationWrapper<T = void> {
|
|
17
|
+
operation: object;
|
|
18
18
|
onProgress?: (progress: Progress, task?: Task) => void;
|
|
19
19
|
onTaskBegin?: TaskBeginCallback;
|
|
20
20
|
onTaskEnd?: TaskEndCallback<T>;
|
package/nrfutil/device/common.ts
CHANGED
|
@@ -205,12 +205,6 @@ export const getDeviceSandbox = async () => {
|
|
|
205
205
|
deviceSandbox = await promiseDeviceSandbox;
|
|
206
206
|
|
|
207
207
|
deviceSandbox.onLogging(evt => {
|
|
208
|
-
if (
|
|
209
|
-
process.env.NODE_ENV === 'production' &&
|
|
210
|
-
deviceSandbox?.logLevel !== 'trace'
|
|
211
|
-
)
|
|
212
|
-
return;
|
|
213
|
-
|
|
214
208
|
const deviceLogger = getNrfutilLogger();
|
|
215
209
|
switch (evt.level) {
|
|
216
210
|
case 'TRACE':
|
|
@@ -238,10 +232,11 @@ export const getDeviceSandbox = async () => {
|
|
|
238
232
|
}
|
|
239
233
|
});
|
|
240
234
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
235
|
+
const fallbackLevel =
|
|
236
|
+
process.env.NODE_ENV === 'production' ? 'off' : 'error';
|
|
237
|
+
deviceSandbox.setLogLevel(
|
|
238
|
+
getIsLoggingVerbose() ? 'trace' : fallbackLevel
|
|
239
|
+
);
|
|
245
240
|
// Only the first reset after selecting "reset with verbose logging" is relevant
|
|
246
241
|
persistIsLoggingVerbose(false);
|
|
247
242
|
}
|
package/nrfutil/device/device.ts
CHANGED
|
@@ -31,11 +31,10 @@ const setLogLevel = async (level: LogLevel) => {
|
|
|
31
31
|
|
|
32
32
|
const setVerboseLogging = async (verbose: boolean) => {
|
|
33
33
|
const sandbox = await getDeviceSandbox();
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
// process.env.NODE_ENV === 'production' ? 'off' : 'error';
|
|
34
|
+
const fallbackLevel =
|
|
35
|
+
process.env.NODE_ENV === 'production' ? 'off' : 'error';
|
|
37
36
|
|
|
38
|
-
sandbox.setLogLevel(verbose ? 'trace' :
|
|
37
|
+
sandbox.setLogLevel(verbose ? 'trace' : fallbackLevel);
|
|
39
38
|
};
|
|
40
39
|
const getModuleVersion = async () => {
|
|
41
40
|
const sandbox = await getDeviceSandbox();
|
package/nrfutil/device/list.ts
CHANGED
|
@@ -72,7 +72,7 @@ const deviceTraitsToArgs = (traits: DeviceTraits) => {
|
|
|
72
72
|
return args;
|
|
73
73
|
};
|
|
74
74
|
|
|
75
|
-
const programmingOptionsToArgs = (options?: ProgrammingOptions) => {
|
|
75
|
+
export const programmingOptionsToArgs = (options?: ProgrammingOptions) => {
|
|
76
76
|
if (!options) return [];
|
|
77
77
|
|
|
78
78
|
const args: string[] = [];
|
package/nrfutil/sandbox.ts
CHANGED
|
@@ -449,6 +449,26 @@ export class NrfutilSandbox {
|
|
|
449
449
|
throw new Error('Unexpected result');
|
|
450
450
|
};
|
|
451
451
|
|
|
452
|
+
public singleInfoOperationOptionalData = async <T = void>(
|
|
453
|
+
command: string,
|
|
454
|
+
controller?: AbortController,
|
|
455
|
+
args: string[] = []
|
|
456
|
+
) => {
|
|
457
|
+
const results = await this.execSubcommand<T>(
|
|
458
|
+
command,
|
|
459
|
+
args,
|
|
460
|
+
undefined,
|
|
461
|
+
undefined,
|
|
462
|
+
undefined,
|
|
463
|
+
controller
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
if (results.info.length === 1) {
|
|
467
|
+
return results.info[0];
|
|
468
|
+
}
|
|
469
|
+
throw new Error('Unexpected result');
|
|
470
|
+
};
|
|
471
|
+
|
|
452
472
|
public onLogging = (handler: (logging: LogMessage) => void) => {
|
|
453
473
|
this.onLoggingHandlers.push(handler);
|
|
454
474
|
|
package/nrfutil/sandboxTypes.ts
CHANGED
|
@@ -82,6 +82,11 @@ type NrfutilJsonBegin = {
|
|
|
82
82
|
data: TaskBegin;
|
|
83
83
|
};
|
|
84
84
|
|
|
85
|
+
type NrfutilJsonInfo<T> = {
|
|
86
|
+
type: 'info';
|
|
87
|
+
data: T;
|
|
88
|
+
};
|
|
89
|
+
|
|
85
90
|
type NrfutilJsonBatch<T = unknown> = {
|
|
86
91
|
batch: {
|
|
87
92
|
id: string;
|
|
@@ -98,10 +103,7 @@ export type NrfutilJsonBatchUpdate<T = unknown> = {
|
|
|
98
103
|
};
|
|
99
104
|
|
|
100
105
|
export type NrfutilJson<T = unknown> =
|
|
101
|
-
|
|
|
102
|
-
type: 'info';
|
|
103
|
-
data: T;
|
|
104
|
-
}
|
|
106
|
+
| NrfutilJsonInfo<T>
|
|
105
107
|
| NrfutilJsonBegin
|
|
106
108
|
| NrfutilJsonEnd<T>
|
|
107
109
|
| NrfutilJsonProgress
|
package/package.json
CHANGED
package/src/App/App.tsx
CHANGED
|
@@ -24,11 +24,11 @@ import {
|
|
|
24
24
|
} from '../Device/deviceSlice';
|
|
25
25
|
import ErrorBoundary from '../ErrorBoundary/ErrorBoundary';
|
|
26
26
|
import ErrorDialog from '../ErrorDialog/ErrorDialog';
|
|
27
|
+
import FeedbackPane, { FeedbackPaneProps } from '../Feedback/FeedbackPane';
|
|
27
28
|
import FlashMessages from '../FlashMessage/FlashMessage';
|
|
28
29
|
import LogViewer from '../Log/LogViewer';
|
|
29
30
|
import logger from '../logging';
|
|
30
31
|
import NavBar from '../NavBar/NavBar';
|
|
31
|
-
import FeedbackPane, { FeedbackPaneProps } from '../Panes/FeedbackPane';
|
|
32
32
|
import classNames from '../utils/classNames';
|
|
33
33
|
import packageJson from '../utils/packageJson';
|
|
34
34
|
import { getPersistedCurrentPane } from '../utils/persistentStore';
|
package/src/Button/Button.tsx
CHANGED
|
@@ -39,37 +39,36 @@ const Button: React.FC<ButtonProps> = ({
|
|
|
39
39
|
title,
|
|
40
40
|
size = 'sm',
|
|
41
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
|
-
</div>
|
|
42
|
+
<button
|
|
43
|
+
type="button"
|
|
44
|
+
id={id}
|
|
45
|
+
className={`${classNames(
|
|
46
|
+
'tw-preflight',
|
|
47
|
+
size === 'sm' && 'tw-h-6 tw-px-2 tw-text-xs',
|
|
48
|
+
size === 'lg' && 'tw-h-8 tw-px-4 tw-text-sm',
|
|
49
|
+
size === 'xl' && 'tw-h-8 tw-px-4 tw-text-base',
|
|
50
|
+
variant === 'primary' &&
|
|
51
|
+
'tw-bg-nordicBlue tw-text-white active:enabled:tw-bg-nordicBlue-700',
|
|
52
|
+
variant === 'secondary' &&
|
|
53
|
+
'tw-border tw-border-gray-700 tw-bg-white tw-text-gray-700 active:enabled:tw-bg-gray-50',
|
|
54
|
+
variant === 'success' &&
|
|
55
|
+
'tw-bg-green tw-text-white active:enabled:tw-bg-green-700',
|
|
56
|
+
variant === 'info' &&
|
|
57
|
+
'tw-bg-nordicBlue tw-text-white active:enabled:tw-bg-nordicBlue-700',
|
|
58
|
+
variant === 'warning' &&
|
|
59
|
+
'tw-bg-orange tw-text-white active:enabled:tw-bg-orange-700',
|
|
60
|
+
variant === 'danger' &&
|
|
61
|
+
'tw-bg-red tw-text-white active:enabled:tw-bg-red-700',
|
|
62
|
+
variant === 'link-button' &&
|
|
63
|
+
'tw-border tw-border-nordicBlue tw-bg-white tw-text-nordicBlue active:enabled:tw-bg-gray-50',
|
|
64
|
+
className
|
|
65
|
+
)}`}
|
|
66
|
+
disabled={disabled}
|
|
67
|
+
onClick={onClick}
|
|
68
|
+
title={title}
|
|
69
|
+
>
|
|
70
|
+
{children}
|
|
71
|
+
</button>
|
|
73
72
|
);
|
|
74
73
|
|
|
75
74
|
export default Button;
|
package/src/Dialog/Dialog.tsx
CHANGED
|
@@ -82,7 +82,9 @@ Dialog.Body = ({ children }: { children: ReactNode }) => (
|
|
|
82
82
|
);
|
|
83
83
|
|
|
84
84
|
Dialog.Footer = ({ children }: { children: ReactNode }) => (
|
|
85
|
-
<Modal.Footer>
|
|
85
|
+
<Modal.Footer className="tw-preflight tw-flex tw-flex-row-reverse tw-justify-start tw-gap-1 tw-border-none tw-p-4">
|
|
86
|
+
{children}
|
|
87
|
+
</Modal.Footer>
|
|
86
88
|
);
|
|
87
89
|
|
|
88
90
|
export interface DialogButtonProps {
|
package/src/Dialog/dialog.scss
CHANGED
|
@@ -9,8 +9,8 @@ import React, { useMemo, useState } from 'react';
|
|
|
9
9
|
import Button from '../Button/Button';
|
|
10
10
|
import Dropdown, { DropdownItem } from '../Dropdown/Dropdown';
|
|
11
11
|
import logger from '../logging';
|
|
12
|
-
import
|
|
13
|
-
import
|
|
12
|
+
import describeError from '../logging/describeError';
|
|
13
|
+
import sendFeedback from './sendFeedback';
|
|
14
14
|
|
|
15
15
|
export interface FeedbackPaneProps {
|
|
16
16
|
categories?: string[];
|
|
@@ -136,49 +136,18 @@ export default ({ categories }: FeedbackPaneProps) => {
|
|
|
136
136
|
);
|
|
137
137
|
};
|
|
138
138
|
|
|
139
|
-
const formURL =
|
|
140
|
-
isDevelopment === true
|
|
141
|
-
? 'https://formkeep.com/f/87deb409a565'
|
|
142
|
-
: 'https://formkeep.com/f/36b394b92851';
|
|
143
|
-
|
|
144
139
|
const handleFormData = async (
|
|
145
140
|
feedback: string,
|
|
146
141
|
setResponse: (response: boolean) => void,
|
|
147
142
|
category?: string
|
|
148
143
|
) => {
|
|
149
|
-
const data: Record<string, unknown> = {
|
|
150
|
-
name: getAppName(),
|
|
151
|
-
feedback,
|
|
152
|
-
platform: process.platform,
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
if (category && category !== 'Select a category') {
|
|
156
|
-
data.category = category;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
144
|
try {
|
|
160
|
-
|
|
161
|
-
method: 'POST',
|
|
162
|
-
body: JSON.stringify(data),
|
|
163
|
-
headers: {
|
|
164
|
-
'Content-Type': 'application/json',
|
|
165
|
-
enctype: 'multipart/form-data',
|
|
166
|
-
},
|
|
167
|
-
});
|
|
145
|
+
await sendFeedback(feedback, category);
|
|
168
146
|
|
|
169
|
-
|
|
170
|
-
setResponse(true);
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
logger.error(
|
|
175
|
-
`FeedbackForm: Server responded with status code ${response.status}`
|
|
176
|
-
);
|
|
147
|
+
setResponse(true);
|
|
177
148
|
} catch (error: unknown) {
|
|
178
149
|
logger.error(
|
|
179
|
-
`FeedbackForm: Could not send feedback. ${
|
|
150
|
+
`FeedbackForm: Could not send feedback. ${describeError(error)}`
|
|
180
151
|
);
|
|
181
152
|
}
|
|
182
153
|
};
|
|
183
|
-
|
|
184
|
-
const getAppName = () => packageJson().name;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023 Nordic Semiconductor ASA
|
|
3
|
+
*
|
|
4
|
+
* SPDX-License-Identifier: LicenseRef-Nordic-4-Clause
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { isDevelopment } from '../utils/environment';
|
|
8
|
+
import packageJson from '../utils/packageJson';
|
|
9
|
+
|
|
10
|
+
const formURL =
|
|
11
|
+
isDevelopment === true
|
|
12
|
+
? 'https://formkeep.com/f/87deb409a565'
|
|
13
|
+
: 'https://formkeep.com/f/36b394b92851';
|
|
14
|
+
|
|
15
|
+
const getAppName = () => packageJson().name;
|
|
16
|
+
|
|
17
|
+
export default async (feedback: string, category?: string) => {
|
|
18
|
+
const data: Record<string, unknown> = {
|
|
19
|
+
name: getAppName(),
|
|
20
|
+
feedback,
|
|
21
|
+
platform: process.platform,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
if (category && category !== 'Select a category') {
|
|
25
|
+
data.category = category;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const response = await fetch(formURL, {
|
|
29
|
+
method: 'POST',
|
|
30
|
+
body: JSON.stringify(data),
|
|
31
|
+
headers: {
|
|
32
|
+
'Content-Type': 'application/json',
|
|
33
|
+
enctype: 'multipart/form-data',
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (response.ok) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
throw new Error(`Server responded with status code ${response.status}`);
|
|
42
|
+
};
|