@@ -41,6 +41,40 @@ let device = {
41
41
};*/
42
42
let LANGUAGE = undefined ;
43
43
44
+ /** Ensure we run transfers one after the other rather that potentially having them overlap if the user clicks around
45
+ https://github.com/espruino/EspruinoAppLoaderCore/issues/67 */
46
+ let currentOperation = Promise . resolve ( ) ;
47
+
48
+ /// Start an operation - calls back
49
+ function startOperation ( options , callback ) {
50
+ options = options || { } ;
51
+ if ( ! options . name ) throw new Error ( "Expecting a name" ) ;
52
+ console . log ( `=========== Queued Operation ${ options . name } ` ) ;
53
+ return new Promise ( resolve => {
54
+ currentOperation = currentOperation . then ( ( ) => {
55
+ console . log ( `=========== Starting Operation ${ options . name } ` ) ;
56
+ let promise = callback ( ) ;
57
+ if ( ! ( promise instanceof Promise ) )
58
+ throw new Error ( `Operation ${ options . name } didn't return a promise!` ) ;
59
+ return promise ;
60
+ } ) . then ( ( result ) => {
61
+ console . log ( `=========== Operation ${ options . name } Complete` ) ;
62
+ Progress . hide ( { sticky :true } ) ;
63
+ refreshMyApps ( ) ;
64
+ refreshLibrary ( ) ;
65
+ resolve ( result ) ;
66
+ } , ( err ) => {
67
+ console . error ( `=========== ERROR during Operation ${ options . name } ` ) ;
68
+ showToast ( `${ options . name } failed, ${ err } ` , "error" ) ;
69
+ Progress . hide ( { sticky :true } ) ;
70
+ // remove loading indicator
71
+ refreshMyApps ( ) ;
72
+ refreshLibrary ( ) ;
73
+ resolve ( ) ;
74
+ } ) ;
75
+ } ) ;
76
+ }
77
+
44
78
function appJSONLoadedHandler ( ) {
45
79
appJSON . forEach ( app => {
46
80
if ( app . screenshots )
@@ -378,16 +412,10 @@ function handleCustomApp(appTemplate) {
378
412
console . log ( "Received custom app" , app ) ;
379
413
modal . remove ( ) ;
380
414
381
- getInstalledApps ( )
415
+ startOperation ( { name : "Custom App Upload" } , ( ) => getInstalledApps ( )
382
416
. then ( ( ) => checkDependencies ( app ) )
383
417
. then ( ( ) => Comms . uploadApp ( app , { device :device , language :LANGUAGE , noFinish : msg . options && msg . options . noFinish } ) )
384
- . then ( ( ) => {
385
- Progress . hide ( { sticky :true } ) ;
386
- resolve ( ) ;
387
- } ) . catch ( err => {
388
- Progress . hide ( { sticky :true } ) ;
389
- reject ( 'Upload failed, ' + err , 'error' ) ;
390
- } ) ;
418
+ . then ( resolve , reject ) ) ;
391
419
}
392
420
}
393
421
} ) ;
@@ -788,61 +816,43 @@ function uploadApp(app, options) {
788
816
if ( app . type == "defaultconfig" && ! options . force ) {
789
817
return showPrompt ( "Default Configuration Install" , "<b>This will remove all apps and data from your Bangle</b> and will install a new set of apps. Please ensure you have backed up your Bangle first. Continue?" , { yes :1 , no :1 } , false )
790
818
. then ( ( ) => showPrompt ( "Device Erasure" , "<b>Everything will be deleted from your Bangle.</b> Are you really sure?" , { yes :1 , no :1 } , false ) )
791
- . then ( ( ) => Comms . removeAllApps ( ) )
792
- . then ( ( ) => uploadApp ( app , { force :true } ) )
793
- . catch ( err => {
794
- showToast ( "Configuration install failed, " + err , "error" ) ;
795
- refreshMyApps ( ) ;
796
- refreshLibrary ( ) ;
797
- } ) ;
819
+ . then ( ( ) => startOperation ( { name :"Remove All Apps" } , ( ) => Comms . removeAllApps ( ) ) )
820
+ . then ( ( ) => uploadApp ( app , { force :true } ) ) ;
798
821
}
799
822
800
- return getInstalledApps ( ) . then ( ( ) => {
823
+ return startOperation ( { name : "App Upload" } , ( ) => getInstalledApps ( ) . then ( ( ) => {
801
824
if ( device . appsInstalled . some ( i => i . id === app . id ) ) {
802
825
return updateApp ( app ) ;
803
826
}
804
827
return checkDependencies ( app )
805
828
. then ( ( ) => Comms . uploadApp ( app , { device :device , language :LANGUAGE } ) )
806
829
. then ( ( appJSON ) => {
807
- Progress . hide ( { sticky : true } ) ;
808
830
if ( appJSON ) {
809
831
device . appsInstalled . push ( appJSON ) ;
810
832
}
811
833
showToast ( app . name + ' Uploaded!' , 'success' ) ;
812
834
} ) . catch ( err => {
813
- Progress . hide ( { sticky : true } ) ;
814
835
showToast ( 'Upload failed, ' + err , 'error' ) ;
815
- } ) . finally ( ( ) => {
816
- refreshMyApps ( ) ;
817
- refreshLibrary ( ) ;
818
836
} ) ;
819
- } ) . catch ( err => {
820
- showToast ( "App Upload failed, " + err , "error" ) ;
821
- // remove loading indicator
822
- refreshMyApps ( ) ;
823
- refreshLibrary ( ) ;
824
- } ) ;
837
+ } ) ) ;
825
838
}
826
839
827
840
/** Prompt user and then remove app from the device */
828
841
function removeApp ( app ) {
829
842
return showPrompt ( "Delete" , "Really remove '" + app . name + "'?" )
830
- . then ( ( ) => getInstalledApps ( ) )
831
- . then ( ( ) => Comms . removeApp ( device . appsInstalled . find ( a => a . id === app . id ) ) ) // a = from appid.info, app = from apps.json
832
- . then ( ( ) => {
833
- device . appsInstalled = device . appsInstalled . filter ( a => a . id != app . id ) ;
834
- showToast ( app . name + " removed successfully" , "success" ) ;
835
- refreshMyApps ( ) ;
836
- refreshLibrary ( ) ;
837
- } , err => {
838
- showToast ( app . name + " removal failed, " + err , "error" ) ;
839
- } ) ;
843
+ . then ( startOperation ( { name :"Remove App" } , ( ) => ( ) => getInstalledApps ( ) )
844
+ . then ( ( ) => Comms . removeApp ( device . appsInstalled . find ( a => a . id === app . id ) ) ) // a = from appid.info, app = from apps.json
845
+ . then ( ( ) => {
846
+ device . appsInstalled = device . appsInstalled . filter ( a => a . id != app . id ) ;
847
+ showToast ( app . name + " removed successfully" , "success" ) ;
848
+ } , err => {
849
+ showToast ( app . name + " removal failed, " + err , "error" ) ;
850
+ } ) ) ;
840
851
}
841
852
842
853
/** Show window for a new app and finally upload it */
843
854
function customApp ( app ) {
844
- return handleCustomApp ( app ) . then ( ( appJSON ) => {
845
- if ( appJSON ) device . appsInstalled . push ( appJSON ) ;
855
+ return handleCustomApp ( app ) . then ( ( ) => {
846
856
showToast ( app . name + " Uploaded!" , "success" ) ;
847
857
refreshMyApps ( ) ;
848
858
refreshLibrary ( ) ;
@@ -913,7 +923,7 @@ if options.noFinish is true, showUploadFinished isn't called (displaying the reb
913
923
function updateApp ( app , options ) {
914
924
options = options || { } ;
915
925
if ( app . custom ) return customApp ( app ) ;
916
- return Comms . getAppInfo ( app ) . then ( remove => {
926
+ return startOperation ( { name : "Update App" } , ( ) => Comms . getAppInfo ( app ) . then ( remove => {
917
927
// remove = from appid.info, app = from apps.json
918
928
if ( remove . files === undefined ) remove . files = "" ;
919
929
// no need to remove files which will be overwritten anyway
@@ -938,13 +948,7 @@ function updateApp(app, options) {
938
948
) . then ( ( appJSON ) => {
939
949
if ( appJSON ) device . appsInstalled . push ( appJSON ) ;
940
950
showToast ( app . name + " Updated!" , "success" ) ;
941
- refreshMyApps ( ) ;
942
- refreshLibrary ( ) ;
943
- } , err => {
944
- showToast ( app . name + " update failed, " + err , "error" ) ;
945
- refreshMyApps ( ) ;
946
- refreshLibrary ( ) ;
947
- } ) ;
951
+ } ) ) ;
948
952
}
949
953
950
954
@@ -1194,9 +1198,11 @@ function handleConnectionChange(connected) {
1194
1198
}
1195
1199
1196
1200
htmlToArray ( document . querySelectorAll ( ".btn.refresh" ) ) . map ( button => button . addEventListener ( "click" , ( ) => {
1197
- getInstalledApps ( true ) . catch ( err => {
1198
- showToast ( "Getting app list failed, " + err , "error" ) ;
1199
- } ) ;
1201
+ startOperation ( { name :"Refresh Apps" } , ( ) =>
1202
+ getInstalledApps ( true ) . catch ( err => {
1203
+ showToast ( "Getting app list failed, " + err , "error" ) ;
1204
+ } )
1205
+ ) ;
1200
1206
} ) ) ;
1201
1207
htmlToArray ( document . querySelectorAll ( ".btn.updateapps" ) ) . map ( button => button . addEventListener ( "click" , ( ) => {
1202
1208
updateAllApps ( ) ;
@@ -1290,33 +1296,31 @@ if (btn) btn.addEventListener("click",event=>{
1290
1296
1291
1297
btn = document . getElementById ( "resetwatch" ) ;
1292
1298
if ( btn ) btn . addEventListener ( "click" , event => {
1293
- Comms . resetDevice ( ) . then ( ( ) => {
1294
- showToast ( "Reset watch successfully" , "success" ) ;
1295
- } , err => {
1296
- showToast ( "Error resetting watch: " + err , "error" ) ;
1297
- } ) ;
1299
+ startOperation ( { name :"Reset Watch" } , ( ) =>
1300
+ Comms . resetDevice ( ) . then ( ( ) => {
1301
+ showToast ( "Reset watch successfully" , "success" ) ;
1302
+ } , err => {
1303
+ showToast ( "Error resetting watch: " + err , "error" ) ;
1304
+ } ) ) ;
1298
1305
} ) ;
1299
1306
btn = document . getElementById ( "settime" ) ;
1300
1307
if ( btn ) btn . addEventListener ( "click" , event => {
1301
- Comms . setTime ( ) . then ( ( ) => {
1302
- showToast ( "Time set successfully" , "success" ) ;
1303
- } , err => {
1304
- showToast ( "Error setting time, " + err , "error" ) ;
1305
- } ) ;
1308
+ startOperation ( { name :"Set Time" } , ( ) =>
1309
+ Comms . setTime ( ) . then ( ( ) => {
1310
+ showToast ( "Time set successfully" , "success" ) ;
1311
+ } , err => {
1312
+ showToast ( "Error setting time, " + err , "error" ) ;
1313
+ } ) ) ;
1306
1314
} ) ;
1307
1315
btn = document . getElementById ( "removeall" ) ;
1308
1316
if ( btn ) btn . addEventListener ( "click" , event => {
1309
- showPrompt ( "Remove All" , "Really remove all apps?" ) . then ( ( ) => {
1310
- return Comms . removeAllApps ( ) ;
1311
- } ) . then ( ( ) => {
1312
- Progress . hide ( { sticky :true } ) ;
1313
- device . appsInstalled = [ ] ;
1314
- showToast ( "All apps removed" , "success" ) ;
1315
- return getInstalledApps ( true ) ;
1316
- } ) . catch ( err => {
1317
- Progress . hide ( { sticky :true } ) ;
1318
- showToast ( "App removal failed, " + err , "error" ) ;
1319
- } ) ;
1317
+ showPrompt ( "Remove All" , "Really remove all apps?" ) . then ( ( ) =>
1318
+ startOperation ( { name :"Remove All Apps" } , ( ) => Comms . removeAllApps ( )
1319
+ . then ( ( ) => {
1320
+ device . appsInstalled = [ ] ;
1321
+ showToast ( "All apps removed" , "success" ) ;
1322
+ return getInstalledApps ( true ) ;
1323
+ } ) ) ) ;
1320
1324
} ) ;
1321
1325
1322
1326
// Install all favourite apps in one go
@@ -1328,10 +1332,7 @@ if (btn) btn.addEventListener("click",event => {
1328
1332
if ( ! nonCustomFavourites . includes ( id ) )
1329
1333
nonCustomFavourites . unshift ( id ) ;
1330
1334
} ) ;
1331
- installMultipleApps ( nonCustomFavourites , "favourite" ) . catch ( err => {
1332
- Progress . hide ( { sticky :true } ) ;
1333
- showToast ( "App Install failed, " + err , "error" ) ;
1334
- } ) ;
1335
+ startOperation ( { name :"Install Favourite Apps" } , ( ) => installMultipleApps ( nonCustomFavourites , "favourite" ) ) ;
1335
1336
} ) ;
1336
1337
1337
1338
// Create a new issue on github
@@ -1349,52 +1350,52 @@ if (btn) btn.addEventListener("click", event => {
1349
1350
// Screenshot button
1350
1351
btn = document . getElementById ( "screenshot" ) ;
1351
1352
if ( btn ) btn . addEventListener ( "click" , event => {
1352
- getInstalledApps ( false ) . then ( ( ) => {
1353
- if ( device . id == "BANGLEJS" ) {
1354
- showPrompt ( "Screenshot" , "Screenshots are not supported on Bangle.js 1" , { ok : 1 } ) ;
1355
- } else {
1356
- let url ;
1357
- Progress . show ( { title : "Creating screenshot" , interval : 10 , percent : "animate" , sticky : true } ) ;
1358
- Comms . write ( "\x10g.dump();\n" ) . then ( ( s ) => {
1359
- let oImage = new Image ( ) ;
1360
- oImage . onload = function ( ) {
1361
- Progress . show ( { title : "Converting screenshot" , percent : 90 , sticky : true } ) ;
1362
- let oCanvas = document . createElement ( 'canvas' ) ;
1363
- oCanvas . width = oImage . width ;
1364
- oCanvas . height = oImage . height ;
1365
- let oCtx = oCanvas . getContext ( '2d' ) ;
1366
- oCtx . drawImage ( oImage , 0 , 0 ) ;
1367
- url = oCanvas . toDataURL ( ) ;
1368
-
1369
- let screenshotHtml = `
1370
- <div style="text-align: center;">
1371
- <img align=" center" src=" ${ url } "></img >
1372
- </div >
1373
- `
1374
-
1375
- showPrompt ( "Save Screenshot?" , screenshotHtml , undefined , false ) . then ( ( r ) => {
1376
- Progress . show ( { title : "Saving screenshot" , percent : 99 , sticky : true } ) ;
1377
- let link = document . createElement ( "a" ) ;
1378
- link . download = "screenshot.png" ;
1379
- link . target = "_blank " ;
1380
- link . href = url ;
1381
- document . body . appendChild ( link ) ;
1382
- link . click ( ) ;
1383
- document . body . removeChild ( link ) ;
1384
- } ) . catch ( ( ) => {
1385
- } ) . finally ( ( ) => {
1386
- Progress . hide ( { sticky :true } ) ;
1387
- } ) ;
1388
- }
1389
- oImage . src = s . split ( "\n" ) [ 0 ] ;
1390
- Progress . hide ( { sticky :true } ) ;
1391
- Progress . show ( { title :"Screenshot done" , percent :85 , sticky :true } ) ;
1353
+ startOperation ( { name : "Screenshot" } , ( ) =>
1354
+ getInstalledApps ( false ) . then ( ( ) => {
1355
+ if ( device . id == "BANGLEJS" ) {
1356
+ showPrompt ( "Screenshot" , "Screenshots are not supported on Bangle.js 1" , { ok : 1 } ) ;
1357
+ } else {
1358
+ let url ;
1359
+ Progress . show ( { title : "Creating screenshot" , interval : 10 , percent : "animate" , sticky : true } ) ;
1360
+ return Comms . write ( "\x10g.dump();\n" ) . then ( ( s ) => {
1361
+ let oImage = new Image ( ) ;
1362
+ oImage . onload = function ( ) {
1363
+ Progress . show ( { title : "Converting screenshot" , percent : 90 , sticky : true } ) ;
1364
+ let oCanvas = document . createElement ( 'canvas' ) ;
1365
+ oCanvas . width = oImage . width ;
1366
+ oCanvas . height = oImage . height ;
1367
+ let oCtx = oCanvas . getContext ( '2d' ) ;
1368
+ oCtx . drawImage ( oImage , 0 , 0 ) ;
1369
+ url = oCanvas . toDataURL ( ) ;
1370
+
1371
+ let screenshotHtml = `
1372
+ <div style="text-align: center;" >
1373
+ <img align="center" src=" ${ url } "></img >
1374
+ </div>
1375
+ `
1376
+
1377
+ showPrompt ( "Save Screenshot?" , screenshotHtml , undefined , false ) . then ( ( r ) => {
1378
+ Progress . show ( { title : "Saving screenshot" , percent : 99 , sticky : true } ) ;
1379
+ let link = document . createElement ( "a" ) ;
1380
+ link . download = "screenshot.png " ;
1381
+ link . target = "_blank" ;
1382
+ link . href = url ;
1383
+ document . body . appendChild ( link ) ;
1384
+ link . click ( ) ;
1385
+ document . body . removeChild ( link ) ;
1386
+ } ) . catch ( ( ) => {
1387
+ Progress . hide ( { sticky :true } ) ; // cancelled
1388
+ } ) ;
1389
+ }
1390
+ oImage . src = s . split ( "\n" ) [ 0 ] ;
1391
+ Progress . hide ( { sticky :true } ) ;
1392
+ Progress . show ( { title :"Screenshot done" , percent :85 , sticky :true } ) ;
1392
1393
1393
- } , err => {
1394
- showToast ( "Error creating screenshot: " + err , "error" ) ;
1395
- } ) ;
1396
- }
1397
- } ) ;
1394
+ } , err => {
1395
+ showToast ( "Error creating screenshot: " + err , "error" ) ;
1396
+ } ) ;
1397
+ }
1398
+ } ) ) ;
1398
1399
} ) ;
1399
1400
1400
1401
// Open terminal button
0 commit comments