@@ -112,7 +112,11 @@ function installFromFiles() {
112112 tags : metadata . tags ,
113113 sortorder : metadata . sortorder ,
114114 storage : metadata . storage ,
115- data : metadata . data || [ ] // NOTE: data[] files are NOT uploaded unless they have url/content
115+ data : metadata . data || [ ] , // NOTE: data[] files are NOT uploaded unless they have url/content
116+ dependencies : metadata . dependencies , // Dependencies on other apps/modules/widgets
117+ provides_modules : metadata . provides_modules , // Modules this app provides
118+ provides_widgets : metadata . provides_widgets , // Widgets this app provides
119+ provides_features : metadata . provides_features // Features this app provides
116120 } ;
117121
118122 // SOURCE: core/js/appinfo.js getFiles() - filter by device support
@@ -148,9 +152,15 @@ function installFromFiles() {
148152
149153 // Build breakdown string (omit data if zero)
150154 var breakdown = `${ storageTransferCount } storage file(s)` + ( dataTransferCount > 0 ? ` and ${ dataTransferCount } data file(s)` : "" ) ;
155+ // Dependency note (if any declared in metadata)
156+ var dependencyNote = "" ;
157+ if ( metadata && metadata . dependencies && typeof metadata . dependencies === 'object' && Object . keys ( metadata . dependencies ) . length ) {
158+ var depNames = Object . keys ( metadata . dependencies ) . join ( ", " ) ;
159+ dependencyNote = `\n\nRequires dependencies: ${ depNames } . We'll attempt to install these if missing.` ;
160+ }
151161
152162 showPrompt ( "Install App from Files" ,
153- `Install app "${ app . name } " (${ app . id } ) version ${ app . version } ?\n\nWill transfer ${ breakdown } from metadata.\n\nThis will delete the existing version if installed.`
163+ `Install app "${ app . name } " (${ app . id } ) version ${ app . version } ?\n\nWill transfer ${ breakdown } from metadata.${ dependencyNote } \n\nThis will delete the existing version if installed.`
154164 ) . then ( ( ) => {
155165 Progress . show ( { title :`Reading files...` } ) ;
156166
@@ -249,6 +259,10 @@ function installFromFiles() {
249259 type : app . type ,
250260 tags : app . tags ,
251261 sortorder : app . sortorder ,
262+ dependencies : app . dependencies , // Pass through dependencies for checking
263+ provides_modules : app . provides_modules ,
264+ provides_widgets : app . provides_widgets ,
265+ provides_features : app . provides_features ,
252266 storage : app . storage . map ( storageEntry => {
253267 var url = storageEntry . url || storageEntry . name ;
254268 var content = sourceContents [ url ] ;
@@ -291,7 +305,7 @@ function installFromFiles() {
291305
292306 // SOURCE: core/js/index.js updateApp() lines 963-978
293307 // Check for noOverwrite files that exist on device
294- var noOverwriteChecks = Promise . resolve ( ) ;
308+ var noOverwriteChecks = Promise . resolve ( appForUpload ) ;
295309 var filesToCheck = appForUpload . storage . filter ( f => f . noOverwrite ) ;
296310
297311 if ( filesToCheck . length > 0 ) {
@@ -307,7 +321,7 @@ function installFromFiles() {
307321 Comms . eval ( `[${ checkCmd } ]` , ( result , err ) => {
308322 if ( err ) {
309323 console . warn ( 'Error checking noOverwrite files:' , err ) ;
310- resolveCheck ( ) ; // Continue anyway
324+ resolveCheck ( appForUpload ) ; // Continue anyway, pass appForUpload through
311325 return ;
312326 }
313327 try {
@@ -322,10 +336,10 @@ function installFromFiles() {
322336 }
323337 }
324338 } ) ;
325- resolveCheck ( ) ;
339+ resolveCheck ( appForUpload ) ; // Pass appForUpload through the promise chain
326340 } catch ( e ) {
327341 console . warn ( 'Error parsing noOverwrite check results:' , e ) ;
328- resolveCheck ( ) ; // Continue anyway
342+ resolveCheck ( appForUpload ) ; // Continue anyway, pass appForUpload through
329343 }
330344 } ) ;
331345 } ) ;
@@ -348,6 +362,33 @@ function installFromFiles() {
348362 // This matches the updateApp pattern exactly
349363 return Comms . removeApp ( remove , { containsFileList :true } ) . then ( ( ) => appForUpload ) ;
350364 } ) ;
365+ } ) . then ( appForUpload => {
366+ // SOURCE: core/js/index.js uploadApp() line 839
367+ // Check and install dependencies before uploading
368+ Progress . hide ( { sticky :true } ) ;
369+ Progress . show ( { title :`Checking dependencies...` } ) ;
370+ return AppInfo . checkDependencies ( appForUpload , device , {
371+ apps : appJSON ,
372+ device : device ,
373+ language : LANGUAGE ,
374+ needsApp : ( depApp , uploadOptions ) => Comms . uploadApp ( depApp , uploadOptions ) ,
375+ showQuery : ( msg , appToRemove ) => {
376+ return showPrompt ( "App Dependencies" ,
377+ `${ msg } . What would you like to do?` ,
378+ { options :[ "Replace" , "Keep Both" , "Cancel" ] } )
379+ . then ( choice => {
380+ if ( choice === "Replace" ) {
381+ return Comms . removeApp ( appToRemove ) . then ( ( ) => {
382+ device . appsInstalled = device . appsInstalled . filter ( a => a . id != appToRemove . id ) ;
383+ } ) ;
384+ } else if ( choice === "Keep Both" ) {
385+ return Promise . resolve ( ) ;
386+ } else {
387+ return Promise . reject ( "User cancelled" ) ;
388+ }
389+ } ) ;
390+ }
391+ } ) . then ( ( ) => appForUpload ) ;
351392 } ) . then ( appForUpload => {
352393 // SOURCE: core/js/index.js uploadApp() line 840 and updateApp() line 983
353394 // Upload using the standard pipeline
@@ -363,9 +404,17 @@ function installFromFiles() {
363404 // - Progress updates
364405 // - Final success message via showUploadFinished()
365406 return Comms . uploadApp ( appForUpload , { device : device , language : LANGUAGE } ) ;
366- } ) . then ( ( ) => {
407+ } ) . then ( ( appJSON ) => {
367408 Progress . hide ( { sticky :true } ) ;
409+ if ( appJSON ) {
410+ // Keep device.appsInstalled in sync like the normal loader
411+ device . appsInstalled = device . appsInstalled . filter ( a => a . id != app . id ) ;
412+ device . appsInstalled . push ( appJSON ) ;
413+ }
368414 showToast ( `App "${ app . name } " installed successfully!` , 'success' ) ;
415+ // Refresh UI panels like normal loader flows
416+ if ( typeof refreshMyApps === 'function' ) refreshMyApps ( ) ;
417+ if ( typeof refreshLibrary === 'function' ) refreshLibrary ( ) ;
369418 resolve ( ) ;
370419 } ) . catch ( err => {
371420 Progress . hide ( { sticky :true } ) ;
0 commit comments