1
- import { Device , FilesPayload } from "nativescript-preview-sdk" ;
2
1
import { TrackActionNames , PREPARE_READY_EVENT_NAME } from "../constants" ;
3
2
import { PrepareController } from "./prepare-controller" ;
3
+ import { Device , FilesPayload } from "nativescript-preview-sdk" ;
4
4
import { performanceLog } from "../common/decorators" ;
5
- import { stringify } from "../common/helpers" ;
5
+ import { stringify , deferPromise } from "../common/helpers" ;
6
6
import { HmrConstants } from "../common/constants" ;
7
7
import { EventEmitter } from "events" ;
8
8
import { PrepareDataService } from "../services/prepare-data-service" ;
@@ -11,7 +11,12 @@ import { PreviewAppLiveSyncEvents } from "../services/livesync/playground/previe
11
11
export class PreviewAppController extends EventEmitter implements IPreviewAppController {
12
12
private prepareReadyEventHandler : any = null ;
13
13
private deviceInitializationPromise : IDictionary < boolean > = { } ;
14
- private promise = Promise . resolve ( ) ;
14
+ private devicesLiveSyncChain : IDictionary < Promise < void > > = { } ;
15
+ private devicesCanExecuteHmr : IDictionary < boolean > = { } ;
16
+ // holds HMR files per device in order to execute batch upload on fast updates
17
+ private devicesHmrFiles : IDictionary < string [ ] > = { } ;
18
+ // holds the current HMR hash per device in order to watch the proper hash status on fast updates
19
+ private devicesCurrentHmrHash : IDictionary < string > = { } ;
15
20
16
21
constructor (
17
22
private $analyticsService : IAnalyticsService ,
@@ -89,6 +94,7 @@ export class PreviewAppController extends EventEmitter implements IPreviewAppCon
89
94
90
95
if ( data . useHotModuleReload ) {
91
96
this . $hmrStatusService . attachToHmrStatusEvent ( ) ;
97
+ this . devicesCanExecuteHmr [ device . id ] = true ;
92
98
}
93
99
94
100
await this . $previewAppPluginsService . comparePluginsOnDevice ( data , device ) ;
@@ -109,7 +115,7 @@ export class PreviewAppController extends EventEmitter implements IPreviewAppCon
109
115
await this . $prepareController . prepare ( prepareData ) ;
110
116
111
117
try {
112
- const payloads = await this . getInitialFilesForPlatformSafe ( data , device . platform ) ;
118
+ const payloads = await this . getInitialFilesForDeviceSafe ( data , device ) ;
113
119
return payloads ;
114
120
} finally {
115
121
this . deviceInitializationPromise [ device . id ] = null ;
@@ -129,52 +135,95 @@ export class PreviewAppController extends EventEmitter implements IPreviewAppCon
129
135
130
136
@performanceLog ( )
131
137
private async handlePrepareReadyEvent ( data : IPreviewAppLiveSyncData , currentPrepareData : IFilesChangeEventData ) {
132
- await this . promise
133
- . then ( async ( ) => {
134
- const { hmrData, files, platform } = currentPrepareData ;
135
- const platformHmrData = _ . cloneDeep ( hmrData ) ;
136
-
137
- this . promise = this . syncFilesForPlatformSafe ( data , { filesToSync : files } , platform ) ;
138
- await this . promise ;
139
-
140
- if ( data . useHotModuleReload && platformHmrData . hash ) {
141
- const devices = this . $previewDevicesService . getDevicesForPlatform ( platform ) ;
142
-
143
- await Promise . all ( _ . map ( devices , async ( previewDevice : Device ) => {
144
- const status = await this . $hmrStatusService . getHmrStatus ( previewDevice . id , platformHmrData . hash ) ;
145
- if ( status === HmrConstants . HMR_ERROR_STATUS ) {
146
- const originalUseHotModuleReload = data . useHotModuleReload ;
147
- data . useHotModuleReload = false ;
148
- await this . syncFilesForPlatformSafe ( data , { filesToSync : platformHmrData . fallbackFiles } , platform , previewDevice . id ) ;
149
- data . useHotModuleReload = originalUseHotModuleReload ;
150
- }
151
- } ) ) ;
138
+ const { hmrData, files, platform } = currentPrepareData ;
139
+ const platformHmrData = _ . cloneDeep ( hmrData ) ;
140
+ const connectedDevices = this . $previewDevicesService . getDevicesForPlatform ( platform ) ;
141
+ if ( ! connectedDevices || ! connectedDevices . length ) {
142
+ this . $logger . warn ( "Unable to find any connected devices. In order to execute live sync, open your Preview app and optionally re-scan the QR code using the Playground app." ) ;
143
+ return ;
144
+ }
145
+
146
+ await Promise . all ( _ . map ( connectedDevices , async ( device ) => {
147
+ const previousSync = this . devicesLiveSyncChain [ device . id ] || Promise . resolve ( ) ;
148
+ const currentSyncDeferPromise = deferPromise < void > ( ) ;
149
+ this . devicesLiveSyncChain [ device . id ] = currentSyncDeferPromise . promise ;
150
+ this . devicesCurrentHmrHash [ device . id ] = this . devicesCurrentHmrHash [ device . id ] || platformHmrData . hash ;
151
+ this . devicesHmrFiles [ device . id ] = this . devicesHmrFiles [ device . id ] || [ ] ;
152
+ this . devicesHmrFiles [ device . id ] . push ( ...files ) ;
153
+ await previousSync ;
154
+ if ( ! this . devicesHmrFiles [ device . id ] || ! this . devicesHmrFiles [ device . id ] . length ) {
155
+ this . $logger . info ( "Skipping files sync. The changes are already batch transferred in a previous sync." ) ;
156
+ currentSyncDeferPromise . resolve ( ) ;
157
+ return ;
158
+ }
159
+
160
+ try {
161
+
162
+ let executeHmrSync = false ;
163
+ const hmrHash = this . devicesCurrentHmrHash [ device . id ] ;
164
+ this . devicesCurrentHmrHash [ device . id ] = null ;
165
+ if ( data . useHotModuleReload && hmrHash ) {
166
+ if ( this . devicesCanExecuteHmr [ device . id ] ) {
167
+ executeHmrSync = true ;
168
+ this . $hmrStatusService . watchHmrStatus ( device . id , hmrHash ) ;
169
+ }
152
170
}
153
- } ) ;
171
+
172
+ const filesToSync = executeHmrSync ? this . devicesHmrFiles [ device . id ] : platformHmrData . fallbackFiles ;
173
+ this . devicesHmrFiles [ device . id ] = [ ] ;
174
+ if ( executeHmrSync ) {
175
+ await this . syncFilesForPlatformSafe ( data , { filesToSync } , platform , device ) ;
176
+ const status = await this . $hmrStatusService . getHmrStatus ( device . id , hmrHash ) ;
177
+ if ( ! status ) {
178
+ this . devicesCanExecuteHmr [ device . id ] = false ;
179
+ const noStatusWarning = this . getDeviceMsg ( device . name ,
180
+ "Unable to get LiveSync status from the Preview app. Ensure the app is running in order to sync changes." ) ;
181
+ this . $logger . warn ( noStatusWarning ) ;
182
+ } else {
183
+ this . devicesCanExecuteHmr [ device . id ] = status === HmrConstants . HMR_SUCCESS_STATUS ;
184
+ }
185
+ } else {
186
+ const noHmrData = _ . assign ( { } , data , { useHotModuleReload : false } ) ;
187
+ await this . syncFilesForPlatformSafe ( noHmrData , { filesToSync } , platform , device ) ;
188
+ this . devicesCanExecuteHmr [ device . id ] = true ;
189
+ }
190
+ currentSyncDeferPromise . resolve ( ) ;
191
+ } catch ( e ) {
192
+ currentSyncDeferPromise . resolve ( ) ;
193
+ }
194
+ } ) ) ;
154
195
}
155
196
156
- private async getInitialFilesForPlatformSafe ( data : IPreviewAppLiveSyncData , platform : string ) : Promise < FilesPayload > {
157
- this . $logger . info ( `Start sending initial files for platform ${ platform } .` ) ;
197
+ private getDeviceMsg ( deviceId : string , message : string ) {
198
+ return `[${ deviceId } ] ${ message } ` ;
199
+ }
200
+
201
+ private async getInitialFilesForDeviceSafe ( data : IPreviewAppLiveSyncData , device : Device ) : Promise < FilesPayload > {
202
+ const platform = device . platform ;
203
+ this . $logger . info ( `Start sending initial files for device ${ device . name } .` ) ;
158
204
159
205
try {
160
206
const payloads = this . $previewAppFilesService . getInitialFilesPayload ( data , platform ) ;
161
- this . $logger . info ( `Successfully sent initial files for platform ${ platform } .` ) ;
207
+ this . $logger . info ( `Successfully sent initial files for device ${ device . name } .` ) ;
162
208
return payloads ;
163
209
} catch ( err ) {
164
- this . $logger . warn ( `Unable to apply changes for platform ${ platform } . Error is: ${ err } , ${ stringify ( err ) } ` ) ;
210
+ this . $logger . warn ( `Unable to apply changes for device ${ device . name } . Error is: ${ err } , ${ stringify ( err ) } ` ) ;
165
211
}
166
212
}
167
213
168
- private async syncFilesForPlatformSafe ( data : IPreviewAppLiveSyncData , filesData : IPreviewAppFilesData , platform : string , deviceId ?: string ) : Promise < void > {
214
+ private async syncFilesForPlatformSafe ( data : IPreviewAppLiveSyncData , filesData : IPreviewAppFilesData , platform : string , device : Device ) : Promise < void > {
215
+ const deviceId = device && device . id || "" ;
216
+
169
217
try {
170
218
const payloads = this . $previewAppFilesService . getFilesPayload ( data , filesData , platform ) ;
219
+ payloads . deviceId = deviceId ;
171
220
if ( payloads && payloads . files && payloads . files . length ) {
172
- this . $logger . info ( `Start syncing changes for platform ${ platform } .` ) ;
221
+ this . $logger . info ( `Start syncing changes for device ${ device . name } .` ) ;
173
222
await this . $previewSdkService . applyChanges ( payloads ) ;
174
- this . $logger . info ( `Successfully synced ${ payloads . files . map ( filePayload => filePayload . file . yellow ) } for platform ${ platform } .` ) ;
223
+ this . $logger . info ( `Successfully synced ${ payloads . files . map ( filePayload => filePayload . file . yellow ) } for device ${ device . name } .` ) ;
175
224
}
176
225
} catch ( error ) {
177
- this . $logger . warn ( `Unable to apply changes for platform ${ platform } . Error is: ${ error } , ${ JSON . stringify ( error , null , 2 ) } .` ) ;
226
+ this . $logger . warn ( `Unable to apply changes for device ${ device . name } . Error is: ${ error } , ${ JSON . stringify ( error , null , 2 ) } .` ) ;
178
227
this . emit ( PreviewAppLiveSyncEvents . PREVIEW_APP_LIVE_SYNC_ERROR , {
179
228
error,
180
229
data,
0 commit comments