Skip to content

Commit cc8e877

Browse files
fix: initial sync on Preview app (Android) leads to loop
When `tns preview` command is executed and the QR code is scanned with Android device, the Preview app on this device asks for initial sync of the files. At this point CLI starts preparing the project and once the files are prepared, it sends them to device. However, this operation may take some time, especially for code sharing projects, which have a lot of dependencies and webpack is used. Meanwhile the Preview app on device sees that it had not received the files within 10 seconds and sends a new message to get the initial files. So CLI starts another process of preparation and trying to sync the changes. Based on the time required, this may become an indefinite loop. Fix this by caching the promise for initial sync and in case device asks again, CLI will use the already pending promise. Also fix the case where another device from the same platform is used to scan the QR code at some point. Currently we've not passed device identifier to pubnub and the initial sync was sending the files to all devices in the mentioned scenario.
1 parent 9f131d4 commit cc8e877

File tree

1 file changed

+42
-27
lines changed

1 file changed

+42
-27
lines changed

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

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const isTextOrBinary = require('istextorbinary');
77
export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService {
88
private excludedFileExtensions = [".ts", ".sass", ".scss", ".less"];
99
private excludedFiles = [".DS_Store"];
10+
private deviceInitializationPromise: IDictionary<Promise<FilesPayload>> = {};
1011

1112
constructor(private $fs: IFileSystem,
1213
private $errors: IErrors,
@@ -26,37 +27,51 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService {
2627
this.$errors.failWithoutHelp("Sending initial preview files without a specified device is not supported.");
2728
}
2829

29-
const filesToSyncMap: IDictionary<string[]> = {};
30-
let promise = Promise.resolve<FilesPayload>(null);
31-
const startSyncFilesTimeout = async (platform: string) => {
32-
await promise
33-
.then(async () => {
34-
const projectData = this.$projectDataService.getProjectData(data.projectDir);
35-
promise = this.applyChanges(this.$platformsData.getPlatformData(platform, projectData), projectData, filesToSyncMap[platform]);
36-
await promise;
37-
});
38-
filesToSyncMap[platform] = [];
39-
};
40-
await this.$hooksService.executeBeforeHooks("preview-sync", {
41-
hookArgs: {
42-
projectData: this.$projectDataService.getProjectData(data.projectDir),
43-
config: {
44-
env: data.env,
45-
platform: device.platform,
46-
appFilesUpdaterOptions: data.appFilesUpdaterOptions,
47-
},
48-
externals: this.$previewAppPluginsService.getExternalPlugins(device),
49-
filesToSyncMap,
50-
startSyncFilesTimeout: startSyncFilesTimeout.bind(this)
51-
}
52-
});
53-
await this.$previewAppPluginsService.comparePluginsOnDevice(data, device);
54-
const payloads = await this.syncFilesForPlatformSafe(data, device.platform);
30+
if (this.deviceInitializationPromise[device.id]) {
31+
return this.deviceInitializationPromise[device.id];
32+
}
5533

56-
return payloads;
34+
this.deviceInitializationPromise[device.id] = this.initializePreviewForDevice(data, device);
35+
try {
36+
const payloads = await this.deviceInitializationPromise[device.id];
37+
return payloads;
38+
} finally {
39+
this.deviceInitializationPromise[device.id] = null;
40+
}
5741
});
5842
}
5943

44+
private async initializePreviewForDevice(data: IPreviewAppLiveSyncData, device: Device): Promise<FilesPayload> {
45+
const filesToSyncMap: IDictionary<string[]> = {};
46+
let promise = Promise.resolve<FilesPayload>(null);
47+
const startSyncFilesTimeout = async (platform: string) => {
48+
await promise
49+
.then(async () => {
50+
const projectData = this.$projectDataService.getProjectData(data.projectDir);
51+
promise = this.applyChanges(this.$platformsData.getPlatformData(platform, projectData), projectData, filesToSyncMap[platform]);
52+
await promise;
53+
});
54+
filesToSyncMap[platform] = [];
55+
};
56+
await this.$hooksService.executeBeforeHooks("preview-sync", {
57+
hookArgs: {
58+
projectData: this.$projectDataService.getProjectData(data.projectDir),
59+
config: {
60+
env: data.env,
61+
platform: device.platform,
62+
appFilesUpdaterOptions: data.appFilesUpdaterOptions,
63+
},
64+
externals: this.$previewAppPluginsService.getExternalPlugins(device),
65+
filesToSyncMap,
66+
startSyncFilesTimeout: startSyncFilesTimeout.bind(this)
67+
}
68+
});
69+
await this.$previewAppPluginsService.comparePluginsOnDevice(data, device);
70+
const payloads = await this.syncFilesForPlatformSafe(data, device.platform);
71+
payloads.deviceId = device.id;
72+
return payloads;
73+
}
74+
6075
public async syncFiles(data: IPreviewAppLiveSyncData, files?: string[]): Promise<void> {
6176
this.showWarningsForNativeFiles(files);
6277

0 commit comments

Comments
 (0)