2019-11-03 11:13:21 +00:00
var appJSON = [ ] ; // List of apps and info from apps.json
2019-12-05 14:48:56 +00:00
var appsInstalled = [ ] ; // list of app JSON
2019-12-24 13:47:02 +00:00
var files = [ ] ; // list of files on Bangle
2019-11-03 11:13:21 +00:00
2019-10-30 17:33:58 +00:00
httpGet ( "apps.json" ) . then ( apps => {
2019-11-07 08:43:56 +00:00
try {
appJSON = JSON . parse ( apps ) ;
} catch ( e ) {
console . log ( e ) ;
showToast ( "App List Corrupted" , "error" ) ;
}
2019-11-03 11:13:21 +00:00
appJSON . sort ( appSorter ) ;
2019-10-30 17:33:58 +00:00
refreshLibrary ( ) ;
} ) ;
// Status
// =========================================== Top Navigation
2019-11-03 11:13:21 +00:00
function showToast ( message , type ) {
2019-10-30 17:33:58 +00:00
// toast-primary, toast-success, toast-warning or toast-error
2019-11-06 17:25:02 +00:00
var style = "toast-primary" ;
if ( type == "success" ) style = "toast-success" ;
else if ( type == "error" ) style = "toast-error" ;
else if ( type !== undefined ) console . log ( "showToast: unknown toast " + type ) ;
2019-10-30 17:33:58 +00:00
var toastcontainer = document . getElementById ( "toastcontainer" ) ;
2019-11-06 17:25:02 +00:00
var msgDiv = htmlElement ( ` <div class="toast ${ style } "></div> ` ) ;
2019-10-30 17:33:58 +00:00
msgDiv . innerHTML = message ;
toastcontainer . append ( msgDiv ) ;
setTimeout ( function ( ) {
msgDiv . remove ( ) ;
} , 5000 ) ;
}
2020-02-27 15:52:20 +00:00
var progressToast ; // the DOM element
var progressSticky ; // showProgress(,,"sticky") don't remove until hideProgress("sticky")
var progressInterval ; // the interval used if showProgress(..., "animate")
var progressPercent ; // the current progress percentage
function showProgress ( text , percent , sticky ) {
if ( sticky == "sticky" )
progressSticky = true ;
2019-12-05 11:48:56 +00:00
if ( ! progressToast ) {
2020-02-27 15:52:20 +00:00
if ( progressInterval ) {
clearInterval ( progressInterval ) ;
progressInterval = undefined ;
}
if ( percent == "animate" ) {
progressInterval = setInterval ( function ( ) {
progressPercent += 2 ;
if ( progressPercent > 100 ) progressPercent = 0 ;
showProgress ( undefined , progressPercent ) ;
} , 100 ) ;
percent = 0 ;
}
progressPercent = percent ;
2019-12-05 11:48:56 +00:00
var toastcontainer = document . getElementById ( "toastcontainer" ) ;
progressToast = htmlElement ( ` <div class="toast">
2020-02-27 15:52:20 +00:00
$ { text ? ` <div> ${ text } </div> ` : ` ` }
2019-12-05 11:48:56 +00:00
< div class = "bar bar-sm" >
< div class = "bar-item" id = "progressToast" role = "progressbar" style = "width:${percent}%;" aria - valuenow = "${percent}" aria - valuemin = "0" aria - valuemax = "100" > < / d i v >
< / d i v >
< / d i v > ` ) ;
toastcontainer . append ( progressToast ) ;
} else {
var pt = document . getElementById ( "progressToast" ) ;
pt . setAttribute ( "aria-valuenow" , percent ) ;
pt . style . width = percent + "%" ;
}
}
2020-02-27 15:52:20 +00:00
function hideProgress ( sticky ) {
if ( progressSticky && sticky != "sticky" )
return ;
progressSticky = false ;
if ( progressInterval ) {
clearInterval ( progressInterval ) ;
progressInterval = undefined ;
}
if ( progressToast ) progressToast . remove ( ) ;
progressToast = undefined ;
}
Puck . writeProgress = function ( charsSent , charsTotal ) {
if ( charsSent === undefined ) {
hideProgress ( ) ;
return ;
}
var percent = Math . round ( charsSent * 100 / charsTotal ) ;
showProgress ( undefined , percent ) ;
}
2020-02-13 10:12:49 +00:00
function showPrompt ( title , text , buttons ) {
if ( ! buttons ) buttons = { yes : 1 , no : 1 } ;
2019-10-30 17:33:58 +00:00
return new Promise ( ( resolve , reject ) => {
var modal = htmlElement ( ` <div class="modal active">
<!-- < a href = "#close" class = "modal-overlay" aria - label = "Close" > < / a > - - >
< div class = "modal-container" >
< div class = "modal-header" >
< a href = "#close" class = "btn btn-clear float-right" aria - label = "Close" > < / a >
< div class = "modal-title h5" > $ { escapeHtml ( title ) } < / d i v >
< / d i v >
< div class = "modal-body" >
< div class = "content" >
2020-02-13 10:12:49 +00:00
$ { escapeHtml ( text ) . replace ( /\n/g , '<br/>' ) }
2019-10-30 17:33:58 +00:00
< / d i v >
< / d i v >
< div class = "modal-footer" >
< div class = "modal-footer" >
2020-02-13 10:12:49 +00:00
$ { buttons . yes ? '<button class="btn btn-primary" isyes="1">Yes</button>' : '' }
$ { buttons . no ? '<button class="btn" isyes="0">No</button>' : '' }
$ { buttons . ok ? '<button class="btn" isyes="1">Ok</button>' : '' }
2019-10-30 17:33:58 +00:00
< / d i v >
< / d i v >
< / d i v >
< / d i v > ` ) ;
document . body . append ( modal ) ;
2020-02-13 10:12:49 +00:00
modal . querySelector ( "a[href='#close']" ) . addEventListener ( "click" , event => {
event . preventDefault ( ) ;
2020-02-27 15:52:20 +00:00
reject ( "User cancelled" ) ;
2020-02-13 10:12:49 +00:00
modal . remove ( ) ;
} ) ;
2019-10-30 17:33:58 +00:00
htmlToArray ( modal . getElementsByTagName ( "button" ) ) . forEach ( button => {
button . addEventListener ( "click" , event => {
2019-11-06 17:25:02 +00:00
event . preventDefault ( ) ;
2019-11-03 11:13:21 +00:00
var isYes = event . target . getAttribute ( "isyes" ) == "1" ;
2019-10-30 17:33:58 +00:00
if ( isYes ) resolve ( ) ;
2020-02-27 15:52:20 +00:00
else reject ( "User cancelled" ) ;
2019-10-30 17:33:58 +00:00
modal . remove ( ) ;
} ) ;
} ) ;
} ) ;
}
2020-02-13 10:12:49 +00:00
function showChangeLog ( appid ) {
var app = appNameToApp ( appid ) ;
function show ( contents ) {
showPrompt ( app . name + " Change Log" , contents , { ok : true } ) . catch ( ( ) => { } ) ; ;
}
httpGet ( ` apps/ ${ appid } /ChangeLog ` ) .
then ( show ) . catch ( ( ) => show ( "No Change Log available" ) ) ;
}
2019-11-06 17:25:02 +00:00
function handleCustomApp ( app ) {
2020-02-07 17:16:45 +00:00
// Pops up an IFRAME that allows an app to be customised
if ( ! app . custom ) throw new Error ( "App doesn't have custom HTML" ) ;
2019-11-06 17:25:02 +00:00
return new Promise ( ( resolve , reject ) => {
var modal = htmlElement ( ` <div class="modal active">
< a href = "#close" class = "modal-overlay " aria - label = "Close" > < / a >
< div class = "modal-container" style = "height:100%" >
< div class = "modal-header" >
< a href = "#close" class = "btn btn-clear float-right" aria - label = "Close" > < / a >
< div class = "modal-title h5" > $ { escapeHtml ( app . name ) } < / d i v >
< / d i v >
< div class = "modal-body" style = "height:100%" >
< div class = "content" style = "height:100%" >
2019-11-17 22:39:31 +00:00
< iframe src = "apps/${app.id}/${app.custom}" style = "width:100%;height:100%;border:0px;" >
2019-11-06 17:25:02 +00:00
< / d i v >
< / d i v >
< / d i v >
< / d i v > ` ) ;
document . body . append ( modal ) ;
htmlToArray ( modal . getElementsByTagName ( "a" ) ) . forEach ( button => {
button . addEventListener ( "click" , event => {
event . preventDefault ( ) ;
modal . remove ( ) ;
reject ( "Window closed" ) ;
} ) ;
} ) ;
var iframe = modal . getElementsByTagName ( "iframe" ) [ 0 ] ;
iframe . contentWindow . addEventListener ( "message" , function ( event ) {
var app = event . data ;
console . log ( "Received custom app" , app ) ;
modal . remove ( ) ;
2020-02-27 15:52:20 +00:00
showProgress ( ` Uploading ${ app . name } ` , undefined , "sticky" ) ;
Comms . uploadApp ( app ) . then ( ( ) => {
hideProgress ( "sticky" ) ;
resolve ( ) ;
} ) . catch ( e => {
hideProgress ( "sticky" ) ;
reject ( e ) ;
} ) ;
2019-11-06 17:25:02 +00:00
} , false ) ;
} ) ;
}
2020-02-07 17:16:45 +00:00
function handleAppInterface ( app ) {
// IFRAME interface window that can be used to get data from the app
if ( ! app . interface ) throw new Error ( "App doesn't have interface HTML" ) ;
return new Promise ( ( resolve , reject ) => {
var modal = htmlElement ( ` <div class="modal active">
< a href = "#close" class = "modal-overlay " aria - label = "Close" > < / a >
< div class = "modal-container" style = "height:100%" >
< div class = "modal-header" >
< a href = "#close" class = "btn btn-clear float-right" aria - label = "Close" > < / a >
< div class = "modal-title h5" > $ { escapeHtml ( app . name ) } < / d i v >
< / d i v >
< div class = "modal-body" style = "height:100%" >
< div class = "content" style = "height:100%" >
< iframe style = "width:100%;height:100%;border:0px;" >
< / d i v >
< / d i v >
< / d i v >
< / d i v > ` ) ;
document . body . append ( modal ) ;
htmlToArray ( modal . getElementsByTagName ( "a" ) ) . forEach ( button => {
button . addEventListener ( "click" , event => {
event . preventDefault ( ) ;
modal . remove ( ) ;
//reject("Window closed");
} ) ;
} ) ;
var iframe = modal . getElementsByTagName ( "iframe" ) [ 0 ] ;
2020-02-10 13:49:36 +00:00
iframe . onload = function ( ) {
var iwin = iframe . contentWindow ;
iwin . addEventListener ( "message" , function ( event ) {
var msg = event . data ;
if ( msg . type == "eval" ) {
Puck . eval ( msg . data , function ( result ) {
iwin . postMessage ( {
type : "evalrsp" ,
data : result ,
id : msg . id
} ) ;
2020-02-07 17:16:45 +00:00
} ) ;
2020-02-10 13:49:36 +00:00
} else if ( msg . type == "write" ) {
Puck . write ( msg . data , function ( result ) {
iwin . postMessage ( {
type : "writersp" ,
data : result ,
id : msg . id
} ) ;
2020-02-07 17:16:45 +00:00
} ) ;
2020-02-10 13:49:36 +00:00
}
} , false ) ;
iwin . postMessage ( { type : "init" } ) ;
} ;
iframe . src = ` apps/ ${ app . id } / ${ app . interface } ` ;
2020-02-07 17:16:45 +00:00
} ) ;
}
2019-10-30 17:33:58 +00:00
// =========================================== Top Navigation
function showTab ( tabname ) {
htmlToArray ( document . querySelectorAll ( "#tab-navigate .tab-item" ) ) . forEach ( tab => {
tab . classList . remove ( "active" ) ;
} ) ;
htmlToArray ( document . querySelectorAll ( ".bangle-tab" ) ) . forEach ( tab => {
tab . style . display = "none" ;
} ) ;
document . getElementById ( "tab-" + tabname ) . classList . add ( "active" ) ;
document . getElementById ( tabname ) . style . display = "inherit" ;
}
// =========================================== Library
2019-11-13 17:27:22 +00:00
var activeFilter = '' ;
var currentSearch = '' ;
2019-10-30 17:33:58 +00:00
function refreshLibrary ( ) {
var panelbody = document . querySelector ( "#librarycontainer .panel-body" ) ;
2019-11-13 17:27:22 +00:00
var visibleApps = appJSON ;
if ( activeFilter ) {
visibleApps = visibleApps . filter ( app => app . tags && app . tags . split ( ',' ) . includes ( activeFilter ) ) ;
}
if ( currentSearch ) {
visibleApps = visibleApps . filter ( app => app . name . toLowerCase ( ) . includes ( currentSearch ) || app . tags . includes ( currentSearch ) ) ;
}
panelbody . innerHTML = visibleApps . map ( ( app , idx ) => {
2020-02-07 14:51:31 +00:00
var appInstalled = appsInstalled . find ( a => a . id == app . id ) ;
var version = getVersionInfo ( app , appInstalled ) ;
var versionInfo = version . text ;
2019-12-05 14:48:56 +00:00
if ( versionInfo ) versionInfo = " <small>(" + versionInfo + ")</small>" ;
2019-11-06 17:25:02 +00:00
return ` <div class="tile column col-6 col-sm-12 col-xs-12">
2019-10-30 17:33:58 +00:00
< div class = "tile-icon" >
2019-11-17 22:39:31 +00:00
< figure class = "avatar" > < img src = "apps/${app.icon?`${app.id}/${app.icon}`:" unknown . png "}" alt = "${escapeHtml(app.name)}" > < / f i g u r e >
2019-10-30 17:33:58 +00:00
< / d i v >
< div class = "tile-content" >
2019-12-05 14:48:56 +00:00
< p class = "tile-title text-bold" > $ { escapeHtml ( app . name ) } $ { versionInfo } < / p >
2019-10-30 17:33:58 +00:00
< p class = "tile-subtitle" > $ { escapeHtml ( app . description ) } < / p >
< / d i v >
< div class = "tile-action" >
2020-02-07 17:16:45 +00:00
< button class = "btn btn-link btn-action btn-lg ${(appInstalled&&app.interface)?" ":" d - hide "}" appid = "${app.id}" title = "Download data from app" > < i class = "icon icon-download" > < / i > < / b u t t o n >
2020-02-07 14:51:31 +00:00
< button class = "btn btn-link btn-action btn-lg ${app.allow_emulator?" ":" d - hide "}" appid = "${app.id}" title = "Try in Emulator" > < i class = "icon icon-share" > < / i > < / b u t t o n >
< button class = "btn btn-link btn-action btn-lg ${version.canUpdate?" ":" d - hide "}" appid = "${app.id}" title = "Update App" > < i class = "icon icon-refresh" > < / i > < / b u t t o n >
< button class = "btn btn-link btn-action btn-lg ${!appInstalled?" ":" d - hide "}" appid = "${app.id}" title = "Upload App" > < i class = "icon icon-upload" > < / i > < / b u t t o n >
< button class = "btn btn-link btn-action btn-lg ${appInstalled?" ":" d - hide "}" appid = "${app.id}" title = "Remove App" > < i class = "icon icon-delete" > < / i > < / b u t t o n >
< button class = "btn btn-link btn-action btn-lg ${app.custom?" ":" d - hide "}" appid = "${app.id}" title = "Customise and Upload App" > < i class = "icon icon-menu" > < / i > < / b u t t o n >
2019-10-30 17:33:58 +00:00
< / d i v >
< / d i v >
2019-11-06 17:25:02 +00:00
` ;}).join("");
2019-10-30 17:33:58 +00:00
// set badge up top
var tab = document . querySelector ( "#tab-librarycontainer a" ) ;
tab . classList . add ( "badge" ) ;
2019-11-03 11:13:21 +00:00
tab . setAttribute ( "data-badge" , appJSON . length ) ;
2019-10-30 17:33:58 +00:00
htmlToArray ( panelbody . getElementsByTagName ( "button" ) ) . forEach ( button => {
button . addEventListener ( "click" , event => {
2020-02-07 14:51:31 +00:00
var button = event . currentTarget ;
var icon = button . firstChild ;
var appid = button . getAttribute ( "appid" ) ;
var app = appNameToApp ( appid ) ;
if ( ! app ) throw new Error ( "App " + appid + " not found" ) ;
// check icon to figure out what we should do
2019-12-03 11:45:55 +00:00
if ( icon . classList . contains ( "icon-share" ) ) {
// emulator
var file = app . storage . find ( f => f . name [ 0 ] == '-' ) ;
if ( ! file ) {
console . error ( "No entrypoint found for " + appid ) ;
return ;
}
var baseurl = window . location . href ;
var url = baseurl + "apps/" + app . id + "/" + file . url ;
window . open ( ` https://espruino.com/ide/emulator.html?codeurl= ${ url } &upload ` ) ;
} else if ( icon . classList . contains ( "icon-upload" ) ) {
2020-02-07 14:51:31 +00:00
// upload
2019-11-03 11:13:21 +00:00
icon . classList . remove ( "icon-upload" ) ;
icon . classList . add ( "loading" ) ;
2020-02-27 15:52:20 +00:00
showProgress ( ` Uploading ${ app . name } ` , undefined , "sticky" ) ;
2019-12-05 14:48:56 +00:00
Comms . uploadApp ( app ) . then ( ( appJSON ) => {
2020-02-27 15:52:20 +00:00
hideProgress ( "sticky" ) ;
2019-12-05 14:48:56 +00:00
if ( appJSON ) appsInstalled . push ( appJSON ) ;
2019-11-03 11:13:21 +00:00
showToast ( app . name + " Uploaded!" , "success" ) ;
icon . classList . remove ( "loading" ) ;
icon . classList . add ( "icon-delete" ) ;
refreshMyApps ( ) ;
2020-02-07 14:51:31 +00:00
refreshLibrary ( ) ;
2019-11-03 11:13:21 +00:00
} ) . catch ( err => {
2020-02-27 15:52:20 +00:00
hideProgress ( "sticky" ) ;
2019-11-03 11:13:21 +00:00
showToast ( "Upload failed, " + err , "error" ) ;
icon . classList . remove ( "loading" ) ;
icon . classList . add ( "icon-upload" ) ;
} ) ;
2019-11-06 17:25:02 +00:00
} else if ( icon . classList . contains ( "icon-menu" ) ) {
2020-02-07 14:51:31 +00:00
// custom HTML update
2019-11-06 17:25:02 +00:00
if ( app . custom ) {
icon . classList . remove ( "icon-menu" ) ;
icon . classList . add ( "loading" ) ;
2019-12-05 14:48:56 +00:00
handleCustomApp ( app ) . then ( ( appJSON ) => {
if ( appJSON ) appsInstalled . push ( appJSON ) ;
2019-11-06 17:25:02 +00:00
showToast ( app . name + " Uploaded!" , "success" ) ;
icon . classList . remove ( "loading" ) ;
icon . classList . add ( "icon-delete" ) ;
refreshMyApps ( ) ;
2020-02-07 14:51:31 +00:00
refreshLibrary ( ) ;
2019-11-06 17:25:02 +00:00
} ) . catch ( err => {
showToast ( "Customise failed, " + err , "error" ) ;
icon . classList . remove ( "loading" ) ;
icon . classList . add ( "icon-menu" ) ;
} ) ;
}
2020-02-07 14:51:31 +00:00
} else if ( icon . classList . contains ( "icon-delete" ) ) {
// Remove app
2019-11-03 11:13:21 +00:00
icon . classList . remove ( "icon-delete" ) ;
icon . classList . add ( "loading" ) ;
removeApp ( app ) ;
2020-02-07 14:51:31 +00:00
} else if ( icon . classList . contains ( "icon-refresh" ) ) {
// Update app
icon . classList . remove ( "icon-refresh" ) ;
icon . classList . add ( "loading" ) ;
updateApp ( app ) ;
2020-02-07 17:16:45 +00:00
} else if ( icon . classList . contains ( "icon-download" ) ) {
handleAppInterface ( app ) ;
2019-11-03 11:13:21 +00:00
}
2019-10-30 17:33:58 +00:00
} ) ;
} ) ;
}
refreshLibrary ( ) ;
// =========================================== My Apps
2019-11-03 11:13:21 +00:00
function removeApp ( app ) {
2019-11-06 15:53:38 +00:00
return showPrompt ( "Delete" , "Really remove '" + app . name + "'?" ) . then ( ( ) => {
2020-02-07 14:51:31 +00:00
return Comms . removeApp ( app ) ;
} ) . then ( ( ) => {
appsInstalled = appsInstalled . filter ( a => a . id != app . id ) ;
showToast ( app . name + " removed successfully" , "success" ) ;
refreshMyApps ( ) ;
refreshLibrary ( ) ;
} , err => {
showToast ( app . name + " removal failed, " + err , "error" ) ;
} ) ;
}
function updateApp ( app ) {
2020-02-27 15:52:20 +00:00
showProgress ( ` Upgrading ${ app . name } ` , undefined , "sticky" ) ;
2020-02-07 14:51:31 +00:00
Comms . removeApp ( app ) . then ( ( ) => {
showToast ( app . name + " removed successfully. Updating..." , ) ;
appsInstalled = appsInstalled . filter ( a => a . id != app . id ) ;
return Comms . uploadApp ( app ) ;
} ) . then ( ( appJSON ) => {
2020-02-27 15:52:20 +00:00
hideProgress ( "sticky" ) ;
2020-02-07 14:51:31 +00:00
if ( appJSON ) appsInstalled . push ( appJSON ) ;
showToast ( app . name + " Updated!" , "success" ) ;
refreshMyApps ( ) ;
refreshLibrary ( ) ;
} , err => {
2020-02-27 15:52:20 +00:00
hideProgress ( "sticky" ) ;
2020-02-07 14:51:31 +00:00
showToast ( app . name + " update failed, " + err , "error" ) ;
2019-11-03 11:13:21 +00:00
} ) ;
}
2020-02-07 17:16:45 +00:00
2019-10-30 17:33:58 +00:00
function appNameToApp ( appName ) {
2019-11-03 11:13:21 +00:00
var app = appJSON . find ( app => app . id == appName ) ;
2019-10-30 17:33:58 +00:00
if ( app ) return app ;
2019-11-03 11:13:21 +00:00
/ * I f a p p n o t k n o w n , a d d j u s t o n e f i l e
which is the JSON - so we ' ll remove it from
the menu but may not get rid of all files . * /
return { id : appName ,
2019-10-30 17:33:58 +00:00
name : "Unknown app " + appName ,
2019-11-17 22:39:31 +00:00
icon : "../unknown.png" ,
2019-10-30 17:33:58 +00:00
description : "Unknown app" ,
2019-11-03 11:13:21 +00:00
storage : [ { name : "+" + appName } ] ,
2019-10-30 17:33:58 +00:00
unknown : true ,
} ;
}
2019-12-24 13:47:02 +00:00
function showLoadingIndicator ( id ) {
var panelbody = document . querySelector ( ` # ${ id } .panel-body ` ) ;
var tab = document . querySelector ( ` #tab- ${ id } a ` ) ;
2019-10-30 17:33:58 +00:00
// set badge up top
tab . classList . add ( "badge" ) ;
tab . setAttribute ( "data-badge" , "" ) ;
// Loading indicator
2019-11-03 11:50:00 +00:00
panelbody . innerHTML = '<div class="tile column col-12"><div class="tile-content" style="min-height:48px;"><div class="loading loading-lg"></div></div></div>' ;
2019-11-03 11:13:21 +00:00
}
function refreshMyApps ( ) {
var panelbody = document . querySelector ( "#myappscontainer .panel-body" ) ;
var tab = document . querySelector ( "#tab-myappscontainer a" ) ;
tab . setAttribute ( "data-badge" , appsInstalled . length ) ;
2020-02-07 17:16:45 +00:00
panelbody . innerHTML = appsInstalled . map ( appInstalled => {
var app = appNameToApp ( appInstalled . id ) ;
var version = getVersionInfo ( app , appInstalled ) ;
2019-12-05 14:48:56 +00:00
return ` <div class="tile column col-6 col-sm-12 col-xs-12">
2019-11-03 11:13:21 +00:00
< div class = "tile-icon" >
2019-11-17 22:39:31 +00:00
< figure class = "avatar" > < img src = "apps/${app.icon?`${app.id}/${app.icon}`:" unknown . png "}" alt = "${escapeHtml(app.name)}" > < / f i g u r e >
2019-10-30 17:33:58 +00:00
< / d i v >
2019-11-03 11:13:21 +00:00
< div class = "tile-content" >
2020-02-07 14:51:31 +00:00
< p class = "tile-title text-bold" > $ { escapeHtml ( app . name ) } < small > ( $ { version . text } ) < / s m a l l > < / p >
2019-11-03 11:13:21 +00:00
< p class = "tile-subtitle" > $ { escapeHtml ( app . description ) } < / p >
< / d i v >
< div class = "tile-action" >
2020-02-07 17:16:45 +00:00
< button class = "btn btn-link btn-action btn-lg ${(appInstalled&&app.interface)?" ":" d - hide "}" appid = "${app.id}" title = "Download data from app" > < i class = "icon icon-download" > < / i > < / b u t t o n >
2020-02-07 14:51:31 +00:00
< button class = "btn btn-link btn-action btn-lg ${version.canUpdate?'':'d-hide'}" appid = "${app.id}" title = "Update App" > < i class = "icon icon-refresh" > < / i > < / b u t t o n >
< button class = "btn btn-link btn-action btn-lg" appid = "${app.id}" title = "Remove App" > < i class = "icon icon-delete" > < / i > < / b u t t o n >
2019-11-03 11:13:21 +00:00
< / d i v >
< / d i v >
2019-12-05 14:48:56 +00:00
` }).join("");
2019-11-03 11:13:21 +00:00
htmlToArray ( panelbody . getElementsByTagName ( "button" ) ) . forEach ( button => {
button . addEventListener ( "click" , event => {
2020-02-07 14:51:31 +00:00
var button = event . currentTarget ;
var icon = button . firstChild ;
var appid = button . getAttribute ( "appid" ) ;
2019-11-03 11:13:21 +00:00
var app = appNameToApp ( appid ) ;
2020-02-07 14:51:31 +00:00
if ( ! app ) throw new Error ( "App " + appid + " not found" ) ;
// check icon to figure out what we should do
if ( icon . classList . contains ( "icon-delete" ) ) removeApp ( app ) ;
if ( icon . classList . contains ( "icon-refresh" ) ) updateApp ( app ) ;
2020-02-07 17:16:45 +00:00
if ( icon . classList . contains ( "icon-download" ) ) handleAppInterface ( app )
2019-10-30 17:33:58 +00:00
} ) ;
} ) ;
}
2019-11-03 11:13:21 +00:00
function getInstalledApps ( ) {
2019-12-24 13:47:02 +00:00
showLoadingIndicator ( "myappscontainer" ) ;
2020-02-27 15:52:20 +00:00
showProgress ( ` Getting app list... ` , undefined , "sticky" ) ;
2019-12-24 13:47:02 +00:00
// Get apps and files
return Comms . getInstalledApps ( )
. then ( appJSON => {
2020-02-27 15:52:20 +00:00
hideProgress ( "sticky" ) ;
2019-12-24 13:47:02 +00:00
appsInstalled = appJSON ;
refreshMyApps ( ) ;
refreshLibrary ( ) ;
} )
2020-02-27 15:52:20 +00:00
. then ( ( ) => handleConnectionChange ( true ) )
. catch ( err => {
hideProgress ( "sticky" ) ;
return Promise . reject ( ) ;
} ) ;
2019-11-03 11:13:21 +00:00
}
2019-11-12 10:56:31 +00:00
var connectMyDeviceBtn = document . getElementById ( "connectmydevice" ) ;
function handleConnectionChange ( connected ) {
2019-11-12 14:41:03 +00:00
connectMyDeviceBtn . textContent = connected ? 'Disconnect' : 'Connect' ;
connectMyDeviceBtn . classList . toggle ( 'is-connected' , connected ) ;
2019-11-12 10:56:31 +00:00
}
2019-12-24 13:47:02 +00:00
htmlToArray ( document . querySelectorAll ( ".btn.refresh" ) ) . map ( button => button . addEventListener ( "click" , ( ) => {
2019-11-12 14:41:03 +00:00
getInstalledApps ( ) . catch ( err => {
showToast ( "Getting app list failed, " + err , "error" ) ;
} ) ;
2019-12-24 13:47:02 +00:00
} ) ) ;
2019-11-12 14:41:03 +00:00
connectMyDeviceBtn . addEventListener ( "click" , ( ) => {
if ( connectMyDeviceBtn . classList . contains ( 'is-connected' ) ) {
Comms . disconnectDevice ( ) ;
} else {
getInstalledApps ( ) . catch ( err => {
showToast ( "Device connection failed, " + err , "error" ) ;
} ) ;
}
} ) ;
2019-11-12 10:56:31 +00:00
Comms . watchConnectionChange ( handleConnectionChange ) ;
2019-11-07 09:26:46 +00:00
2019-11-13 17:27:22 +00:00
var filtersContainer = document . querySelector ( "#librarycontainer .filter-nav" ) ;
filtersContainer . addEventListener ( 'click' , ( { target } ) => {
if ( ! target . hasAttribute ( 'filterid' ) ) return ;
if ( target . classList . contains ( 'active' ) ) return ;
2019-11-17 22:39:31 +00:00
2019-11-13 17:27:22 +00:00
activeFilter = target . getAttribute ( 'filterid' ) ;
filtersContainer . querySelector ( '.active' ) . classList . remove ( 'active' ) ;
target . classList . add ( 'active' ) ;
refreshLibrary ( ) ;
} ) ;
var librarySearchInput = document . querySelector ( "#searchform input" ) ;
2019-11-17 22:39:31 +00:00
librarySearchInput . addEventListener ( 'input' , evt => {
2019-11-13 17:27:22 +00:00
currentSearch = evt . target . value . toLowerCase ( ) ;
2019-11-17 22:39:31 +00:00
refreshLibrary ( ) ;
2019-11-13 17:27:22 +00:00
} ) ;
2019-11-07 09:26:46 +00:00
// =========================================== About
document . getElementById ( "settime" ) . addEventListener ( "click" , event => {
Comms . setTime ( ) . then ( ( ) => {
showToast ( "Time set successfully" , "success" ) ;
} , err => {
showToast ( "Error setting time, " + err , "error" ) ;
} ) ;
} ) ;
document . getElementById ( "removeall" ) . addEventListener ( "click" , event => {
showPrompt ( "Remove All" , "Really remove all apps?" ) . then ( ( ) => {
2020-02-27 15:52:20 +00:00
showProgress ( "Removing all apps" , "animate" , "sticky" ) ;
2020-02-04 16:30:31 +00:00
return Comms . removeAllApps ( ) ;
} ) . then ( ( ) => {
2020-02-27 15:52:20 +00:00
hideProgress ( "sticky" ) ;
2020-02-04 16:30:31 +00:00
appsInstalled = [ ] ;
showToast ( "All apps removed" , "success" ) ;
return getInstalledApps ( ) ;
} ) . catch ( err => {
2020-02-27 15:52:20 +00:00
hideProgress ( "sticky" ) ;
2020-02-04 16:30:31 +00:00
showToast ( "App removal failed, " + err , "error" ) ;
2019-11-07 09:26:46 +00:00
} ) ;
} ) ;
2020-02-04 16:13:06 +00:00
// Install all default apps in one go
document . getElementById ( "installdefault" ) . addEventListener ( "click" , event => {
2020-02-04 16:30:31 +00:00
var defaultApps , appCount ;
2020-02-04 16:13:06 +00:00
httpGet ( "defaultapps.json" ) . then ( json => {
defaultApps = JSON . parse ( json ) ;
defaultApps = defaultApps . map ( appid => appJSON . find ( app => app . id == appid ) ) ;
if ( defaultApps . some ( x => x === undefined ) )
throw "Not all apps found" ;
2020-02-04 16:30:31 +00:00
appCount = defaultApps . length ;
2020-02-04 16:13:06 +00:00
return showPrompt ( "Install Defaults" , "Remove everything and install default apps?" ) ;
} ) . then ( ( ) => {
2020-02-27 15:52:20 +00:00
showProgress ( "Removing all apps" , "animate" , "sticky" ) ;
2020-02-04 16:13:06 +00:00
return Comms . removeAllApps ( ) ;
} ) . then ( ( ) => {
2020-02-27 15:52:20 +00:00
hideProgress ( "sticky" ) ;
2020-02-04 16:30:31 +00:00
appsInstalled = [ ] ;
showToast ( ` Existing apps removed. Installing ${ appCount } apps... ` ) ;
2020-02-27 15:52:20 +00:00
return new Promise ( ( resolve , reject ) => {
2020-02-04 16:13:06 +00:00
function upload ( ) {
var app = defaultApps . shift ( ) ;
if ( app === undefined ) return resolve ( ) ;
2020-02-27 15:52:20 +00:00
showProgress ( ` ${ app . name } ( ${ appCount - defaultApps . length } / ${ appCount } ) ` , undefined , "sticky" ) ;
2020-02-04 16:13:06 +00:00
Comms . uploadApp ( app ) . then ( ( appJSON ) => {
2020-02-27 15:52:20 +00:00
hideProgress ( "sticky" ) ;
2020-02-04 16:13:06 +00:00
if ( appJSON ) appsInstalled . push ( appJSON ) ;
2020-02-04 16:30:31 +00:00
showToast ( ` ( ${ appCount - defaultApps . length } / ${ appCount } ) ${ app . name } Uploaded ` ) ;
2020-02-04 16:13:06 +00:00
upload ( ) ;
2020-02-27 15:52:20 +00:00
} ) . catch ( function ( ) {
hideProgress ( "sticky" ) ;
reject ( )
2020-02-04 16:13:06 +00:00
} ) ;
}
upload ( ) ;
} ) ;
2020-02-13 08:18:32 +00:00
} ) . then ( ( ) => {
return Comms . setTime ( ) ;
2020-02-04 16:13:06 +00:00
} ) . then ( ( ) => {
showToast ( "Default apps successfully installed!" , "success" ) ;
2020-02-04 16:30:31 +00:00
return getInstalledApps ( ) ;
2020-02-04 16:13:06 +00:00
} ) . catch ( err => {
showToast ( "App Install failed, " + err , "error" ) ;
} ) ;
} ) ;