@nordicsemiconductor/pc-nrfconnect-shared 122.0.0 → 124.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.
Files changed (96) hide show
  1. package/Changelog.md +58 -0
  2. package/coverage/cobertura-coverage.xml +1343 -978
  3. package/ipc/launcherConfig.ts +25 -0
  4. package/ipc/openWindow.ts +17 -1
  5. package/ipc/schema/packageJson.ts +26 -22
  6. package/main/index.ts +11 -5
  7. package/mocks/packageJsonMock.ts +1 -1
  8. package/nrfutil/device/batch.ts +1 -7
  9. package/nrfutil/device/common.ts +15 -8
  10. package/nrfutil/device/list.ts +1 -1
  11. package/nrfutil/moduleVersion.ts +2 -2
  12. package/nrfutil/sandbox.ts +52 -32
  13. package/nrfutil/sandboxTypes.ts +1 -1
  14. package/package.json +3 -2
  15. package/scripts/check-app-properties.ts +6 -6
  16. package/scripts/esbuild.ts +3 -3
  17. package/scripts/nordic-publish.js +23 -23
  18. package/scripts/nordic-publish.ts +3 -3
  19. package/scripts/release-shared.ts +2 -2
  20. package/src/About/ApplicationCard.tsx +3 -5
  21. package/src/App/App.test.tsx +7 -0
  22. package/src/App/App.tsx +14 -27
  23. package/src/Device/DeviceSelector/DeviceList/MoreDeviceInfo.tsx +1 -1
  24. package/src/Device/DeviceSelector/DeviceSelector.tsx +27 -9
  25. package/src/Device/deviceLister.ts +66 -23
  26. package/src/Device/deviceSetup.ts +4 -4
  27. package/src/Device/deviceSlice.ts +12 -3
  28. package/src/ErrorBoundary/ErrorBoundary.tsx +7 -13
  29. package/src/Feedback/sendFeedback.ts +2 -4
  30. package/src/Link/FileLink.tsx +4 -1
  31. package/src/index.ts +3 -0
  32. package/src/logging/sendInitialLogMessages.ts +2 -4
  33. package/src/utils/appDetails.ts +22 -0
  34. package/src/utils/appDirs.ts +4 -4
  35. package/src/utils/launcherConfig.ts +17 -0
  36. package/src/utils/packageJson.ts +52 -10
  37. package/src/utils/persistentStore.ts +21 -14
  38. package/src/utils/systemReport.ts +1 -3
  39. package/src/utils/usageData.ts +75 -167
  40. package/src/utils/usageDataCommon.ts +59 -0
  41. package/src/utils/usageDataMain.ts +117 -0
  42. package/src/utils/usageDataRenderer.ts +126 -0
  43. package/src/utils/useHotKey.ts +2 -2
  44. package/typings/generated/ipc/launcherConfig.d.ts +14 -0
  45. package/typings/generated/ipc/launcherConfig.d.ts.map +1 -0
  46. package/typings/generated/ipc/openWindow.d.ts +10 -4
  47. package/typings/generated/ipc/openWindow.d.ts.map +1 -1
  48. package/typings/generated/ipc/schema/packageJson.d.ts +34 -52
  49. package/typings/generated/ipc/schema/packageJson.d.ts.map +1 -1
  50. package/typings/generated/main/index.d.ts +8 -2
  51. package/typings/generated/main/index.d.ts.map +1 -1
  52. package/typings/generated/nrfutil/device/batch.d.ts.map +1 -1
  53. package/typings/generated/nrfutil/device/common.d.ts +1 -1
  54. package/typings/generated/nrfutil/device/common.d.ts.map +1 -1
  55. package/typings/generated/nrfutil/device/device.d.ts +2 -2
  56. package/typings/generated/nrfutil/device/list.d.ts +1 -1
  57. package/typings/generated/nrfutil/device/list.d.ts.map +1 -1
  58. package/typings/generated/nrfutil/sandbox.d.ts +2 -2
  59. package/typings/generated/nrfutil/sandbox.d.ts.map +1 -1
  60. package/typings/generated/nrfutil/sandboxTypes.d.ts +1 -1
  61. package/typings/generated/nrfutil/sandboxTypes.d.ts.map +1 -1
  62. package/typings/generated/src/About/ApplicationCard.d.ts.map +1 -1
  63. package/typings/generated/src/App/App.d.ts +0 -1
  64. package/typings/generated/src/App/App.d.ts.map +1 -1
  65. package/typings/generated/src/Device/DeviceSelector/DeviceSelector.d.ts.map +1 -1
  66. package/typings/generated/src/Device/deviceLister.d.ts.map +1 -1
  67. package/typings/generated/src/Device/deviceSetup.d.ts +2 -2
  68. package/typings/generated/src/Device/deviceSetup.d.ts.map +1 -1
  69. package/typings/generated/src/Device/deviceSlice.d.ts +5 -1
  70. package/typings/generated/src/Device/deviceSlice.d.ts.map +1 -1
  71. package/typings/generated/src/ErrorBoundary/ErrorBoundary.d.ts.map +1 -1
  72. package/typings/generated/src/Feedback/sendFeedback.d.ts.map +1 -1
  73. package/typings/generated/src/Link/FileLink.d.ts +2 -1
  74. package/typings/generated/src/Link/FileLink.d.ts.map +1 -1
  75. package/typings/generated/src/index.d.ts +2 -0
  76. package/typings/generated/src/index.d.ts.map +1 -1
  77. package/typings/generated/src/logging/sendInitialLogMessages.d.ts.map +1 -1
  78. package/typings/generated/src/utils/appDetails.d.ts +4 -0
  79. package/typings/generated/src/utils/appDetails.d.ts.map +1 -0
  80. package/typings/generated/src/utils/appDirs.d.ts +1 -1
  81. package/typings/generated/src/utils/appDirs.d.ts.map +1 -1
  82. package/typings/generated/src/utils/launcherConfig.d.ts +4 -0
  83. package/typings/generated/src/utils/launcherConfig.d.ts.map +1 -0
  84. package/typings/generated/src/utils/packageJson.d.ts +30 -7
  85. package/typings/generated/src/utils/packageJson.d.ts.map +1 -1
  86. package/typings/generated/src/utils/persistentStore.d.ts +3 -2
  87. package/typings/generated/src/utils/persistentStore.d.ts.map +1 -1
  88. package/typings/generated/src/utils/systemReport.d.ts.map +1 -1
  89. package/typings/generated/src/utils/usageData.d.ts +8 -65
  90. package/typings/generated/src/utils/usageData.d.ts.map +1 -1
  91. package/typings/generated/src/utils/usageDataCommon.d.ts +27 -0
  92. package/typings/generated/src/utils/usageDataCommon.d.ts.map +1 -0
  93. package/typings/generated/src/utils/usageDataMain.d.ts +10 -0
  94. package/typings/generated/src/utils/usageDataMain.d.ts.map +1 -0
  95. package/typings/generated/src/utils/usageDataRenderer.d.ts +10 -0
  96. package/typings/generated/src/utils/usageDataRenderer.d.ts.map +1 -0
@@ -14,7 +14,7 @@ import semver from 'semver';
14
14
  import calculateShasum from 'shasum';
15
15
 
16
16
  import { AppInfo, SourceJson } from '../ipc/MetaFiles';
17
- import { PackageJson } from '../ipc/schema/packageJson';
17
+ import { PackageJsonApp } from '../ipc/schema/packageJson';
18
18
  import checkAppProperties from './check-app-properties';
19
19
 
20
20
  interface LegacyAppInfo {
@@ -41,7 +41,7 @@ interface App {
41
41
  releaseNotesFilename: string;
42
42
  iconFilename: string;
43
43
  isOfficial: boolean;
44
- packageJson: PackageJson;
44
+ packageJson: PackageJsonApp;
45
45
  }
46
46
 
47
47
  const client = new FtpClient();
@@ -113,7 +113,7 @@ const parseOptions = (): Options => {
113
113
  };
114
114
  };
115
115
 
116
- const readPackageJson = (): PackageJson =>
116
+ const readPackageJson = (): PackageJsonApp =>
117
117
  JSON.parse(fs.readFileSync('./package.json', 'utf-8'));
118
118
 
119
119
  const packPackage = () => {
@@ -25,7 +25,7 @@
25
25
  */
26
26
  import { execSync, spawnSync } from 'node:child_process';
27
27
 
28
- import { PackageJson } from '../ipc/schema/packageJson';
28
+ import { PackageJsonApp } from '../ipc/schema/packageJson';
29
29
 
30
30
  const logError = (message: string) => {
31
31
  console.error(message);
@@ -88,7 +88,7 @@ export const packageJsonIsCorrect = (
88
88
  }
89
89
  ) => {
90
90
  const expectedVersionString = `${expectedVersionNumber}.0.0`;
91
- const actualVersionString = parseJson<PackageJson>(packageJson).version;
91
+ const actualVersionString = parseJson<PackageJsonApp>(packageJson).version;
92
92
 
93
93
  return equality(
94
94
  expectedVersionString,
@@ -6,12 +6,10 @@
6
6
 
7
7
  import React, { useEffect, useState } from 'react';
8
8
 
9
- import {
10
- AppDetailsFromLauncher,
11
- inMain as appDetails,
12
- } from '../../ipc/appDetails';
9
+ import { AppDetailsFromLauncher } from '../../ipc/appDetails';
13
10
  import Card from '../Card/Card';
14
11
  import FactoryResetButton from '../FactoryReset/FactoryResetButton';
12
+ import appDetails from '../utils/appDetails';
15
13
  import AboutButton from './AboutButton';
16
14
  import Section from './Section';
17
15
  import ShortcutButton from './ShortcutButton';
@@ -20,7 +18,7 @@ export default () => {
20
18
  const [appInfo, setAppInfo] = useState<AppDetailsFromLauncher>();
21
19
 
22
20
  useEffect(() => {
23
- appDetails.getAppDetails().then(setAppInfo);
21
+ appDetails().then(setAppInfo);
24
22
  }, [setAppInfo]);
25
23
 
26
24
  if (appInfo == null) return null;
@@ -7,6 +7,7 @@
7
7
  import React from 'react';
8
8
  import { screen } from '@testing-library/react';
9
9
 
10
+ import packageJsonFromShared from '../../package.json';
10
11
  import render from '../../test/testrenderer';
11
12
  import App, { Pane } from './App';
12
13
 
@@ -17,6 +18,12 @@ jest.mock('../Log/LogViewer', () => ({
17
18
 
18
19
  jest.mock('../logging/index.ts', () => ({
19
20
  initialise: () => {},
21
+ debug: () => {},
22
+ }));
23
+
24
+ jest.mock('../utils/packageJson', () => ({
25
+ isLauncher: () => false,
26
+ packageJson: () => packageJsonFromShared,
20
27
  }));
21
28
 
22
29
  const renderApp = (panes: Pane[]) => {
package/src/App/App.tsx CHANGED
@@ -30,9 +30,8 @@ import LogViewer from '../Log/LogViewer';
30
30
  import logger from '../logging';
31
31
  import NavBar from '../NavBar/NavBar';
32
32
  import classNames from '../utils/classNames';
33
- import packageJson from '../utils/packageJson';
34
33
  import { getPersistedCurrentPane } from '../utils/persistentStore';
35
- import { init as usageDataInit, setUsageLogger } from '../utils/usageData';
34
+ import usageData from '../utils/usageData';
36
35
  import useHotKey from '../utils/useHotKey';
37
36
  import {
38
37
  currentPane as currentPaneSelector,
@@ -49,19 +48,6 @@ import './app.scss';
49
48
  import './shared.scss';
50
49
  import './tailwind.css';
51
50
 
52
- let usageDataAlreadyInitialised = false;
53
- const initialiseUsageData = () => {
54
- if (!usageDataAlreadyInitialised) {
55
- usageDataAlreadyInitialised = true;
56
- try {
57
- usageDataInit(packageJson());
58
- } catch (error) {
59
- // No need to display the error message for the user
60
- console.log(error);
61
- }
62
- }
63
- };
64
-
65
51
  export interface PaneProps {
66
52
  active: boolean;
67
53
  }
@@ -77,7 +63,6 @@ interface ConnectedAppProps {
77
63
  panes: Pane[];
78
64
  sidePanel?: ReactNode;
79
65
  showLogByDefault?: boolean;
80
- reportUsageData?: boolean;
81
66
  documentation?: ReactNode[];
82
67
  feedback?: boolean | FeedbackPaneProps;
83
68
  children?: ReactNode;
@@ -89,25 +74,25 @@ const ConnectedApp: FC<ConnectedAppProps> = ({
89
74
  panes,
90
75
  sidePanel,
91
76
  showLogByDefault = true,
92
- reportUsageData = false,
93
77
  documentation,
94
78
  feedback,
95
79
  children,
96
80
  autoReselectByDefault = false,
97
81
  }) => {
98
- const initialisedLogger = useRef(false);
82
+ const initApp = useRef(false);
99
83
 
100
- if (!initialisedLogger.current) {
84
+ if (!initApp.current) {
101
85
  logger.initialise();
102
86
  setNrfutilLogger(logger);
103
- setUsageLogger(logger);
104
- initialisedLogger.current = true;
87
+ usageData.setLogger(logger);
88
+ initApp.current = true;
105
89
  }
106
90
 
107
91
  usePersistedPane();
108
92
  const isLogVisible = useSelector(isLogVisibleSelector);
109
93
  const currentPane = useSelector(currentPaneSelector);
110
94
  const allPanes = useAllPanes(panes, documentation, feedback);
95
+ const paneName = useRef(allPanes.map(({ name }) => name));
111
96
  const dispatch = useDispatch();
112
97
 
113
98
  useHotKey({
@@ -117,6 +102,14 @@ const ConnectedApp: FC<ConnectedAppProps> = ({
117
102
  action: openWindow.openLauncher,
118
103
  });
119
104
 
105
+ useEffect(() => {
106
+ paneName.current = allPanes.map(({ name }) => name);
107
+ }, [allPanes]);
108
+
109
+ useEffect(() => {
110
+ usageData.sendPageView(paneName.current[currentPane]);
111
+ }, [currentPane]);
112
+
120
113
  useEffect(() => {
121
114
  dispatch(setAutoReselect(autoReselectByDefault));
122
115
  }, [dispatch, autoReselectByDefault]);
@@ -134,12 +127,6 @@ const ConnectedApp: FC<ConnectedAppProps> = ({
134
127
  const isSidePanelVisible =
135
128
  useSelector(isSidePanelVisibleSelector) && currentSidePanel;
136
129
 
137
- useEffect(() => {
138
- if (reportUsageData) {
139
- initialiseUsageData();
140
- }
141
- }, [reportUsageData]);
142
-
143
130
  return (
144
131
  <div className="core19-app">
145
132
  <NavBar deviceSelect={deviceSelect} />
@@ -44,7 +44,7 @@ const MaybeDeviceName = ({ device }: { device: Device }) => {
44
44
  const Serialports = ({ ports }: { ports: SerialPort[] }) =>
45
45
  ports.length > 0 ? (
46
46
  <Row>
47
- <ul className="tw-text-center tw-underline">
47
+ <ul className="tw-underline">
48
48
  {ports.map(port => (
49
49
  <li key={port.path}>{port.comName}</li>
50
50
  ))}
@@ -10,6 +10,8 @@ import { useDispatch, useSelector } from 'react-redux';
10
10
  import { NrfutilDeviceLib } from '../../../nrfutil';
11
11
  import { DeviceTraits } from '../../../nrfutil/device/common';
12
12
  import logger from '../../logging';
13
+ import usageData from '../../utils/usageData';
14
+ import { simplifyDeviceForLogging } from '../../utils/usageDataCommon';
13
15
  import useHotKey from '../../utils/useHotKey';
14
16
  import {
15
17
  clearWaitForDevice,
@@ -23,6 +25,7 @@ import {
23
25
  deselectDevice,
24
26
  Device,
25
27
  deviceIsSelected as deviceIsSelectedSelector,
28
+ isDeviceWithSerialNumber,
26
29
  selectDevice,
27
30
  selectedDevice,
28
31
  setSelectedDeviceInfo,
@@ -61,12 +64,22 @@ export default ({
61
64
  const waitingToAutoReconnect = useSelector(getWaitingToAutoReselect);
62
65
  const showSelectedDevice = deviceIsSelected || waitingToAutoReconnect;
63
66
 
64
- const doDeselectDevice = useCallback(() => {
65
- dispatch(clearWaitForDevice());
66
- dispatch(setAutoSelectDevice(undefined));
67
- onDeviceDeselected();
68
- dispatch(deselectDevice());
69
- }, [dispatch, onDeviceDeselected]);
67
+ const doDeselectDevice = useCallback(
68
+ (device?: Device) => {
69
+ if (device) {
70
+ usageData.sendUsageData(
71
+ 'device deselected ',
72
+ simplifyDeviceForLogging(device)
73
+ );
74
+ }
75
+
76
+ dispatch(clearWaitForDevice());
77
+ dispatch(setAutoSelectDevice(undefined));
78
+ onDeviceDeselected();
79
+ dispatch(deselectDevice());
80
+ },
81
+ [dispatch, onDeviceDeselected]
82
+ );
70
83
 
71
84
  const abortController = useRef<AbortController>();
72
85
 
@@ -92,8 +105,13 @@ export default ({
92
105
  abortController.current = undefined;
93
106
  setSelectedDeviceInfo(deviceInfo);
94
107
 
108
+ usageData.sendUsageData('device selected', {
109
+ device: simplifyDeviceForLogging(device),
110
+ deviceInfo,
111
+ });
112
+
95
113
  if (deviceSetupConfig) {
96
- if (device.serialNumber) {
114
+ if (isDeviceWithSerialNumber(device)) {
97
115
  dispatch(
98
116
  setupDevice(
99
117
  device,
@@ -158,7 +176,7 @@ export default ({
158
176
  <div className="core19-device-selector">
159
177
  {showSelectedDevice ? (
160
178
  <SelectedDevice
161
- doDeselectDevice={doDeselectDevice}
179
+ doDeselectDevice={() => doDeselectDevice(currentDevice)}
162
180
  toggleDeviceListVisible={toggleDeviceListVisible}
163
181
  />
164
182
  ) : (
@@ -176,7 +194,7 @@ export default ({
176
194
  }
177
195
 
178
196
  if (deviceIsSelected) {
179
- doDeselectDevice();
197
+ doDeselectDevice(currentDevice);
180
198
  }
181
199
 
182
200
  doSelectDevice(device, autoReselected);
@@ -8,6 +8,8 @@ import { DeviceTraits, NrfutilDevice } from '../../nrfutil/device/common';
8
8
  import NrfutilDeviceLib from '../../nrfutil/device/device';
9
9
  import logger from '../logging';
10
10
  import type { AppThunk, RootState } from '../store';
11
+ import usageData from '../utils/usageData';
12
+ import { simplifyDeviceForLogging } from '../utils/usageDataCommon';
11
13
  import {
12
14
  clearWaitForDevice,
13
15
  clearWaitForDeviceTimeout,
@@ -22,7 +24,6 @@ import { closeDeviceSetupDialog } from './deviceSetupSlice';
22
24
  import {
23
25
  addDevice,
24
26
  Device,
25
- getDevice,
26
27
  removeDevice,
27
28
  selectDevice,
28
29
  setSelectedDeviceInfo,
@@ -164,10 +165,16 @@ export const startWatchingDevices =
164
165
  (dispatch, getState) => {
165
166
  const onDeviceArrived = async (device: NrfutilDevice) => {
166
167
  if (hasValidDeviceTraits(device.traits, deviceListing)) {
168
+ usageData.sendUsageData(
169
+ 'device connected',
170
+ simplifyDeviceForLogging(device)
171
+ );
167
172
  if (
168
- device.serialNumber &&
169
173
  !getState().device.devices.find(
170
- d => d.serialNumber === device.serialNumber
174
+ d =>
175
+ d.id === device.id ||
176
+ (device.serialNumber && // we want to disregard comparing devices with no sn
177
+ d.serialNumber === device.serialNumber)
171
178
  )
172
179
  ) {
173
180
  onDeviceConnected(device);
@@ -294,7 +301,7 @@ export const startWatchingDevices =
294
301
  const waitForDevice =
295
302
  getState().deviceAutoSelect.waitForDevice;
296
303
  if (
297
- device.serialNumber &&
304
+ device.serialNumber && // we want to disregard comparing devices with no sn
298
305
  device.serialNumber ===
299
306
  getState().deviceAutoSelect.device?.serialNumber
300
307
  ) {
@@ -341,7 +348,10 @@ export const startWatchingDevices =
341
348
  stopWatchingDevices(async () => {
342
349
  const operation = await NrfutilDeviceLib.list(
343
350
  deviceListing,
344
- d => d.forEach(onDeviceArrived),
351
+ d => {
352
+ d.forEach(onDeviceArrived);
353
+ dispatch(autoSelectDeviceCLI(doSelectDevice));
354
+ },
345
355
  error => {
346
356
  logger.error(error);
347
357
  },
@@ -353,30 +363,63 @@ export const startWatchingDevices =
353
363
  callback?.();
354
364
  });
355
365
  };
356
-
357
- if (!autoSelectDeviceCLISerialUsed) {
358
- const autoSelectSN = getAutoSelectDeviceCLISerial();
359
-
360
- if (autoSelectSN !== undefined) {
361
- const autoSelectDevice = getDevice(
362
- getState(),
363
- autoSelectSN
364
- );
365
-
366
- if (autoSelectDevice)
367
- doSelectDevice(autoSelectDevice, true);
368
- }
369
- autoSelectDeviceCLISerialUsed = true;
370
- }
371
366
  });
372
367
  };
373
368
 
374
- const getAutoSelectDeviceCLISerial = () => {
369
+ const getAutoSelectDeviceCLIProperty = (
370
+ property: string,
371
+ findDevice: (value: string) => Device | undefined
372
+ ) => {
375
373
  const { argv } = process;
376
- const serialIndex = argv.findIndex(arg => arg === '--deviceSerial');
377
- return serialIndex > -1 ? argv[serialIndex + 1] : undefined;
374
+ const index = argv.findIndex(arg => arg === property);
375
+ return index > -1
376
+ ? { index, device: findDevice(argv[index + 1]) }
377
+ : undefined;
378
+ };
379
+
380
+ const getAutoSelectDevice = (devices: Device[]) => {
381
+ const serialNumber = getAutoSelectDeviceCLIProperty('--deviceSerial', sn =>
382
+ devices.find(device => device.serialNumber === sn)
383
+ );
384
+ const serialPort = getAutoSelectDeviceCLIProperty('--comPort', portPath =>
385
+ devices.find(device =>
386
+ device.serialPorts?.find(port => port.comName === portPath)
387
+ )
388
+ );
389
+
390
+ if (serialNumber && serialPort) {
391
+ if (serialNumber.index > serialPort.index) {
392
+ return serialPort.device;
393
+ }
394
+
395
+ return serialNumber.device;
396
+ }
397
+
398
+ if (serialNumber) {
399
+ return serialNumber.device;
400
+ }
401
+
402
+ if (serialPort) {
403
+ return serialPort.device;
404
+ }
378
405
  };
379
406
 
407
+ const autoSelectDeviceCLI =
408
+ (
409
+ doSelectDevice: (device: Device, autoReselected: boolean) => void
410
+ ): AppThunk<RootState> =>
411
+ (_, getState) => {
412
+ if (!autoSelectDeviceCLISerialUsed) {
413
+ const autoSelectDevice = getAutoSelectDevice(
414
+ getState().device.devices
415
+ );
416
+
417
+ if (autoSelectDevice) doSelectDevice(autoSelectDevice, true);
418
+
419
+ autoSelectDeviceCLISerialUsed = true;
420
+ }
421
+ };
422
+
380
423
  export const stopWatchingDevices = (callback?: () => void) => {
381
424
  if (stopNrfutilDevice) stopNrfutilDevice(callback);
382
425
  else callback?.();
@@ -13,7 +13,7 @@ import {
13
13
  setDeviceSetupProgress,
14
14
  setDeviceSetupProgressMessage,
15
15
  } from './deviceSetupSlice';
16
- import { Device } from './deviceSlice';
16
+ import { Device, DeviceWithSerialNumber } from './deviceSlice';
17
17
  import { InitPacket } from './initPacket';
18
18
 
19
19
  export interface DfuEntry {
@@ -239,10 +239,10 @@ export const prepareDevice =
239
239
 
240
240
  export const setupDevice =
241
241
  (
242
- device: Device,
242
+ device: DeviceWithSerialNumber,
243
243
  deviceSetupConfig: DeviceSetupConfig,
244
244
  onDeviceIsReady: (device: Device) => void,
245
- doDeselectDevice: () => void,
245
+ doDeselectDevice: (device?: Device) => void,
246
246
  deviceInfo: DeviceInfo | undefined
247
247
  ): AppThunk<RootState> =>
248
248
  (dispatch, getState) =>
@@ -270,7 +270,7 @@ export const setupDevice =
270
270
  `Error while setting up device ${device.serialNumber}`
271
271
  );
272
272
  logger.error(describeError(error));
273
- doDeselectDevice();
273
+ doDeselectDevice(getState().device.selectedDevice);
274
274
  },
275
275
  deviceInfo
276
276
  )
@@ -26,10 +26,19 @@ export interface Device extends NrfutilDevice {
26
26
  persistedSerialPortOptions?: SerialPortOpenOptions<AutoDetectTypes>;
27
27
  }
28
28
 
29
+ export interface DeviceWithSerialNumber extends Device {
30
+ serialNumber: string;
31
+ }
32
+
33
+ export const isDeviceWithSerialNumber = (
34
+ device: Device
35
+ ): device is DeviceWithSerialNumber =>
36
+ !!(device as DeviceWithSerialNumber).serialNumber;
37
+
29
38
  const findDeviceItem = (
30
39
  devices: Device[],
31
40
  id: number,
32
- serialNumber?: string
41
+ serialNumber?: string | null
33
42
  ) => {
34
43
  const index = devices.findIndex(
35
44
  d => d.id === id || d.serialNumber === serialNumber
@@ -42,7 +51,7 @@ const updateDevice = (
42
51
  state: DeviceState,
43
52
  updateToMergeIn: Partial<Device>,
44
53
  id: number,
45
- serialNumber?: string
54
+ serialNumber?: string | null
46
55
  ) => {
47
56
  const device = findDeviceItem(state.devices, id, serialNumber).device;
48
57
  if (device) {
@@ -147,7 +156,7 @@ const slice = createSlice({
147
156
  );
148
157
 
149
158
  if (
150
- selectedDevice.serialNumber &&
159
+ selectedDevice.serialNumber && // we want to disregard comparing devices with no sn
151
160
  vComIndex !== undefined &&
152
161
  vComIndex !== -1
153
162
  ) {
@@ -13,27 +13,20 @@ import FactoryResetButton from '../FactoryReset/FactoryResetButton';
13
13
  import { CollapsibleGroup } from '../SidePanel/Group';
14
14
  import Spinner from '../Spinner/Spinner';
15
15
  import { openUrl } from '../utils/open';
16
- import packageJson from '../utils/packageJson';
16
+ import { packageJson } from '../utils/packageJson';
17
17
  import { getAppSpecificStore as store } from '../utils/persistentStore';
18
18
  import { generateSystemReport } from '../utils/systemReport';
19
- import {
20
- init as initGA,
21
- isEnabled,
22
- isInitialized as isGAInitialized,
23
- sendErrorReport,
24
- } from '../utils/usageData';
19
+ import usageData from '../utils/usageData';
25
20
  import bugIcon from './bug.svg';
26
21
 
27
22
  import './error-boundary.scss';
28
23
 
29
24
  const sendGAEvent = (error: string) => {
30
- if (!isEnabled()) {
25
+ if (!usageData.isEnabled()) {
31
26
  return;
32
27
  }
33
- if (!isGAInitialized()) {
34
- initGA(packageJson());
35
- }
36
- sendErrorReport(error);
28
+
29
+ usageData.sendErrorReport(error);
37
30
  };
38
31
 
39
32
  interface Props {
@@ -97,7 +90,8 @@ class ErrorBoundary extends React.Component<
97
90
  return children;
98
91
  }
99
92
 
100
- const appDisplayName = appName || packageJson().displayName;
93
+ const appDisplayName =
94
+ appName || packageJson().displayName || packageJson().name;
101
95
 
102
96
  return (
103
97
  <div className="error-boundary__container">
@@ -5,18 +5,16 @@
5
5
  */
6
6
 
7
7
  import { isDevelopment } from '../utils/environment';
8
- import packageJson from '../utils/packageJson';
8
+ import { packageJson } from '../utils/packageJson';
9
9
 
10
10
  const formURL =
11
11
  isDevelopment === true
12
12
  ? 'https://formkeep.com/f/87deb409a565'
13
13
  : 'https://formkeep.com/f/36b394b92851';
14
14
 
15
- const getAppName = () => packageJson().name;
16
-
17
15
  export default async (feedback: string, category?: string) => {
18
16
  const data: Record<string, unknown> = {
19
- name: getAppName(),
17
+ name: packageJson().name,
20
18
  feedback,
21
19
  platform: process.platform,
22
20
  };
@@ -12,15 +12,18 @@ import { openFile } from '../utils/open';
12
12
  export default ({
13
13
  label,
14
14
  fileLocation,
15
+ className,
15
16
  }: {
16
17
  label: string;
17
18
  fileLocation: string;
19
+ className: string;
18
20
  }) => (
19
21
  <button
20
22
  type="button"
21
23
  onClick={() => openFile(fileLocation)}
22
24
  className={classNames(
23
- 'tw-preflight tw-text-nordicBlue hover:tw-underline'
25
+ 'tw-preflight tw-overflow-hidden tw-text-ellipsis tw-text-nordicBlue hover:tw-underline',
26
+ className
24
27
  )}
25
28
  >
26
29
  {label}
package/src/index.ts CHANGED
@@ -77,6 +77,7 @@ export { openUrl } from './utils/open';
77
77
  export { default as systemReport } from './utils/systemReport';
78
78
 
79
79
  export { default as usageData } from './utils/usageData';
80
+ export { type Metadata as UsageDataMetadata } from './utils/usageDataCommon';
80
81
  export { default as classNames } from './utils/classNames';
81
82
  export { truncateMiddle } from './utils/truncateMiddle';
82
83
 
@@ -97,6 +98,8 @@ export {
97
98
  persistApiKey,
98
99
  } from './utils/persistentStore';
99
100
 
101
+ export { default as launcherConfig } from './utils/launcherConfig';
102
+
100
103
  export { jprogDeviceSetup } from './Device/jprogOperations';
101
104
 
102
105
  export { sdfuDeviceSetup } from './Device/sdfuOperations';
@@ -4,12 +4,12 @@
4
4
  * SPDX-License-Identifier: LicenseRef-Nordic-4-Clause
5
5
  */
6
6
 
7
- import { inMain as appDetails } from '../../ipc/appDetails';
8
7
  import NrfutilDeviceLib from '../../nrfutil/device/device';
9
8
  import {
10
9
  getExpectedVersion,
11
10
  resolveModuleVersion,
12
11
  } from '../../nrfutil/moduleVersion';
12
+ import appDetails from '../utils/appDetails';
13
13
  import { getAppDataDir } from '../utils/appDirs';
14
14
  import logLibVersions from '../utils/logLibVersions';
15
15
  import udevInstalled from '../utils/udevInstalled';
@@ -23,8 +23,6 @@ export default async () => {
23
23
  logLibVersions();
24
24
  logger.debug(`Application data folder: ${getAppDataDir()}`);
25
25
 
26
- const details = await appDetails.getAppDetails();
27
-
28
26
  const {
29
27
  name,
30
28
  currentVersion,
@@ -35,7 +33,7 @@ export default async () => {
35
33
  homeDir,
36
34
  tmpDir,
37
35
  source,
38
- } = details;
36
+ } = await appDetails();
39
37
 
40
38
  logger.debug(`App ${name} v${currentVersion} (${source})`);
41
39
  logger.debug(`App path: ${installed.path}`);
@@ -0,0 +1,22 @@
1
+ /*
2
+ * Copyright (c) 2023 Nordic Semiconductor ASA
3
+ *
4
+ * SPDX-License-Identifier: LicenseRef-Nordic-4-Clause
5
+ */
6
+
7
+ import { type AppDetailsFromLauncher, inMain } from '../../ipc/appDetails';
8
+ import { isLauncher } from './packageJson';
9
+
10
+ let cached: AppDetailsFromLauncher;
11
+
12
+ export default async () => {
13
+ if (isLauncher()) {
14
+ throw new Error('Must not be called by the launcher.');
15
+ }
16
+
17
+ if (cached == null) {
18
+ cached = await inMain.getAppDetails();
19
+ }
20
+
21
+ return cached;
22
+ };
@@ -4,12 +4,12 @@
4
4
  * SPDX-License-Identifier: LicenseRef-Nordic-4-Clause
5
5
  */
6
6
 
7
- import { getGlobal } from '@electron/remote';
8
7
  import path from 'path';
9
8
 
10
- import packageJson from './packageJson';
9
+ import launcherConfig from './launcherConfig';
10
+ import { packageJsonApp } from './packageJson';
11
11
 
12
- const getUserDataDir = () => getGlobal('userDataDir');
12
+ const getUserDataDir = () => launcherConfig().userDataDir;
13
13
 
14
14
  /**
15
15
  * Get the filesystem path of the currently loaded app.
@@ -17,7 +17,7 @@ const getUserDataDir = () => getGlobal('userDataDir');
17
17
  * @returns {string|undefined} Absolute path of current app.
18
18
  */
19
19
  const getAppDir = () => {
20
- const html = packageJson().nrfConnectForDesktop.html;
20
+ const html = packageJsonApp().nrfConnectForDesktop.html;
21
21
  const dir = path.parse(html).dir;
22
22
  return path.parse(__filename).dir.replace(dir, '');
23
23
  };