Skip to content

Commit 447f42b

Browse files
author
Fatme
authored
Merge pull request #4083 from NativeScript/fatme/preview-api
feat(preview): add public API for deviceFound and deviceLost for preview devices
2 parents a4943ca + 8a5f64f commit 447f42b

File tree

8 files changed

+186
-20
lines changed

8 files changed

+186
-20
lines changed

lib/bootstrap.ts

+1
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ $injector.require("usbLiveSyncService", "./services/livesync/livesync-service");
134134
$injector.require("previewAppLiveSyncService", "./services/livesync/playground/preview-app-livesync-service");
135135
$injector.require("previewAppPluginsService", "./services/livesync/playground/preview-app-plugins-service");
136136
$injector.require("previewSdkService", "./services/livesync/playground/preview-sdk-service");
137+
$injector.requirePublicClass("previewDevicesService", "./services/livesync/playground/devices/preview-devices-service");
137138
$injector.require("playgroundQrCodeGenerator", "./services/livesync/playground/qr-code-generator");
138139
$injector.requirePublic("sysInfo", "./sys-info");
139140

lib/definitions/preview-app-livesync.d.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ declare global {
1212

1313
interface IPreviewSdkService extends EventEmitter {
1414
getQrCodeUrl(options: IHasUseHotModuleReloadOption): string;
15-
connectedDevices: Device[];
1615
initialize(getInitialFiles: (device: Device) => Promise<FilesPayload>): void;
1716
applyChanges(filesPayload: FilesPayload): Promise<void>;
1817
stop(): void;
@@ -34,4 +33,11 @@ declare global {
3433
*/
3534
link: boolean;
3635
}
36+
37+
interface IPreviewDevicesService extends EventEmitter {
38+
getConnectedDevices(): Device[];
39+
updateConnectedDevices(devices: Device[]): void;
40+
getDeviceById(id: string): Device;
41+
getDevicesForPlatform(platform: string): Device[];
42+
}
3743
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Device } from "nativescript-preview-sdk";
2+
import { EventEmitter } from "events";
3+
import { DeviceDiscoveryEventNames } from "../../../../common/constants";
4+
5+
export class PreviewDevicesService extends EventEmitter implements IPreviewDevicesService {
6+
private connectedDevices: Device[] = [];
7+
8+
public getConnectedDevices(): Device[] {
9+
return this.connectedDevices;
10+
}
11+
12+
public updateConnectedDevices(devices: Device[]): void {
13+
_(devices)
14+
.reject(d => _.find(this.connectedDevices, device => d.id === device.id))
15+
.each(device => this.raiseDeviceFound(device));
16+
17+
_(this.connectedDevices)
18+
.reject(d => _.find(devices, device => d.id === device.id))
19+
.each(device => this.raiseDeviceLost(device));
20+
}
21+
22+
public getDeviceById(id: string): Device {
23+
return _.find(this.connectedDevices, { id });
24+
}
25+
26+
public getDevicesForPlatform(platform: string): Device[] {
27+
return _.filter(this.connectedDevices, { platform: platform.toLowerCase() });
28+
}
29+
30+
private raiseDeviceFound(device: Device) {
31+
this.emit(DeviceDiscoveryEventNames.DEVICE_FOUND, device);
32+
this.connectedDevices.push(device);
33+
}
34+
35+
private raiseDeviceLost(device: Device) {
36+
this.emit(DeviceDiscoveryEventNames.DEVICE_LOST, device);
37+
_.remove(this.connectedDevices, d => d.id === device.id);
38+
}
39+
}
40+
$injector.register("previewDevicesService", PreviewDevicesService);

lib/services/livesync/playground/preview-app-livesync-service.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService {
2828
private $projectDataService: IProjectDataService,
2929
private $previewSdkService: IPreviewSdkService,
3030
private $previewAppPluginsService: IPreviewAppPluginsService,
31+
private $previewDevicesService: IPreviewDevicesService,
3132
private $projectFilesManager: IProjectFilesManager,
3233
private $hmrStatusService: IHmrStatusService,
3334
private $projectFilesProvider: IProjectFilesProvider) { }
@@ -55,15 +56,15 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService {
5556
public async syncFiles(data: IPreviewAppLiveSyncData, filesToSync: string[], filesToRemove: string[]): Promise<void> {
5657
this.showWarningsForNativeFiles(filesToSync);
5758

58-
for (const device of this.$previewSdkService.connectedDevices) {
59+
const connectedDevices = this.$previewDevicesService.getConnectedDevices();
60+
for (const device of connectedDevices) {
5961
await this.$previewAppPluginsService.comparePluginsOnDevice(data, device);
6062
}
6163

62-
const platforms = _(this.$previewSdkService.connectedDevices)
64+
const platforms = _(connectedDevices)
6365
.map(device => device.platform)
6466
.uniq()
6567
.value();
66-
6768
for (const platform of platforms) {
6869
await this.syncFilesForPlatformSafe(data, platform, { filesToSync, filesToRemove, useHotModuleReload: data.appFilesUpdaterOptions.useHotModuleReload });
6970
}
@@ -112,7 +113,7 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService {
112113
await promise;
113114

114115
if (data.appFilesUpdaterOptions.useHotModuleReload && platformHmrData.hash) {
115-
const devices = _.filter(this.$previewSdkService.connectedDevices, { platform: platform.toLowerCase() });
116+
const devices = this.$previewDevicesService.getDevicesForPlatform(platform);
116117

117118
await Promise.all(_.map(devices, async (previewDevice: Device) => {
118119
const status = await this.$hmrStatusService.getHmrStatus(previewDevice.id, platformHmrData.hash);

lib/services/livesync/playground/preview-sdk-service.ts

+9-13
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
import { MessagingService, Config, Device, DeviceConnectedMessage, SdkCallbacks, ConnectedDevices, FilesPayload } from "nativescript-preview-sdk";
22
import { PubnubKeys } from "./preview-app-constants";
3-
import { DEVICE_LOG_EVENT_NAME } from "../../../common/constants";
43
import { EventEmitter } from "events";
4+
import { DEVICE_LOG_EVENT_NAME } from "../../../common/constants";
55
const pako = require("pako");
66

77
export class PreviewSdkService extends EventEmitter implements IPreviewSdkService {
88
private static MAX_FILES_UPLOAD_BYTE_LENGTH = 15 * 1024 * 1024; // In MBs
99
private messagingService: MessagingService = null;
1010
private instanceId: string = null;
11-
public connectedDevices: Device[] = [];
1211

13-
constructor(private $logger: ILogger,
12+
constructor(private $config: IConfiguration,
1413
private $httpClient: Server.IHttpClient,
15-
private $config: IConfiguration) {
14+
private $logger: ILogger,
15+
private $previewDevicesService: IPreviewDevicesService) {
1616
super();
1717
}
1818

@@ -60,9 +60,8 @@ export class PreviewSdkService extends EventEmitter implements IPreviewSdkServic
6060
onLogSdkMessage: (log: string) => {
6161
this.$logger.trace("Received onLogSdkMessage message: ", log);
6262
},
63-
onConnectedDevicesChange: (connectedDevices: ConnectedDevices) => ({ }),
6463
onLogMessage: (log: string, deviceName: string, deviceId: string) => {
65-
const device = _.find(this.connectedDevices, { id: deviceId});
64+
const device = this.$previewDevicesService.getDeviceById(deviceId);
6665
this.emit(DEVICE_LOG_EVENT_NAME, log, deviceId, device ? device.platform : "");
6766
this.$logger.info(`LOG from device ${deviceName}: ${log}`);
6867
},
@@ -72,13 +71,10 @@ export class PreviewSdkService extends EventEmitter implements IPreviewSdkServic
7271
onUncaughtErrorMessage: () => {
7372
this.$logger.warn("The Preview app has terminated unexpectedly. Please run it again to get a detailed crash report.");
7473
},
75-
onDeviceConnectedMessage: (deviceConnectedMessage: DeviceConnectedMessage) => ({ }),
76-
onDeviceConnected: (device: Device) => {
77-
if (!_.find(this.connectedDevices, {id: device.id})) {
78-
this.connectedDevices.push(device);
79-
}
80-
},
81-
onDevicesPresence: (devices: Device[]) => ({ }),
74+
onConnectedDevicesChange: (connectedDevices: ConnectedDevices) => ({}),
75+
onDeviceConnectedMessage: (deviceConnectedMessage: DeviceConnectedMessage) => ({}),
76+
onDeviceConnected: (device: Device) => ({}),
77+
onDevicesPresence: (devices: Device[]) => this.$previewDevicesService.updateConnectedDevices(devices),
8278
onSendingChange: (sending: boolean) => ({ }),
8379
onBiggerFilesUpload: async (filesContent, callback) => {
8480
const gzippedContent = Buffer.from(pako.gzip(filesContent));

test/services/playground/preview-app-livesync-service.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ class PreviewSdkServiceMock extends EventEmitter implements IPreviewSdkService {
7575
return "my_cool_qr_code_url";
7676
}
7777

78-
public connectedDevices: Device[] = [deviceMockData];
7978
public initialize(getInitialFiles: (device: Device) => Promise<FilesPayload>) {
8079
this.getInitialFiles = async (device) => {
8180
const filesPayload = await getInitialFiles(device);
@@ -158,6 +157,9 @@ function createTestInjector(options?: {
158157
isHookCalledWithHMR = args.hookArgs.config.appFilesUpdaterOptions.useHotModuleReload;
159158
}
160159
});
160+
injector.register("previewDevicesService", {
161+
getConnectedDevices: () => [deviceMockData]
162+
});
161163

162164
return injector;
163165
}
+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { Yok } from "../../lib/common/yok";
2+
import { PreviewDevicesService } from "../../lib/services/livesync/playground/devices/preview-devices-service";
3+
import { Device } from "nativescript-preview-sdk";
4+
import { assert } from "chai";
5+
import { DeviceDiscoveryEventNames } from "../../lib/common/constants";
6+
import { LoggerStub } from "../stubs";
7+
8+
let foundDevices: Device[] = [];
9+
let lostDevices: Device[] = [];
10+
11+
function createTestInjector(): IInjector {
12+
const injector = new Yok();
13+
injector.register("previewDevicesService", PreviewDevicesService);
14+
injector.register("logger", LoggerStub);
15+
return injector;
16+
}
17+
18+
function createDevice(id: string): Device {
19+
return {
20+
id,
21+
platform: "ios",
22+
model: "my test model",
23+
name: "my test name",
24+
osVersion: "10.0.0",
25+
previewAppVersion: "19.0.0",
26+
runtimeVersion: "5.0.0"
27+
};
28+
}
29+
30+
function resetDevices() {
31+
foundDevices = [];
32+
lostDevices = [];
33+
}
34+
35+
describe("PreviewDevicesService", () => {
36+
describe("onDevicesPresence", () => {
37+
let previewDevicesService: IPreviewDevicesService = null;
38+
beforeEach(() => {
39+
const injector = createTestInjector();
40+
previewDevicesService = injector.resolve("previewDevicesService");
41+
previewDevicesService.on(DeviceDiscoveryEventNames.DEVICE_FOUND, device => {
42+
foundDevices.push(device);
43+
});
44+
previewDevicesService.on(DeviceDiscoveryEventNames.DEVICE_LOST, device => {
45+
lostDevices.push(device);
46+
});
47+
});
48+
49+
afterEach(() => {
50+
previewDevicesService.removeAllListeners();
51+
resetDevices();
52+
});
53+
54+
it("should add new device", () => {
55+
const device = createDevice("device1");
56+
57+
previewDevicesService.updateConnectedDevices([device]);
58+
59+
assert.deepEqual(previewDevicesService.getConnectedDevices(), [device]);
60+
assert.deepEqual(foundDevices, [device]);
61+
assert.deepEqual(lostDevices, []);
62+
});
63+
it("should add new device when there are already connected devices", () => {
64+
const device1 = createDevice("device1");
65+
const device2 = createDevice("device2");
66+
67+
previewDevicesService.updateConnectedDevices([device1]);
68+
assert.deepEqual(previewDevicesService.getConnectedDevices(), [device1]);
69+
assert.deepEqual(foundDevices, [device1]);
70+
assert.deepEqual(lostDevices, []);
71+
resetDevices();
72+
73+
previewDevicesService.updateConnectedDevices([device1, device2]);
74+
75+
assert.deepEqual(previewDevicesService.getConnectedDevices(), [device1, device2]);
76+
assert.deepEqual(foundDevices, [device2]);
77+
assert.deepEqual(lostDevices, []);
78+
});
79+
it("should add more than one new device", () => {
80+
const device1 = createDevice("device1");
81+
const device2 = createDevice("device2");
82+
const device3 = createDevice("device3");
83+
84+
previewDevicesService.updateConnectedDevices([device1, device2, device3]);
85+
86+
assert.deepEqual(previewDevicesService.getConnectedDevices(), [device1, device2, device3]);
87+
assert.deepEqual(foundDevices, [device1, device2, device3]);
88+
assert.deepEqual(lostDevices, []);
89+
});
90+
it("should remove device", () => {
91+
const device1 = createDevice("device1");
92+
previewDevicesService.updateConnectedDevices([device1]);
93+
assert.deepEqual(previewDevicesService.getConnectedDevices(), [device1]);
94+
assert.deepEqual(foundDevices, [device1]);
95+
assert.deepEqual(lostDevices, []);
96+
resetDevices();
97+
98+
previewDevicesService.updateConnectedDevices([]);
99+
100+
assert.deepEqual(foundDevices, []);
101+
assert.deepEqual(lostDevices, [device1]);
102+
});
103+
it("should add and remove devices in the same time", () => {
104+
const device1 = createDevice("device1");
105+
const device2 = createDevice("device2");
106+
107+
previewDevicesService.updateConnectedDevices([device1]);
108+
assert.deepEqual(previewDevicesService.getConnectedDevices(), [device1]);
109+
assert.deepEqual(foundDevices, [device1]);
110+
assert.deepEqual(lostDevices, []);
111+
resetDevices();
112+
113+
previewDevicesService.updateConnectedDevices([device2]);
114+
115+
assert.deepEqual(previewDevicesService.getConnectedDevices(), [device2]);
116+
assert.deepEqual(foundDevices, [device2]);
117+
assert.deepEqual(lostDevices, [device1]);
118+
});
119+
});
120+
});

test/services/preview-sdk-service.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const getPreviewSdkService = (): IPreviewSdkService => {
88
testInjector.register("logger", LoggerStub);
99
testInjector.register("config", {});
1010
testInjector.register("previewSdkService", PreviewSdkService);
11-
11+
testInjector.register("previewDevicesService", {});
1212
testInjector.register("httpClient", {
1313
httpRequest: async (options: any, proxySettings?: IProxySettings): Promise<Server.IResponse> => undefined
1414
});

0 commit comments

Comments
 (0)