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