@@ -41,6 +41,40 @@ let device = {
4141};*/
4242let LANGUAGE = undefined ;
4343
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+
4478function appJSONLoadedHandler ( ) {
4579 appJSON . forEach ( app => {
4680 if ( app . screenshots )
@@ -378,16 +412,10 @@ function handleCustomApp(appTemplate) {
378412 console . log ( "Received custom app" , app ) ;
379413 modal . remove ( ) ;
380414
381- getInstalledApps ( )
415+ startOperation ( { name : "Custom App Upload" } , ( ) => getInstalledApps ( )
382416 . then ( ( ) => checkDependencies ( app ) )
383417 . 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 ) ) ;
391419 }
392420 }
393421 } ) ;
@@ -788,61 +816,43 @@ function uploadApp(app, options) {
788816 if ( app . type == "defaultconfig" && ! options . force ) {
789817 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 )
790818 . 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 } ) ) ;
798821 }
799822
800- return getInstalledApps ( ) . then ( ( ) => {
823+ return startOperation ( { name : "App Upload" } , ( ) => getInstalledApps ( ) . then ( ( ) => {
801824 if ( device . appsInstalled . some ( i => i . id === app . id ) ) {
802825 return updateApp ( app ) ;
803826 }
804827 return checkDependencies ( app )
805828 . then ( ( ) => Comms . uploadApp ( app , { device :device , language :LANGUAGE } ) )
806829 . then ( ( appJSON ) => {
807- Progress . hide ( { sticky : true } ) ;
808830 if ( appJSON ) {
809831 device . appsInstalled . push ( appJSON ) ;
810832 }
811833 showToast ( app . name + ' Uploaded!' , 'success' ) ;
812834 } ) . catch ( err => {
813- Progress . hide ( { sticky : true } ) ;
814835 showToast ( 'Upload failed, ' + err , 'error' ) ;
815- } ) . finally ( ( ) => {
816- refreshMyApps ( ) ;
817- refreshLibrary ( ) ;
818836 } ) ;
819- } ) . catch ( err => {
820- showToast ( "App Upload failed, " + err , "error" ) ;
821- // remove loading indicator
822- refreshMyApps ( ) ;
823- refreshLibrary ( ) ;
824- } ) ;
837+ } ) ) ;
825838}
826839
827840/** Prompt user and then remove app from the device */
828841function removeApp ( app ) {
829842 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+ } ) ) ;
840851}
841852
842853/** Show window for a new app and finally upload it */
843854function customApp ( app ) {
844- return handleCustomApp ( app ) . then ( ( appJSON ) => {
845- if ( appJSON ) device . appsInstalled . push ( appJSON ) ;
855+ return handleCustomApp ( app ) . then ( ( ) => {
846856 showToast ( app . name + " Uploaded!" , "success" ) ;
847857 refreshMyApps ( ) ;
848858 refreshLibrary ( ) ;
@@ -913,7 +923,7 @@ if options.noFinish is true, showUploadFinished isn't called (displaying the reb
913923function updateApp ( app , options ) {
914924 options = options || { } ;
915925 if ( app . custom ) return customApp ( app ) ;
916- return Comms . getAppInfo ( app ) . then ( remove => {
926+ return startOperation ( { name : "Update App" } , ( ) => Comms . getAppInfo ( app ) . then ( remove => {
917927 // remove = from appid.info, app = from apps.json
918928 if ( remove . files === undefined ) remove . files = "" ;
919929 // no need to remove files which will be overwritten anyway
@@ -938,13 +948,7 @@ function updateApp(app, options) {
938948 ) . then ( ( appJSON ) => {
939949 if ( appJSON ) device . appsInstalled . push ( appJSON ) ;
940950 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+ } ) ) ;
948952}
949953
950954
@@ -1194,9 +1198,11 @@ function handleConnectionChange(connected) {
11941198}
11951199
11961200htmlToArray ( 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+ ) ;
12001206} ) ) ;
12011207htmlToArray ( document . querySelectorAll ( ".btn.updateapps" ) ) . map ( button => button . addEventListener ( "click" , ( ) => {
12021208 updateAllApps ( ) ;
@@ -1290,33 +1296,31 @@ if (btn) btn.addEventListener("click",event=>{
12901296
12911297btn = document . getElementById ( "resetwatch" ) ;
12921298if ( 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+ } ) ) ;
12981305} ) ;
12991306btn = document . getElementById ( "settime" ) ;
13001307if ( 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+ } ) ) ;
13061314} ) ;
13071315btn = document . getElementById ( "removeall" ) ;
13081316if ( 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+ } ) ) ) ;
13201324} ) ;
13211325
13221326// Install all favourite apps in one go
@@ -1328,10 +1332,7 @@ if (btn) btn.addEventListener("click",event => {
13281332 if ( ! nonCustomFavourites . includes ( id ) )
13291333 nonCustomFavourites . unshift ( id ) ;
13301334 } ) ;
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" ) ) ;
13351336} ) ;
13361337
13371338// Create a new issue on github
@@ -1349,52 +1350,52 @@ if (btn) btn.addEventListener("click", event => {
13491350// Screenshot button
13501351btn = document . getElementById ( "screenshot" ) ;
13511352if ( 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 } ) ;
13921393
1393- } , err => {
1394- showToast ( "Error creating screenshot: " + err , "error" ) ;
1395- } ) ;
1396- }
1397- } ) ;
1394+ } , err => {
1395+ showToast ( "Error creating screenshot: " + err , "error" ) ;
1396+ } ) ;
1397+ }
1398+ } ) ) ;
13981399} ) ;
13991400
14001401// Open terminal button
0 commit comments