mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-21 23:01:03 +00:00
convert volumes panel to use new struct
This commit is contained in:
parent
79a58c8d48
commit
a4b4fee868
9 changed files with 62 additions and 178 deletions
|
|
@ -44,7 +44,6 @@ type DockerCommand struct {
|
|||
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 []*Container
|
||||
Volumes []*Volume
|
||||
Closers []io.Closer
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,13 +2,10 @@ package commands
|
|||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/samber/lo"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
|
@ -28,29 +25,16 @@ func (v *Volume) GetDisplayStrings(isFocused bool) []string {
|
|||
}
|
||||
|
||||
// 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{})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
volumes := result.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 {
|
||||
ownVolumes[i] = &Volume{
|
||||
Name: volume.Name,
|
||||
|
|
@ -62,15 +46,7 @@ func (c *DockerCommand) RefreshVolumes() error {
|
|||
}
|
||||
}
|
||||
|
||||
ownVolumes = lo.Filter(ownVolumes, func(volume *Volume, _ int) bool {
|
||||
return !lo.SomeBy(c.Config.UserConfig.Ignore, func(ignore string) bool {
|
||||
return strings.Contains(volume.Name, ignore)
|
||||
})
|
||||
})
|
||||
|
||||
c.Volumes = ownVolumes
|
||||
|
||||
return nil
|
||||
return ownVolumes, nil
|
||||
}
|
||||
|
||||
// PruneVolumes prunes volumes
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ var OverlappingEdges = false
|
|||
// by calling functions. The less of these, the better
|
||||
type SentinelErrors struct {
|
||||
ErrNoContainers error
|
||||
ErrNoImages error
|
||||
ErrNoVolumes error
|
||||
}
|
||||
|
||||
|
|
@ -43,7 +42,6 @@ type SentinelErrors struct {
|
|||
func (gui *Gui) GenerateSentinelErrors() {
|
||||
gui.Errors = SentinelErrors{
|
||||
ErrNoContainers: errors.New(gui.Tr.NoContainers),
|
||||
ErrNoImages: errors.New(gui.Tr.NoImages),
|
||||
ErrNoVolumes: errors.New(gui.Tr.NoVolumes),
|
||||
}
|
||||
}
|
||||
|
|
@ -77,6 +75,7 @@ type Gui struct {
|
|||
type Panels struct {
|
||||
Images *SideListPanel[*commands.Image]
|
||||
Services *SideListPanel[*commands.Service]
|
||||
Volumes *SideListPanel[*commands.Volume]
|
||||
}
|
||||
|
||||
type Mutexes struct {
|
||||
|
|
@ -103,16 +102,10 @@ type mainPanelState struct {
|
|||
ObjectKey string
|
||||
}
|
||||
|
||||
type volumePanelState struct {
|
||||
SelectedLine int
|
||||
ContextIndex int
|
||||
}
|
||||
|
||||
type panelStates struct {
|
||||
Containers *containerPanelState
|
||||
Menu *menuPanelState
|
||||
Main *mainPanelState
|
||||
Volumes *volumePanelState
|
||||
Project *projectState
|
||||
}
|
||||
|
||||
|
|
@ -164,7 +157,6 @@ func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand
|
|||
Platform: *oSCommand.Platform,
|
||||
Panels: &panelStates{
|
||||
Containers: &containerPanelState{SelectedLine: -1, ContextIndex: 0},
|
||||
Volumes: &volumePanelState{SelectedLine: -1, ContextIndex: 0},
|
||||
Menu: &menuPanelState{SelectedLine: 0},
|
||||
Main: &mainPanelState{
|
||||
ObjectKey: "",
|
||||
|
|
@ -284,6 +276,7 @@ func (gui *Gui) Run() error {
|
|||
gui.Panels = Panels{
|
||||
Images: gui.getImagesPanel(),
|
||||
Services: gui.getServicesPanel(),
|
||||
Volumes: gui.getVolumesPanel(),
|
||||
}
|
||||
|
||||
if err = gui.keybindings(g); err != nil {
|
||||
|
|
@ -332,7 +325,7 @@ func (gui *Gui) refresh() {
|
|||
}
|
||||
}()
|
||||
go func() {
|
||||
if err := gui.refreshVolumes(); err != nil {
|
||||
if err := gui.reloadVolumes(); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
}()
|
||||
|
|
|
|||
|
|
@ -93,7 +93,6 @@ func (gui *Gui) refreshStateImages() error {
|
|||
return err
|
||||
}
|
||||
|
||||
// TODO: think about also re-filtering/sorting
|
||||
gui.Panels.Images.SetItems(images)
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -454,14 +454,14 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
|||
ViewName: "volumes",
|
||||
Key: '[',
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleVolumesPrevContext,
|
||||
Handler: wrappedHandler(gui.Panels.Volumes.OnPrevContext),
|
||||
Description: gui.Tr.PreviousContext,
|
||||
},
|
||||
{
|
||||
ViewName: "volumes",
|
||||
Key: ']',
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleVolumesNextContext,
|
||||
Handler: wrappedHandler(gui.Panels.Volumes.OnNextContext),
|
||||
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)},
|
||||
"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)},
|
||||
"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},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -127,7 +127,6 @@ func (gui *Gui) focusPointInView(view *gocui.View) {
|
|||
|
||||
listViews := map[string]listViewState{
|
||||
"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},
|
||||
}
|
||||
|
||||
|
|
@ -140,6 +139,8 @@ func (gui *Gui) focusPointInView(view *gocui.View) {
|
|||
gui.Panels.Images.Refocus()
|
||||
case "services":
|
||||
gui.Panels.Services.Refocus()
|
||||
case "volumes":
|
||||
gui.Panels.Volumes.Refocus()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -97,8 +97,8 @@ func (gui *Gui) onMainTabClick(tabIndex int) error {
|
|||
gui.Panels.Images.SetContextIndex(tabIndex)
|
||||
return gui.Panels.Images.HandleSelect()
|
||||
case "volumes":
|
||||
gui.State.Panels.Volumes.ContextIndex = tabIndex
|
||||
return gui.handleVolumeSelect(gui.g, gui.getVolumesView())
|
||||
gui.Panels.Volumes.SetContextIndex(tabIndex)
|
||||
return gui.Panels.Volumes.HandleSelect()
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ func (gui *Gui) newLineFocused(v *gocui.View) error {
|
|||
case "images":
|
||||
return gui.Panels.Images.HandleSelect()
|
||||
case "volumes":
|
||||
return gui.handleVolumeSelect(gui.g, v)
|
||||
return gui.Panels.Volumes.HandleSelect()
|
||||
case "confirmation":
|
||||
return nil
|
||||
case "main":
|
||||
|
|
|
|||
|
|
@ -4,74 +4,54 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||
"github.com/jesseduffield/lazydocker/pkg/config"
|
||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||
)
|
||||
|
||||
// list panel functions
|
||||
|
||||
func (gui *Gui) getVolumeContexts() []string {
|
||||
return []string{"config"}
|
||||
}
|
||||
|
||||
func (gui *Gui) getVolumeContextTitles() []string {
|
||||
return []string{gui.Tr.ConfigTitle}
|
||||
}
|
||||
|
||||
func (gui *Gui) getSelectedVolume() (*commands.Volume, error) {
|
||||
selectedLine := gui.State.Panels.Volumes.SelectedLine
|
||||
if selectedLine == -1 {
|
||||
return nil, gui.Errors.ErrNoVolumes
|
||||
func (gui *Gui) getVolumesPanel() *SideListPanel[*commands.Volume] {
|
||||
return &SideListPanel[*commands.Volume]{
|
||||
contextKeyPrefix: "volumes",
|
||||
ListPanel: ListPanel[*commands.Volume]{
|
||||
list: NewFilteredList[*commands.Volume](),
|
||||
view: gui.Views.Volumes,
|
||||
},
|
||||
contextIdx: 0,
|
||||
noItemsMessge: gui.Tr.NoVolumes,
|
||||
gui: gui.intoInterface(),
|
||||
contexts: []ContextConfig[*commands.Volume]{
|
||||
{
|
||||
key: "config",
|
||||
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 {
|
||||
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 {
|
||||
func (gui *Gui) renderVolumeConfig(volume *commands.Volume) error {
|
||||
return gui.T.NewTask(func(stop chan struct{}) {
|
||||
mainView := gui.Views.Main
|
||||
mainView.Autoscroll = false
|
||||
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 {
|
||||
volumesView := gui.getVolumesView()
|
||||
if volumesView == nil {
|
||||
// if the volumesView hasn't been instantiated yet we just return
|
||||
return nil
|
||||
}
|
||||
if err := gui.DockerCommand.RefreshVolumes(); err != nil {
|
||||
func (gui *Gui) reloadVolumes() error {
|
||||
if err := gui.refreshStateVolumes(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(gui.DockerCommand.Volumes) > 0 && gui.State.Panels.Volumes.SelectedLine == -1 {
|
||||
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
|
||||
return gui.Panels.Volumes.RerenderList()
|
||||
}
|
||||
|
||||
func (gui *Gui) handleVolumesNextLine(g *gocui.Gui, v *gocui.View) error {
|
||||
if gui.popupPanelFocused() || gui.g.CurrentView() != v {
|
||||
return nil
|
||||
func (gui *Gui) refreshStateVolumes() error {
|
||||
volumes, err := gui.DockerCommand.RefreshVolumes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
panelState := gui.State.Panels.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)
|
||||
gui.Panels.Volumes.SetItems(volumes)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -199,7 +115,7 @@ func (r *removeVolumeOption) GetDisplayStrings(isFocused bool) []string {
|
|||
}
|
||||
|
||||
func (gui *Gui) handleVolumesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
|
||||
volume, err := gui.getSelectedVolume()
|
||||
volume, err := gui.Panels.Volumes.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -251,7 +167,7 @@ func (gui *Gui) handlePruneVolumes() 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 {
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue