@@ -26,6 +26,7 @@ type AppServiceInterface interface {
2626 BuildApp ()
2727}
2828
29+ // AppService manages the application state, Homebrew integration, and UI components.
2930type AppService struct {
3031 app * tview.Application
3132 theme * theme.Theme
@@ -37,12 +38,13 @@ type AppService struct {
3738 showOnlyOutdated bool
3839 brewVersion string
3940
40- BrewService BrewServiceInterface
41- SelfUpdateService SelfUpdateServiceInterface
42- IOService IOServiceInterface
41+ brewService BrewServiceInterface
42+ selfUpdateService SelfUpdateServiceInterface
43+ ioService IOServiceInterface
4344}
4445
45- func NewAppService () AppServiceInterface {
46+ // NewAppService creates a new instance of AppService with initialized components.
47+ var NewAppService = func () AppServiceInterface {
4648 app := tview .NewApplication ()
4749 themeService := theme .NewTheme ()
4850 layout := ui .NewLayout (themeService )
@@ -60,36 +62,38 @@ func NewAppService() AppServiceInterface {
6062 }
6163
6264 // Initialize services
63- s .IOService = NewIOService (s )
64- s .BrewService = NewBrewService ()
65- s .SelfUpdateService = NewSelfUpdateService ()
65+ s .ioService = NewIOService (s )
66+ s .brewService = NewBrewService ()
67+ s .selfUpdateService = NewSelfUpdateService ()
6668
6769 return s
6870}
6971
7072func (s * AppService ) GetApp () * tview.Application { return s .app }
7173func (s * AppService ) GetLayout () ui.LayoutInterface { return s .layout }
7274
75+ // Boot initializes the application by setting up Homebrew and loading formulae data.
7376func (s * AppService ) Boot () (err error ) {
74- if s .brewVersion , err = s .BrewService .GetBrewVersion (); err != nil {
77+ if s .brewVersion , err = s .brewService .GetBrewVersion (); err != nil {
7578 // This error is critical, as we need Homebrew to function
7679 return fmt .Errorf ("failed to get Homebrew version: %v" , err )
7780 }
7881
7982 // Download and parse Homebrew formulae data
80- if err = s .BrewService .SetupData (false ); err != nil {
83+ if err = s .brewService .SetupData (false ); err != nil {
8184 return fmt .Errorf ("failed to load Homebrew formulae: %v" , err )
8285 }
8386
84- s .packages = s .BrewService .GetFormulae ()
87+ // Initialize packages and filteredPackages
88+ s .packages = s .brewService .GetFormulae ()
8589 * s .filteredPackages = * s .packages
86-
8790 return nil
8891}
8992
93+ // updateHomeBrew updates the Homebrew formulae and refreshes the results in the UI.
9094func (s * AppService ) updateHomeBrew () {
9195 s .layout .GetNotifier ().ShowWarning ("Updating Homebrew formulae..." )
92- if err := s .BrewService .UpdateHomebrew (); err != nil {
96+ if err := s .brewService .UpdateHomebrew (); err != nil {
9397 s .layout .GetNotifier ().ShowError ("Could not update Homebrew formulae" )
9498 return
9599 }
@@ -98,6 +102,7 @@ func (s *AppService) updateHomeBrew() {
98102 s .forceRefreshResults ()
99103}
100104
105+ // search filters the packages based on the search text and the current filter state.
101106func (s * AppService ) search (searchText string , scrollToTop bool ) {
102107 var filteredList []models.Formula
103108 uniquePackages := make (map [string ]bool )
@@ -154,25 +159,18 @@ func (s *AppService) search(searchText string, scrollToTop bool) {
154159 s .setResults (s .filteredPackages , scrollToTop )
155160}
156161
157- func (s * AppService ) setDetails (info * models.Formula ) {
158- if info == nil {
159- s .layout .GetDetails ().SetContent (nil )
160- return
161- }
162-
163- s .layout .GetDetails ().SetContent (info )
164- }
165-
162+ // forceRefreshResults forces a refresh of the Homebrew formulae data and updates the results in the UI.
166163func (s * AppService ) forceRefreshResults () {
167- _ = s .BrewService .SetupData (true )
168- s .packages = s .BrewService .GetFormulae ()
164+ _ = s .brewService .SetupData (true )
165+ s .packages = s .brewService .GetFormulae ()
169166 * s .filteredPackages = * s .packages
170167
171168 s .app .QueueUpdateDraw (func () {
172169 s .search (s .layout .GetSearch ().Field ().GetText (), false )
173170 })
174171}
175172
173+ // setResults updates the results table with the provided data and optionally scrolls to the top.
176174func (s * AppService ) setResults (data * []models.Formula , scrollToTop bool ) {
177175 s .layout .GetTable ().Clear ()
178176 s .layout .GetTable ().SetTableHeaders ("Name" , "Version" , "Description" , "↓ (90d)" )
@@ -211,56 +209,62 @@ func (s *AppService) setResults(data *[]models.Formula, scrollToTop bool) {
211209 if scrollToTop {
212210 s .layout .GetTable ().View ().Select (1 , 0 )
213211 s .layout .GetTable ().View ().ScrollToBeginning ()
214- s .setDetails (& (* data )[0 ])
212+ s .layout . GetDetails (). SetContent (& (* data )[0 ])
215213 }
216214
217215 // Update the filter counter
218216 s .layout .GetSearch ().UpdateCounter (len (* s .packages ), len (* s .filteredPackages ))
219217 return
220218 }
221219
222- s .setDetails ( nil )
220+ s .layout . GetDetails (). SetContent ( nil ) // Clear details if no results
223221}
224222
223+ // BuildApp builds the application layout, sets up event handlers, and initializes the UI components.
225224func (s * AppService ) BuildApp () {
226225 // Build the layout
227226 s .layout .Setup ()
228227 s .layout .GetHeader ().Update (AppName , AppVersion , s .brewVersion )
229228
230229 // Evaluate if there is a new version available
230+ // This is done in a goroutine to avoid blocking the UI during startup
231+ // In the future, this could be replaced with a more sophisticated update check, and update
232+ // the user if a new version is available instantly instead of waiting for the next app start
231233 go func () {
232234 ctx , cancel := context .WithTimeout (context .Background (), 60 * time .Second )
233235 defer cancel ()
234236
235- if latestVersion , err := s .SelfUpdateService .CheckForUpdates (ctx ); err == nil && latestVersion != AppVersion {
237+ if latestVersion , err := s .selfUpdateService .CheckForUpdates (ctx ); err == nil && latestVersion != AppVersion {
236238 s .app .QueueUpdateDraw (func () {
237239 AppVersion = fmt .Sprintf ("%s ([orange]New Version Available: %s[-])" , AppVersion , latestVersion )
238240 s .layout .GetHeader ().Update (AppName , AppVersion , s .brewVersion )
239241 })
240242 }
241243 }()
242244
243- // Result table section
245+ // Table handler to update the details view when a table row is selected
244246 tableSelectionChangedFunc := func (row , _ int ) {
245247 if row > 0 && row - 1 < len (* s .filteredPackages ) {
246- s .setDetails (& (* s .filteredPackages )[row - 1 ])
248+ s .layout . GetDetails (). SetContent (& (* s .filteredPackages )[row - 1 ])
247249 }
248250 }
249251 s .layout .GetTable ().View ().SetSelectionChangedFunc (tableSelectionChangedFunc )
250252
251- // Search field section
253+ // Search input handlers
252254 inputDoneFunc := func (key tcell.Key ) {
253255 if key == tcell .KeyEnter || key == tcell .KeyEscape {
254- s .app .SetFocus (s .layout .GetTable ().View ())
256+ s .app .SetFocus (s .layout .GetTable ().View ()) // Set focus back to the table on Enter or Escape
255257 }
256258 }
257- changedFunc := func (text string ) {
258- s .search (text , true )
259+ changedFunc := func (text string ) { // Each time the search input changes
260+ s .search (text , true ) // Perform search and scroll to top
259261 }
260262 s .layout .GetSearch ().SetHandlers (inputDoneFunc , changedFunc )
261263
262- // Add key event handler and set the root view
263- s .app .SetInputCapture (s .IOService .HandleKeyEventInput )
264+ // Add key event handler
265+ s .app .SetInputCapture (s .ioService .HandleKeyEventInput )
266+
267+ // Set the root of the application to the layout's root and focus on the table view
264268 s .app .SetRoot (s .layout .Root (), true )
265269 s .app .SetFocus (s .layout .GetTable ().View ())
266270
0 commit comments