@@ -2,9 +2,8 @@ import type { Action, ThunkDispatch } from "@reduxjs/toolkit";
22import { dialog } from "electron" ;
33import { chunk } from "lodash-es" ;
44
5- import { type AppInfo , appSlice } from "../reducers/app " ;
5+ import { type AppInfo , targetSlice } from "../reducers/target " ;
66import { type PageInfo , sessionSlice } from "../reducers/session" ;
7- import { targetSlice } from "../reducers/target" ;
87
98import type { State } from "./store" ;
109import type { RemoteDeviceOptions } from "./targets/types" ;
@@ -22,24 +21,25 @@ export const init: ThunkActionCreator = () => async (dispatch, getState) => {
2221 // Initialize local targets
2322 await targetRegistry . initializeLocalTargets ( ) ;
2423
25- // Register all targets in Redux store
24+ // Register all targets in Redux store and discover their apps
2625 const targets = targetRegistry . getAllInfo ( ) ;
27- targets . forEach ( ( target ) => {
28- dispatch ( targetSlice . actions . registered ( target ) ) ;
29- } ) ;
26+ for ( const targetInfo of targets ) {
27+ dispatch ( targetSlice . actions . registered ( targetInfo ) ) ;
3028
31- // Discover apps from all targets
32- const allApps : AppInfo [ ] = [ ] ;
33- for ( const target of targetRegistry . getAll ( ) ) {
34- const appsResult = await target . discoverApps ( ) ;
35- if ( appsResult . ok ) {
36- allApps . push ( ...appsResult . val ) ;
37- dispatch ( targetSlice . actions . discoveryCompleted ( target . id ) ) ;
29+ // Discover apps for this target
30+ const target = targetRegistry . getById ( targetInfo . id ) ;
31+ if ( target ) {
32+ const appsResult = await target . discoverApps ( ) ;
33+ if ( appsResult . ok ) {
34+ dispatch ( targetSlice . actions . appsUpdated ( {
35+ targetId : target . id ,
36+ apps : appsResult . val ,
37+ } ) ) ;
38+ dispatch ( targetSlice . actions . discoveryCompleted ( target . id ) ) ;
39+ }
3840 }
3941 }
4042
41- dispatch ( appSlice . actions . found ( allApps ) ) ;
42-
4343 // Timer for polling debug endpoints
4444 setInterval ( ( ) => {
4545 void ( async ( ) => {
@@ -78,79 +78,80 @@ export const init: ThunkActionCreator = () => async (dispatch, getState) => {
7878 } , 3000 ) ;
7979} ;
8080
81- export const debug : ThunkActionCreator < AppInfo > = ( app ) => async ( dispatch ) => {
82- try {
83- // Get the target adapter for this app
84- const target = targetRegistry . getById ( app . targetId ) ;
85- if ( ! target ) {
86- throw new Error ( `Target ${ app . targetId } not found` ) ;
87- }
88-
89- // Launch the app using the target adapter
90- const connectionResult = await target . launch ( app , { } ) ;
81+ export const debug : ThunkActionCreator < { targetId : string ; app : AppInfo } >
82+ = ( { targetId, app } ) => async ( dispatch ) => {
83+ try {
84+ // Get the target adapter
85+ const target = targetRegistry . getById ( targetId ) ;
86+ if ( ! target ) {
87+ throw new Error ( `Target ${ targetId } not found` ) ;
88+ }
9189
92- if ( ! connectionResult . ok ) {
93- throw connectionResult . val ;
94- }
90+ // Launch the app using the target adapter
91+ const connectionResult = await target . launch ( app , { } ) ;
9592
96- const connection = connectionResult . val ;
97- const sessionId = connection . connectionId ;
98-
99- // Determine connection type
100- let connectionType : "local-process" | "remote-adb" | "remote-websocket" ;
101- if ( target . type === "local" ) {
102- connectionType = "local-process" ;
103- } else if ( target . id . startsWith ( "remote-adb" ) ) {
104- connectionType = "remote-adb" ;
105- } else {
106- connectionType = "remote-websocket" ;
107- }
93+ if ( ! connectionResult . ok ) {
94+ throw connectionResult . val ;
95+ }
10896
109- // Add session to Redux store
110- dispatch (
111- sessionSlice . actions . added ( {
112- sessionId,
113- appId : app . id ,
114- targetId : app . targetId ,
115- connection : {
116- type : connectionType ,
117- nodePort : connection . debugPorts . node ,
118- windowPort : connection . debugPorts . renderer ,
119- websocketUrl : connection . debugPorts . websocket ,
120- } ,
121- } ) ,
122- ) ;
123-
124- // Handle local process events
125- if ( connection . processHandle ) {
126- const sp = connection . processHandle ;
127-
128- sp . on ( "error" , ( err : Error ) => {
129- dialog . showErrorBox ( `Error: ${ app . name } ` , err . message ) ;
130- } ) ;
97+ const connection = connectionResult . val ;
98+ const sessionId = connection . connectionId ;
99+
100+ // Determine connection type
101+ let connectionType : "local-process" | "remote-adb" | "remote-websocket" ;
102+ if ( target . type === "local" ) {
103+ connectionType = "local-process" ;
104+ } else if ( target . id . startsWith ( "remote-adb" ) ) {
105+ connectionType = "remote-adb" ;
106+ } else {
107+ connectionType = "remote-websocket" ;
108+ }
131109
132- sp . on ( "close" , ( ) => {
133- dispatch ( sessionSlice . actions . removed ( sessionId ) ) ;
134- void connection . cleanup ( ) ;
135- } ) ;
110+ // Add session to Redux store
111+ dispatch (
112+ sessionSlice . actions . added ( {
113+ sessionId,
114+ targetId,
115+ appId : app . id ,
116+ connection : {
117+ type : connectionType ,
118+ nodePort : connection . debugPorts . node ,
119+ windowPort : connection . debugPorts . renderer ,
120+ websocketUrl : connection . debugPorts . websocket ,
121+ } ,
122+ } ) ,
123+ ) ;
136124
137- const handleStdout = ( chunk : Buffer ) => {
138- dispatch (
139- sessionSlice . actions . logAppended ( {
140- sessionId,
141- content : chunk . toString ( ) ,
142- } ) ,
143- ) ;
144- } ;
145-
146- sp . stdout ?. on ( "data" , handleStdout ) ;
147- sp . stderr ?. on ( "data" , handleStdout ) ;
125+ // Handle local process events
126+ if ( connection . processHandle ) {
127+ const sp = connection . processHandle ;
128+
129+ sp . on ( "error" , ( err : Error ) => {
130+ dialog . showErrorBox ( `Error: ${ app . name } ` , err . message ) ;
131+ } ) ;
132+
133+ sp . on ( "close" , ( ) => {
134+ dispatch ( sessionSlice . actions . removed ( sessionId ) ) ;
135+ void connection . cleanup ( ) ;
136+ } ) ;
137+
138+ const handleStdout = ( chunk : Buffer ) => {
139+ dispatch (
140+ sessionSlice . actions . logAppended ( {
141+ sessionId,
142+ content : chunk . toString ( ) ,
143+ } ) ,
144+ ) ;
145+ } ;
146+
147+ sp . stdout ?. on ( "data" , handleStdout ) ;
148+ sp . stderr ?. on ( "data" , handleStdout ) ;
149+ }
150+ } catch ( error ) {
151+ const errorMessage = error instanceof Error ? error . message : String ( error ) ;
152+ dialog . showErrorBox ( `Error: ${ app . name } ` , errorMessage ) ;
148153 }
149- } catch ( error ) {
150- const errorMessage = error instanceof Error ? error . message : String ( error ) ;
151- dialog . showErrorBox ( `Error: ${ app . name } ` , errorMessage ) ;
152- }
153- } ;
154+ } ;
154155
155156export const debugPath : ThunkActionCreator < string > = ( ) => async ( ) => {
156157 // TODO:
@@ -181,9 +182,10 @@ export const addRemoteDevice: ThunkActionCreator<RemoteDeviceOptions>
181182 // Discover apps from the new target
182183 const appsResult = await target . discoverApps ( ) ;
183184 if ( appsResult . ok ) {
184- // eslint-disable-next-line @typescript-eslint/no-explicit-any
185- const existingApps = Object . values ( ( dispatch as any ) . getState ?.( ) ?. app ?? { } ) ;
186- dispatch ( appSlice . actions . found ( [ ...( existingApps as AppInfo [ ] ) , ...appsResult . val ] ) ) ;
185+ dispatch ( targetSlice . actions . appsUpdated ( {
186+ targetId : target . id ,
187+ apps : appsResult . val ,
188+ } ) ) ;
187189 dispatch ( targetSlice . actions . discoveryCompleted ( target . id ) ) ;
188190 }
189191
@@ -200,17 +202,11 @@ export const addRemoteDevice: ThunkActionCreator<RemoteDeviceOptions>
200202
201203export const removeDevice : ThunkActionCreator < string > = ( targetId ) => ( dispatch ) => {
202204 try {
203- // Remove device from registry
205+ // Remove target from registry
204206 targetRegistry . unregister ( targetId ) ;
205207
206- // Remove from Redux store
208+ // Remove from Redux store (apps will be removed automatically as they're nested)
207209 dispatch ( targetSlice . actions . unregistered ( targetId ) ) ;
208-
209- // Remove all apps from this device
210- // eslint-disable-next-line @typescript-eslint/no-explicit-any
211- const existingApps = Object . values ( ( dispatch as any ) . getState ?.( ) ?. app ?? { } ) ;
212- const filteredApps = ( existingApps as AppInfo [ ] ) . filter ( ( app ) => app . targetId !== targetId ) ;
213- dispatch ( appSlice . actions . found ( filteredApps ) ) ;
214210 } catch ( error ) {
215211 const errorMessage = error instanceof Error ? error . message : String ( error ) ;
216212 dialog . showErrorBox ( "Remove Device Error" , errorMessage ) ;
@@ -227,13 +223,10 @@ export const refreshDeviceApps: ThunkActionCreator<string> = (targetId) => async
227223 // Discover apps from the target
228224 const appsResult = await target . discoverApps ( ) ;
229225 if ( appsResult . ok ) {
230- // Get existing apps from other devices
231- // eslint-disable-next-line @typescript-eslint/no-explicit-any
232- const existingApps = Object . values ( ( dispatch as any ) . getState ?.( ) ?. app ?? { } ) ;
233- const otherDeviceApps = ( existingApps as AppInfo [ ] ) . filter ( ( app ) => app . targetId !== targetId ) ;
234-
235- // Merge with new apps from this device
236- dispatch ( appSlice . actions . found ( [ ...otherDeviceApps , ...appsResult . val ] ) ) ;
226+ dispatch ( targetSlice . actions . appsUpdated ( {
227+ targetId,
228+ apps : appsResult . val ,
229+ } ) ) ;
237230 dispatch ( targetSlice . actions . discoveryCompleted ( targetId ) ) ;
238231 }
239232 } catch ( error ) {
0 commit comments