diff --git a/cmd/bbrew/main.go b/cmd/bbrew/main.go index 216d425..21dd617 100644 --- a/cmd/bbrew/main.go +++ b/cmd/bbrew/main.go @@ -7,7 +7,7 @@ import ( func main() { appService := services.NewAppService() - if err := appService.InitData(); err != nil { + if err := appService.Boot(); err != nil { log.Fatalf("Error initializing data: %v", err) } appService.BuildApp() diff --git a/internal/services/app.go b/internal/services/app.go index f6fecb3..9f6d91b 100644 --- a/internal/services/app.go +++ b/internal/services/app.go @@ -16,7 +16,7 @@ var ( type AppServiceInterface interface { GetApp() *tview.Application - InitData() (err error) + Boot() (err error) BuildApp() } @@ -56,7 +56,7 @@ func (s *AppService) GetApp() *tview.Application { return s.app } -func (s *AppService) InitData() (err error) { +func (s *AppService) Boot() (err error) { if err = s.BrewService.LoadAllFormulae(); err != nil { return fmt.Errorf("failed to load Homebrew formulae: %v", err) } @@ -71,9 +71,7 @@ func (s *AppService) InitData() (err error) { return nil } -func (s *AppService) applySearchFilter( - searchText string, -) { +func (s *AppService) search(searchText string) { var filteredList []models.Formula uniquePackages := make(map[string]bool) @@ -105,12 +103,10 @@ func (s *AppService) applySearchFilter( } *s.filteredPackages = filteredList - s.fillTable(s.filteredPackages) - - s.LayoutService.GetFilterCounter().SetText(fmt.Sprintf("Total: %d | Filtered: %d", len(*s.packages), len(*s.filteredPackages))) + s.setResults(s.filteredPackages) } -func (s *AppService) updateDetailsView(info *models.Formula) { +func (s *AppService) setDetails(info *models.Formula) { if info != nil { installedVersion := "Not installed" packagePrefix := "-" @@ -149,20 +145,20 @@ func (s *AppService) updateDetailsView(info *models.Formula) { s.LayoutService.GetDetailsView().SetText("") } -func (s *AppService) updateTableView() { +func (s *AppService) forceRefreshResults() { s.app.QueueUpdateDraw(func() { _ = s.BrewService.LoadAllFormulae() - s.applySearchFilter(s.LayoutService.GetSearchField().GetText()) - s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetTableResult()) + s.search(s.LayoutService.GetSearchField().GetText()) + s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetResultTable()) //TODO: da capire se rimuovere }) } -func (s *AppService) fillTable(data *[]models.Formula) { +func (s *AppService) setResults(data *[]models.Formula) { headers := []string{"Name", "Description", "Version"} - s.LayoutService.GetTableResult().Clear() + s.LayoutService.GetResultTable().Clear() for i, header := range headers { - s.LayoutService.GetTableResult().SetCell(0, i, tview.NewTableCell(header). + s.LayoutService.GetResultTable().SetCell(0, i, tview.NewTableCell(header). SetTextColor(tcell.ColorBlue). SetAlign(tview.AlignLeft). SetSelectable(false). @@ -185,65 +181,66 @@ func (s *AppService) fillTable(data *[]models.Formula) { versionCell.SetTextColor(tcell.ColorOrange) } - s.LayoutService.GetTableResult().SetCell(i+1, 0, nameCell) - s.LayoutService.GetTableResult().SetCell(i+1, 1, tview.NewTableCell(info.Description).SetSelectable(true)) - s.LayoutService.GetTableResult().SetCell(i+1, 2, versionCell) + s.LayoutService.GetResultTable().SetCell(i+1, 0, nameCell) + s.LayoutService.GetResultTable().SetCell(i+1, 1, tview.NewTableCell(info.Description).SetSelectable(true)) + s.LayoutService.GetResultTable().SetCell(i+1, 2, versionCell) } // Update the details view with the first item in the list if len(*data) > 0 { - s.LayoutService.GetTableResult().Select(1, 0) - s.LayoutService.GetTableResult().ScrollToBeginning() - s.updateDetailsView(&(*data)[0]) + s.LayoutService.GetResultTable().Select(1, 0) + s.LayoutService.GetResultTable().ScrollToBeginning() + s.setDetails(&(*data)[0]) + + // Update the filter counter + s.LayoutService.UpdateFilterCounterView(len(*s.packages), len(*s.filteredPackages)) return } - s.updateDetailsView(nil) + s.setDetails(nil) } func (s *AppService) BuildApp() { + // Evaluate if there is a new version available latestVersion, err := s.SelfUpdateService.CheckForUpdates() if err == nil && latestVersion != AppVersion { AppVersion = fmt.Sprintf("%s ([orange]Update available: %s[-])", AppVersion, latestVersion) } - s.LayoutService.SetHeader(AppName, AppVersion, s.brewVersion) - s.LayoutService.SetLegend() - - tableSelectionChangedFunc := func(row, column int) { - if row > 0 && row-1 < len(*s.filteredPackages) { - s.updateDetailsView(&(*s.filteredPackages)[row-1]) - } - } - - s.LayoutService.SetTableResult(tableSelectionChangedFunc) + // Build the layout + s.LayoutService.SetHeaderView(AppName, AppVersion, s.brewVersion) + s.LayoutService.SetLegendView() s.LayoutService.SetDetailsView() s.LayoutService.SetBuildOutputView() + s.LayoutService.SetFilterCounterView() - // Search input to filter packages - inputDoneFunc := func(key tcell.Key) { - if key == tcell.KeyEnter || key == tcell.KeyEscape { - s.app.SetFocus(s.LayoutService.GetTableResult()) + // Result table section + tableSelectionChangedFunc := func(row, column int) { + if row > 0 && row-1 < len(*s.filteredPackages) { + s.setDetails(&(*s.filteredPackages)[row-1]) } } + s.LayoutService.SetResultTable(tableSelectionChangedFunc) - changedFunc := func(text string) { - s.applySearchFilter(s.LayoutService.GetSearchField().GetText()) + // Search field section + inputDoneFunc := func(key tcell.Key) { + if key == tcell.KeyEnter || key == tcell.KeyEscape { + s.app.SetFocus(s.LayoutService.GetResultTable()) + } + } + changedFunc := func(text string) { + s.search(s.LayoutService.GetSearchField().GetText()) } - s.LayoutService.SetSearchField(inputDoneFunc, changedFunc) - s.LayoutService.SetFilterCounter(len(*s.packages), len(*s.filteredPackages)) - + // Set the grid layout (final step) s.LayoutService.SetGrid() - // Add key event handler + // Add key event handler and set the root view s.app.SetInputCapture(s.handleKeyEventInput) - - // Set the grid as the root of the application s.app.SetRoot(s.LayoutService.GetGrid(), true) - s.app.SetFocus(s.LayoutService.GetTableResult()) + s.app.SetFocus(s.LayoutService.GetResultTable()) // Fill the table with the initial data - s.fillTable(s.packages) + s.setResults(s.packages) } diff --git a/internal/services/io.go b/internal/services/io.go index 0453165..4dda05f 100644 --- a/internal/services/io.go +++ b/internal/services/io.go @@ -26,7 +26,7 @@ func (s *AppService) handleKeyEventInput(event *tcell.EventKey) *tcell.EventKey }, tcell.KeyCtrlU: s.handleUpdateHomebrewEvent, tcell.KeyEsc: func() { - s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetTableResult()) + s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetResultTable()) }, } @@ -49,14 +49,14 @@ func (s *AppService) handleQuitEvent() { func (s *AppService) handleUpdateHomebrewEvent() { // Update homebrew modal := s.LayoutService.GenerateModal("Are you sure you want to update Homebrew?", func() { - s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetTableResult()) + s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetResultTable()) s.LayoutService.GetOutputView().Clear() go func() { _ = s.CommandService.UpdateHomebrew(s.app, s.LayoutService.GetOutputView()) - s.updateTableView() + s.forceRefreshResults() }() }, func() { - s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetTableResult()) + s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetResultTable()) }) s.app.SetRoot(modal, true).SetFocus(modal) } @@ -68,59 +68,59 @@ func (s *AppService) handleFilterPackagesEvent() { } else { s.LayoutService.GetSearchField().SetLabel("Search (All): ") } - s.applySearchFilter(s.LayoutService.GetSearchField().GetText()) - s.LayoutService.GetTableResult().ScrollToBeginning() + s.search(s.LayoutService.GetSearchField().GetText()) + s.LayoutService.GetResultTable().ScrollToBeginning() } func (s *AppService) handleInstallPackageEvent() { - row, _ := s.LayoutService.GetTableResult().GetSelection() + row, _ := s.LayoutService.GetResultTable().GetSelection() if row > 0 { info := (*s.filteredPackages)[row-1] modal := s.LayoutService.GenerateModal(fmt.Sprintf("Are you sure you want to install the package: %s?", info.Name), func() { - s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetTableResult()) + s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetResultTable()) s.LayoutService.GetOutputView().Clear() go func() { _ = s.CommandService.InstallPackage(info, s.app, s.LayoutService.GetOutputView()) - s.updateTableView() + s.forceRefreshResults() }() }, func() { - s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetTableResult()) + s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetResultTable()) }) s.app.SetRoot(modal, true).SetFocus(modal) } } func (s *AppService) handleRemovePackageEvent() { - row, _ := s.LayoutService.GetTableResult().GetSelection() + row, _ := s.LayoutService.GetResultTable().GetSelection() if row > 0 { info := (*s.filteredPackages)[row-1] modal := s.LayoutService.GenerateModal(fmt.Sprintf("Are you sure you want to remove the package: %s?", info.Name), func() { - s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetTableResult()) + s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetResultTable()) s.LayoutService.GetOutputView().Clear() go func() { _ = s.CommandService.RemovePackage(info, s.app, s.LayoutService.GetOutputView()) - s.updateTableView() + s.forceRefreshResults() }() }, func() { - s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetTableResult()) + s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetResultTable()) }) s.app.SetRoot(modal, true).SetFocus(modal) } } func (s *AppService) handleUpdatePackageEvent() { - row, _ := s.LayoutService.GetTableResult().GetSelection() + row, _ := s.LayoutService.GetResultTable().GetSelection() if row > 0 { info := (*s.filteredPackages)[row-1] modal := s.LayoutService.GenerateModal(fmt.Sprintf("Are you sure you want to update the package: %s?", info.Name), func() { - s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetTableResult()) + s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetResultTable()) s.LayoutService.GetOutputView().Clear() go func() { _ = s.CommandService.UpdatePackage(info, s.app, s.LayoutService.GetOutputView()) - s.updateTableView() + s.forceRefreshResults() }() }, func() { - s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetTableResult()) + s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetResultTable()) }) s.app.SetRoot(modal, true).SetFocus(modal) diff --git a/internal/services/layout.go b/internal/services/layout.go index 7d3f592..4b52db4 100644 --- a/internal/services/layout.go +++ b/internal/services/layout.go @@ -10,14 +10,14 @@ type LayoutServiceInterface interface { GetGrid() *tview.Grid SetGrid() - GetHeader() *tview.TextView - SetHeader(name, version, brewVersion string) + GetHeaderView() *tview.TextView + SetHeaderView(name, version, brewVersion string) - GetLegend() *tview.TextView - SetLegend() + GetLegendView() *tview.TextView + SetLegendView() - GetTableResult() *tview.Table - SetTableResult(selectionChanged func(row, column int)) + GetResultTable() *tview.Table + SetResultTable(selectionChanged func(row, column int)) GetDetailsView() *tview.TextView SetDetailsView() @@ -28,8 +28,9 @@ type LayoutServiceInterface interface { GetSearchField() *tview.InputField SetSearchField(done func(key tcell.Key), changed func(text string)) - GetFilterCounter() *tview.TextView - SetFilterCounter(total, filtered int) + GetFilterCounterView() *tview.TextView + SetFilterCounterView() + UpdateFilterCounterView(total, filtered int) GenerateModal(text string, confirmFunc func(), cancelFunc func()) *tview.Modal } @@ -85,33 +86,33 @@ func (s *LayoutService) SetGrid() { AddItem(s.legend, 2, 0, 1, 1, 0, 0, false) } -func (s *LayoutService) GetHeader() *tview.TextView { +func (s *LayoutService) GetHeaderView() *tview.TextView { return s.header } -func (s *LayoutService) SetHeader(name, version, brewVersion string) { +func (s *LayoutService) SetHeaderView(name, version, brewVersion string) { s.header = tview.NewTextView(). SetText(fmt.Sprintf("%s %s - %s", name, version, brewVersion)). SetDynamicColors(true). SetTextAlign(tview.AlignCenter) } -func (s *LayoutService) GetLegend() *tview.TextView { +func (s *LayoutService) GetLegendView() *tview.TextView { return s.legend } -func (s *LayoutService) SetLegend() { +func (s *LayoutService) SetLegendView() { s.legend = tview.NewTextView(). SetText(tview.Escape("[/] Search | [f] Filter Installed | [i] Install | [u] Update | [r] Remove | [Esc] Back to Table | [ctrl+u] Update Homebrew | [q] Quit")). SetDynamicColors(true). SetTextAlign(tview.AlignCenter) } -func (s *LayoutService) GetTableResult() *tview.Table { +func (s *LayoutService) GetResultTable() *tview.Table { return s.table } -func (s *LayoutService) SetTableResult(selectionChanged func(row, column int)) { +func (s *LayoutService) SetResultTable(selectionChanged func(row, column int)) { s.table = tview.NewTable(). SetBorders(false). SetSelectable(true, false). @@ -152,15 +153,18 @@ func (s *LayoutService) SetSearchField(done func(key tcell.Key), changed func(te SetChangedFunc(changed) } -func (s *LayoutService) GetFilterCounter() *tview.TextView { +func (s *LayoutService) GetFilterCounterView() *tview.TextView { return s.filterCounter } -func (s *LayoutService) SetFilterCounter(total, filtered int) { +func (s *LayoutService) SetFilterCounterView() { s.filterCounter = tview.NewTextView(). SetDynamicColors(true). - SetTextAlign(tview.AlignRight). - SetText(fmt.Sprintf("Total: %d | Filtered: %d", total, filtered)) + SetTextAlign(tview.AlignRight) +} + +func (s *LayoutService) UpdateFilterCounterView(total, filtered int) { + s.filterCounter.SetText(fmt.Sprintf("Total: %d | Filtered: %d", total, filtered)) } func (s *LayoutService) GenerateModal(text string, confirmFunc func(), cancelFunc func()) *tview.Modal {