convert volumes panel to use new struct

This commit is contained in:
Jesse Duffield 2022-10-20 10:27:42 -07:00
parent 79a58c8d48
commit a4b4fee868
9 changed files with 62 additions and 178 deletions

View file

@ -44,7 +44,6 @@ type DockerCommand struct {
Containers []*Container Containers []*Container
// DisplayContainers is the array of containers we will display in the containers panel. If Gui.ShowAllContainers is false, this will only be those containers which aren't based on a service. This reduces clutter and duplication in the UI // DisplayContainers is the array of containers we will display in the containers panel. If Gui.ShowAllContainers is false, this will only be those containers which aren't based on a service. This reduces clutter and duplication in the UI
DisplayContainers []*Container DisplayContainers []*Container
Volumes []*Volume
Closers []io.Closer Closers []io.Closer
} }

View file

@ -2,13 +2,10 @@ package commands
import ( import (
"context" "context"
"sort"
"strings"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/samber/lo"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -28,29 +25,16 @@ func (v *Volume) GetDisplayStrings(isFocused bool) []string {
} }
// RefreshVolumes gets the volumes and stores them // RefreshVolumes gets the volumes and stores them
func (c *DockerCommand) RefreshVolumes() error { func (c *DockerCommand) RefreshVolumes() ([]*Volume, error) {
result, err := c.Client.VolumeList(context.Background(), filters.Args{}) result, err := c.Client.VolumeList(context.Background(), filters.Args{})
if err != nil { if err != nil {
return err return nil, err
} }
volumes := result.Volumes volumes := result.Volumes
ownVolumes := make([]*Volume, len(volumes)) ownVolumes := make([]*Volume, len(volumes))
// we're sorting these volumes based on whether they have labels defined,
// because those are the ones you typically care about.
// Within that, we also sort them alphabetically
sort.Slice(volumes, func(i, j int) bool {
if len(volumes[i].Labels) == 0 && len(volumes[j].Labels) > 0 {
return false
}
if len(volumes[i].Labels) > 0 && len(volumes[j].Labels) == 0 {
return true
}
return volumes[i].Name < volumes[j].Name
})
for i, volume := range volumes { for i, volume := range volumes {
ownVolumes[i] = &Volume{ ownVolumes[i] = &Volume{
Name: volume.Name, Name: volume.Name,
@ -62,15 +46,7 @@ func (c *DockerCommand) RefreshVolumes() error {
} }
} }
ownVolumes = lo.Filter(ownVolumes, func(volume *Volume, _ int) bool { return ownVolumes, nil
return !lo.SomeBy(c.Config.UserConfig.Ignore, func(ignore string) bool {
return strings.Contains(volume.Name, ignore)
})
})
c.Volumes = ownVolumes
return nil
} }
// PruneVolumes prunes volumes // PruneVolumes prunes volumes

View file

@ -26,7 +26,6 @@ var OverlappingEdges = false
// by calling functions. The less of these, the better // by calling functions. The less of these, the better
type SentinelErrors struct { type SentinelErrors struct {
ErrNoContainers error ErrNoContainers error
ErrNoImages error
ErrNoVolumes error ErrNoVolumes error
} }
@ -43,7 +42,6 @@ type SentinelErrors struct {
func (gui *Gui) GenerateSentinelErrors() { func (gui *Gui) GenerateSentinelErrors() {
gui.Errors = SentinelErrors{ gui.Errors = SentinelErrors{
ErrNoContainers: errors.New(gui.Tr.NoContainers), ErrNoContainers: errors.New(gui.Tr.NoContainers),
ErrNoImages: errors.New(gui.Tr.NoImages),
ErrNoVolumes: errors.New(gui.Tr.NoVolumes), ErrNoVolumes: errors.New(gui.Tr.NoVolumes),
} }
} }
@ -77,6 +75,7 @@ type Gui struct {
type Panels struct { type Panels struct {
Images *SideListPanel[*commands.Image] Images *SideListPanel[*commands.Image]
Services *SideListPanel[*commands.Service] Services *SideListPanel[*commands.Service]
Volumes *SideListPanel[*commands.Volume]
} }
type Mutexes struct { type Mutexes struct {
@ -103,16 +102,10 @@ type mainPanelState struct {
ObjectKey string ObjectKey string
} }
type volumePanelState struct {
SelectedLine int
ContextIndex int
}
type panelStates struct { type panelStates struct {
Containers *containerPanelState Containers *containerPanelState
Menu *menuPanelState Menu *menuPanelState
Main *mainPanelState Main *mainPanelState
Volumes *volumePanelState
Project *projectState Project *projectState
} }
@ -164,7 +157,6 @@ func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand
Platform: *oSCommand.Platform, Platform: *oSCommand.Platform,
Panels: &panelStates{ Panels: &panelStates{
Containers: &containerPanelState{SelectedLine: -1, ContextIndex: 0}, Containers: &containerPanelState{SelectedLine: -1, ContextIndex: 0},
Volumes: &volumePanelState{SelectedLine: -1, ContextIndex: 0},
Menu: &menuPanelState{SelectedLine: 0}, Menu: &menuPanelState{SelectedLine: 0},
Main: &mainPanelState{ Main: &mainPanelState{
ObjectKey: "", ObjectKey: "",
@ -284,6 +276,7 @@ func (gui *Gui) Run() error {
gui.Panels = Panels{ gui.Panels = Panels{
Images: gui.getImagesPanel(), Images: gui.getImagesPanel(),
Services: gui.getServicesPanel(), Services: gui.getServicesPanel(),
Volumes: gui.getVolumesPanel(),
} }
if err = gui.keybindings(g); err != nil { if err = gui.keybindings(g); err != nil {
@ -332,7 +325,7 @@ func (gui *Gui) refresh() {
} }
}() }()
go func() { go func() {
if err := gui.refreshVolumes(); err != nil { if err := gui.reloadVolumes(); err != nil {
gui.Log.Error(err) gui.Log.Error(err)
} }
}() }()

View file

@ -93,7 +93,6 @@ func (gui *Gui) refreshStateImages() error {
return err return err
} }
// TODO: think about also re-filtering/sorting
gui.Panels.Images.SetItems(images) gui.Panels.Images.SetItems(images)
return nil return nil

View file

@ -454,14 +454,14 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
ViewName: "volumes", ViewName: "volumes",
Key: '[', Key: '[',
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleVolumesPrevContext, Handler: wrappedHandler(gui.Panels.Volumes.OnPrevContext),
Description: gui.Tr.PreviousContext, Description: gui.Tr.PreviousContext,
}, },
{ {
ViewName: "volumes", ViewName: "volumes",
Key: ']', Key: ']',
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleVolumesNextContext, Handler: wrappedHandler(gui.Panels.Volumes.OnNextContext),
Description: gui.Tr.NextContext, Description: gui.Tr.NextContext,
}, },
{ {
@ -587,7 +587,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
"services": {onKeyUpPress: wrappedHandler(gui.Panels.Services.OnPrevLine), onKeyDownPress: wrappedHandler(gui.Panels.Services.OnNextLine), onClick: wrappedHandler(gui.Panels.Services.OnClick)}, "services": {onKeyUpPress: wrappedHandler(gui.Panels.Services.OnPrevLine), onKeyDownPress: wrappedHandler(gui.Panels.Services.OnNextLine), onClick: wrappedHandler(gui.Panels.Services.OnClick)},
"containers": {onKeyUpPress: gui.handleContainersPrevLine, onKeyDownPress: gui.handleContainersNextLine, onClick: gui.handleContainersClick}, "containers": {onKeyUpPress: gui.handleContainersPrevLine, onKeyDownPress: gui.handleContainersNextLine, onClick: gui.handleContainersClick},
"images": {onKeyUpPress: wrappedHandler(gui.Panels.Images.OnPrevLine), onKeyDownPress: wrappedHandler(gui.Panels.Images.OnNextLine), onClick: wrappedHandler(gui.Panels.Images.OnClick)}, "images": {onKeyUpPress: wrappedHandler(gui.Panels.Images.OnPrevLine), onKeyDownPress: wrappedHandler(gui.Panels.Images.OnNextLine), onClick: wrappedHandler(gui.Panels.Images.OnClick)},
"volumes": {onKeyUpPress: gui.handleVolumesPrevLine, onKeyDownPress: gui.handleVolumesNextLine, onClick: gui.handleVolumesClick}, "volumes": {onKeyUpPress: wrappedHandler(gui.Panels.Volumes.OnPrevLine), onKeyDownPress: wrappedHandler(gui.Panels.Volumes.OnNextLine), onClick: wrappedHandler(gui.Panels.Volumes.OnClick)},
"main": {onKeyUpPress: gui.scrollUpMain, onKeyDownPress: gui.scrollDownMain, onClick: gui.handleMainClick}, "main": {onKeyUpPress: gui.scrollUpMain, onKeyDownPress: gui.scrollDownMain, onClick: gui.handleMainClick},
} }

View file

@ -127,7 +127,6 @@ func (gui *Gui) focusPointInView(view *gocui.View) {
listViews := map[string]listViewState{ listViews := map[string]listViewState{
"containers": {selectedLine: gui.State.Panels.Containers.SelectedLine, lineCount: len(gui.DockerCommand.DisplayContainers)}, "containers": {selectedLine: gui.State.Panels.Containers.SelectedLine, lineCount: len(gui.DockerCommand.DisplayContainers)},
"volumes": {selectedLine: gui.State.Panels.Volumes.SelectedLine, lineCount: len(gui.DockerCommand.Volumes)},
"menu": {selectedLine: gui.State.Panels.Menu.SelectedLine, lineCount: gui.State.MenuItemCount}, "menu": {selectedLine: gui.State.Panels.Menu.SelectedLine, lineCount: gui.State.MenuItemCount},
} }
@ -140,6 +139,8 @@ func (gui *Gui) focusPointInView(view *gocui.View) {
gui.Panels.Images.Refocus() gui.Panels.Images.Refocus()
case "services": case "services":
gui.Panels.Services.Refocus() gui.Panels.Services.Refocus()
case "volumes":
gui.Panels.Volumes.Refocus()
} }
} }

View file

@ -97,8 +97,8 @@ func (gui *Gui) onMainTabClick(tabIndex int) error {
gui.Panels.Images.SetContextIndex(tabIndex) gui.Panels.Images.SetContextIndex(tabIndex)
return gui.Panels.Images.HandleSelect() return gui.Panels.Images.HandleSelect()
case "volumes": case "volumes":
gui.State.Panels.Volumes.ContextIndex = tabIndex gui.Panels.Volumes.SetContextIndex(tabIndex)
return gui.handleVolumeSelect(gui.g, gui.getVolumesView()) return gui.Panels.Volumes.HandleSelect()
} }
return nil return nil

View file

@ -83,7 +83,7 @@ func (gui *Gui) newLineFocused(v *gocui.View) error {
case "images": case "images":
return gui.Panels.Images.HandleSelect() return gui.Panels.Images.HandleSelect()
case "volumes": case "volumes":
return gui.handleVolumeSelect(gui.g, v) return gui.Panels.Volumes.HandleSelect()
case "confirmation": case "confirmation":
return nil return nil
case "main": case "main":

View file

@ -4,74 +4,54 @@ import (
"fmt" "fmt"
"github.com/fatih/color" "github.com/fatih/color"
"github.com/go-errors/errors"
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazydocker/pkg/commands" "github.com/jesseduffield/lazydocker/pkg/commands"
"github.com/jesseduffield/lazydocker/pkg/config" "github.com/jesseduffield/lazydocker/pkg/config"
"github.com/jesseduffield/lazydocker/pkg/utils" "github.com/jesseduffield/lazydocker/pkg/utils"
) )
// list panel functions func (gui *Gui) getVolumesPanel() *SideListPanel[*commands.Volume] {
return &SideListPanel[*commands.Volume]{
func (gui *Gui) getVolumeContexts() []string { contextKeyPrefix: "volumes",
return []string{"config"} ListPanel: ListPanel[*commands.Volume]{
} list: NewFilteredList[*commands.Volume](),
view: gui.Views.Volumes,
func (gui *Gui) getVolumeContextTitles() []string { },
return []string{gui.Tr.ConfigTitle} contextIdx: 0,
} noItemsMessge: gui.Tr.NoVolumes,
gui: gui.intoInterface(),
func (gui *Gui) getSelectedVolume() (*commands.Volume, error) { contexts: []ContextConfig[*commands.Volume]{
selectedLine := gui.State.Panels.Volumes.SelectedLine {
if selectedLine == -1 { key: "config",
return nil, gui.Errors.ErrNoVolumes title: gui.Tr.ConfigTitle,
render: gui.renderVolumeConfig,
},
},
getSearchStrings: func(volume *commands.Volume) []string {
// TODO: think about more things to search on
return []string{volume.Name}
},
getContextCacheKey: func(volume *commands.Volume) string {
return volume.Name
},
// we're sorting these volumes based on whether they have labels defined,
// because those are the ones you typically care about.
// Within that, we also sort them alphabetically
sort: func(a *commands.Volume, b *commands.Volume) bool {
if len(a.Volume.Labels) == 0 && len(b.Volume.Labels) > 0 {
return false
}
if len(a.Volume.Labels) > 0 && len(b.Volume.Labels) == 0 {
return true
}
return a.Name < b.Name
},
} }
return gui.DockerCommand.Volumes[selectedLine], nil
} }
func (gui *Gui) handleVolumesClick(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) renderVolumeConfig(volume *commands.Volume) error {
itemCount := len(gui.DockerCommand.Volumes)
handleSelect := gui.handleVolumeSelect
selectedLine := &gui.State.Panels.Volumes.SelectedLine
return gui.handleClick(v, itemCount, selectedLine, handleSelect)
}
func (gui *Gui) handleVolumeSelect(g *gocui.Gui, v *gocui.View) error {
volume, err := gui.getSelectedVolume()
if err != nil {
if err != gui.Errors.ErrNoVolumes {
return err
}
return gui.renderStringMain(gui.Tr.NoVolumes)
}
gui.focusY(gui.State.Panels.Volumes.SelectedLine, len(gui.DockerCommand.Volumes), v)
key := "volumes-" + volume.Name + "-" + gui.getVolumeContexts()[gui.State.Panels.Volumes.ContextIndex]
if !gui.shouldRefresh(key) {
return nil
}
mainView := gui.getMainView()
mainView.Tabs = gui.getVolumeContextTitles()
mainView.TabIndex = gui.State.Panels.Volumes.ContextIndex
switch gui.getVolumeContexts()[gui.State.Panels.Volumes.ContextIndex] {
case "config":
if err := gui.renderVolumeConfig(mainView, volume); err != nil {
return err
}
default:
return errors.New("Unknown context for Volumes panel")
}
return nil
}
func (gui *Gui) renderVolumeConfig(mainView *gocui.View, volume *commands.Volume) error {
return gui.T.NewTask(func(stop chan struct{}) { return gui.T.NewTask(func(stop chan struct{}) {
mainView := gui.Views.Main
mainView.Autoscroll = false mainView.Autoscroll = false
mainView.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel mainView.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel
@ -103,85 +83,21 @@ func (gui *Gui) renderVolumeConfig(mainView *gocui.View, volume *commands.Volume
}) })
} }
func (gui *Gui) refreshVolumes() error { func (gui *Gui) reloadVolumes() error {
volumesView := gui.getVolumesView() if err := gui.refreshStateVolumes(); err != nil {
if volumesView == nil {
// if the volumesView hasn't been instantiated yet we just return
return nil
}
if err := gui.DockerCommand.RefreshVolumes(); err != nil {
return err return err
} }
if len(gui.DockerCommand.Volumes) > 0 && gui.State.Panels.Volumes.SelectedLine == -1 { return gui.Panels.Volumes.RerenderList()
gui.State.Panels.Volumes.SelectedLine = 0
}
if len(gui.DockerCommand.Volumes)-1 < gui.State.Panels.Volumes.SelectedLine {
gui.State.Panels.Volumes.SelectedLine = len(gui.DockerCommand.Volumes) - 1
}
gui.g.Update(func(g *gocui.Gui) error {
volumesView.Clear()
isFocused := gui.g.CurrentView().Name() == "volumes"
list, err := utils.RenderList(gui.DockerCommand.Volumes, utils.IsFocused(isFocused))
if err != nil {
return err
}
fmt.Fprint(volumesView, list)
if volumesView == g.CurrentView() {
return gui.handleVolumeSelect(g, volumesView)
}
return nil
})
return nil
} }
func (gui *Gui) handleVolumesNextLine(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) refreshStateVolumes() error {
if gui.popupPanelFocused() || gui.g.CurrentView() != v { volumes, err := gui.DockerCommand.RefreshVolumes()
return nil if err != nil {
return err
} }
panelState := gui.State.Panels.Volumes gui.Panels.Volumes.SetItems(volumes)
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.DockerCommand.Volumes), false)
return gui.handleVolumeSelect(gui.g, v)
}
func (gui *Gui) handleVolumesPrevLine(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() || gui.g.CurrentView() != v {
return nil
}
panelState := gui.State.Panels.Volumes
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.DockerCommand.Volumes), true)
return gui.handleVolumeSelect(gui.g, v)
}
func (gui *Gui) handleVolumesNextContext(g *gocui.Gui, v *gocui.View) error {
contexts := gui.getVolumeContexts()
if gui.State.Panels.Volumes.ContextIndex >= len(contexts)-1 {
gui.State.Panels.Volumes.ContextIndex = 0
} else {
gui.State.Panels.Volumes.ContextIndex++
}
_ = gui.handleVolumeSelect(gui.g, v)
return nil
}
func (gui *Gui) handleVolumesPrevContext(g *gocui.Gui, v *gocui.View) error {
contexts := gui.getVolumeContexts()
if gui.State.Panels.Volumes.ContextIndex <= 0 {
gui.State.Panels.Volumes.ContextIndex = len(contexts) - 1
} else {
gui.State.Panels.Volumes.ContextIndex--
}
_ = gui.handleVolumeSelect(gui.g, v)
return nil return nil
} }
@ -199,7 +115,7 @@ func (r *removeVolumeOption) GetDisplayStrings(isFocused bool) []string {
} }
func (gui *Gui) handleVolumesRemoveMenu(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleVolumesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
volume, err := gui.getSelectedVolume() volume, err := gui.Panels.Volumes.GetSelectedItem()
if err != nil { if err != nil {
return nil return nil
} }
@ -251,7 +167,7 @@ func (gui *Gui) handlePruneVolumes() error {
} }
func (gui *Gui) handleVolumesCustomCommand(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleVolumesCustomCommand(g *gocui.Gui, v *gocui.View) error {
volume, err := gui.getSelectedVolume() volume, err := gui.Panels.Volumes.GetSelectedItem()
if err != nil { if err != nil {
return nil return nil
} }