mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-25 08:31:03 +00:00
Rework static notification handling
This commit is contained in:
parent
59f39d76cc
commit
70e96c6a49
15 changed files with 73 additions and 59 deletions
|
|
@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
## Container
|
## Container
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd></kbd>: copy container id
|
<kbd>y</kbd>: copy container ID
|
||||||
<kbd>d</kbd>: entfernen
|
<kbd>d</kbd>: entfernen
|
||||||
<kbd>e</kbd>: hide/show stopped containers
|
<kbd>e</kbd>: hide/show stopped containers
|
||||||
<kbd>p</kbd>: pause
|
<kbd>p</kbd>: pause
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
## Containers
|
## Containers
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd></kbd>: copy container id
|
<kbd>y</kbd>: copy container ID
|
||||||
<kbd>d</kbd>: remove
|
<kbd>d</kbd>: remove
|
||||||
<kbd>e</kbd>: hide/show stopped containers
|
<kbd>e</kbd>: hide/show stopped containers
|
||||||
<kbd>p</kbd>: pause
|
<kbd>p</kbd>: pause
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
## Contenedores
|
## Contenedores
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd></kbd>: copy container id
|
<kbd>y</kbd>: copy container ID
|
||||||
<kbd>d</kbd>: borrar
|
<kbd>d</kbd>: borrar
|
||||||
<kbd>e</kbd>: esconder/mostrar contenedores parados
|
<kbd>e</kbd>: esconder/mostrar contenedores parados
|
||||||
<kbd>p</kbd>: pausa
|
<kbd>p</kbd>: pausa
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
## Conteneurs
|
## Conteneurs
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd></kbd>: copy container id
|
<kbd>y</kbd>: copy container ID
|
||||||
<kbd>d</kbd>: supprimer
|
<kbd>d</kbd>: supprimer
|
||||||
<kbd>e</kbd>: cacher/montrer les conteneurs arrêtés
|
<kbd>e</kbd>: cacher/montrer les conteneurs arrêtés
|
||||||
<kbd>p</kbd>: pause
|
<kbd>p</kbd>: pause
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
## Containers
|
## Containers
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd></kbd>: copy container id
|
<kbd>y</kbd>: copy container ID
|
||||||
<kbd>d</kbd>: verwijder
|
<kbd>d</kbd>: verwijder
|
||||||
<kbd>e</kbd>: verberg gestopte containers
|
<kbd>e</kbd>: verberg gestopte containers
|
||||||
<kbd>p</kbd>: pause
|
<kbd>p</kbd>: pause
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
## Kontenery
|
## Kontenery
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd></kbd>: copy container id
|
<kbd>y</kbd>: copy container ID
|
||||||
<kbd>d</kbd>: usuń
|
<kbd>d</kbd>: usuń
|
||||||
<kbd>e</kbd>: hide/show stopped containers
|
<kbd>e</kbd>: hide/show stopped containers
|
||||||
<kbd>p</kbd>: pause
|
<kbd>p</kbd>: pause
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
## Contêineres
|
## Contêineres
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd></kbd>: copy container id
|
<kbd>y</kbd>: copy container ID
|
||||||
<kbd>d</kbd>: remover
|
<kbd>d</kbd>: remover
|
||||||
<kbd>e</kbd>: ocultar/mostrar contêineres parados
|
<kbd>e</kbd>: ocultar/mostrar contêineres parados
|
||||||
<kbd>p</kbd>: pausar
|
<kbd>p</kbd>: pausar
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
## Konteynerler
|
## Konteynerler
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd></kbd>: copy container id
|
<kbd>y</kbd>: copy container ID
|
||||||
<kbd>d</kbd>: kaldır
|
<kbd>d</kbd>: kaldır
|
||||||
<kbd>e</kbd>: hide/show stopped containers
|
<kbd>e</kbd>: hide/show stopped containers
|
||||||
<kbd>p</kbd>: pause
|
<kbd>p</kbd>: pause
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
## 容器
|
## 容器
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd></kbd>: copy container id
|
<kbd>y</kbd>: copy container ID
|
||||||
<kbd>d</kbd>: 移除
|
<kbd>d</kbd>: 移除
|
||||||
<kbd>e</kbd>: 隐藏/显示已停止的容器
|
<kbd>e</kbd>: 隐藏/显示已停止的容器
|
||||||
<kbd>p</kbd>: 暂停
|
<kbd>p</kbd>: 暂停
|
||||||
|
|
|
||||||
|
|
@ -381,5 +381,13 @@ func (c *OSCommand) CopyToClipboard(str string) error {
|
||||||
|
|
||||||
c.Log.Debug(utils.ResolvePlaceholderString("Copying '{{str}}' to clipboard", map[string]string{"str": truncated}))
|
c.Log.Debug(utils.ResolvePlaceholderString("Copying '{{str}}' to clipboard", map[string]string{"str": truncated}))
|
||||||
|
|
||||||
|
// Not needed yet
|
||||||
|
//if c.UserConfig().OS.CopyToClipboardCmd != "" {
|
||||||
|
// cmdStr := utils.ResolvePlaceholderString(c.UserConfig().OS.CopyToClipboardCmd, map[string]string{
|
||||||
|
// "text": c.Cmd.Quote(str),
|
||||||
|
// })
|
||||||
|
// return c.Cmd.NewShell(cmdStr).Run()
|
||||||
|
//}
|
||||||
|
|
||||||
return clipboard.WriteAll(str)
|
return clipboard.WriteAll(str)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package gui
|
package gui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
|
|
@ -16,15 +15,14 @@ type appStatus struct {
|
||||||
|
|
||||||
type statusManager struct {
|
type statusManager struct {
|
||||||
statuses []appStatus
|
statuses []appStatus
|
||||||
lock *sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
TickIntervalMs = 50
|
||||||
|
)
|
||||||
|
|
||||||
func (m *statusManager) removeStatus(name string) {
|
func (m *statusManager) removeStatus(name string) {
|
||||||
newStatuses := []appStatus{}
|
newStatuses := []appStatus{}
|
||||||
|
|
||||||
m.lock.Lock()
|
|
||||||
defer m.lock.Unlock()
|
|
||||||
|
|
||||||
for _, status := range m.statuses {
|
for _, status := range m.statuses {
|
||||||
if status.name != name {
|
if status.name != name {
|
||||||
newStatuses = append(newStatuses, status)
|
newStatuses = append(newStatuses, status)
|
||||||
|
|
@ -33,61 +31,40 @@ func (m *statusManager) removeStatus(name string) {
|
||||||
m.statuses = newStatuses
|
m.statuses = newStatuses
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *statusManager) addWaitingStatus(name string) {
|
func (m *statusManager) addStatus(name string, statusType string, duration int) {
|
||||||
m.lock.Lock()
|
|
||||||
defer m.lock.Unlock()
|
|
||||||
|
|
||||||
m.removeStatus(name)
|
m.removeStatus(name)
|
||||||
newStatus := appStatus{
|
newStatus := appStatus{
|
||||||
name: name,
|
name: name,
|
||||||
statusType: "waiting",
|
statusType: statusType,
|
||||||
duration: 0,
|
duration: duration,
|
||||||
}
|
}
|
||||||
m.statuses = append([]appStatus{newStatus}, m.statuses...)
|
m.statuses = append([]appStatus{newStatus}, m.statuses...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *statusManager) getStatusString() string {
|
func (m *statusManager) getStatusString() string {
|
||||||
m.lock.Lock()
|
|
||||||
defer m.lock.Unlock()
|
|
||||||
|
|
||||||
if len(m.statuses) == 0 {
|
if len(m.statuses) == 0 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
topStatus := m.statuses[0]
|
topStatus := m.statuses[0]
|
||||||
if topStatus.statusType == "waiting" {
|
if topStatus.statusType == "waiting" {
|
||||||
return topStatus.name + " " + utils.Loader()
|
return topStatus.name + " " + utils.Loader()
|
||||||
}
|
} else if topStatus.statusType == "info" {
|
||||||
return topStatus.name
|
return topStatus.name
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithStaticWaitingStatus shows a waiting status for a specific duration
|
return topStatus.name
|
||||||
func (gui *Gui) WithStaticWaitingStatus(name string, duration time.Duration) error {
|
|
||||||
return gui.WithWaitingStatus(name, func() error { time.Sleep(duration); return nil })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithWaitingStatus wraps a function and shows a waiting status while the function is still executing
|
// WithWaitingStatus wraps a function and shows a waiting status while the function is still executing
|
||||||
func (gui *Gui) WithWaitingStatus(name string, f func() error) error {
|
func (gui *Gui) WithWaitingStatus(name string, f func() error) error {
|
||||||
go func() {
|
go func() {
|
||||||
gui.statusManager.addWaitingStatus(name)
|
go gui.Notify(name, "waiting", 0)()
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
gui.statusManager.removeStatus(name)
|
gui.statusManager.removeStatus(name)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
|
||||||
ticker := time.NewTicker(time.Millisecond * 50)
|
|
||||||
defer ticker.Stop()
|
|
||||||
for range ticker.C {
|
|
||||||
appStatus := gui.statusManager.getStatusString()
|
|
||||||
if appStatus == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := gui.renderString(gui.g, "appStatus", appStatus); err != nil {
|
|
||||||
gui.Log.Warn(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := f(); err != nil {
|
if err := f(); err != nil {
|
||||||
gui.g.Update(func(g *gocui.Gui) error {
|
gui.g.Update(func(g *gocui.Gui) error {
|
||||||
return gui.createErrorPanel(err.Error())
|
return gui.createErrorPanel(err.Error())
|
||||||
|
|
@ -97,3 +74,37 @@ func (gui *Gui) WithWaitingStatus(name string, f func() error) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Notify sends static notification to the user.
|
||||||
|
// duration of 0 will disable the self-cleaning of the notification
|
||||||
|
func (gui *Gui) Notify(name string, statusType string, duration int) func() {
|
||||||
|
return func() {
|
||||||
|
gui.statusManager.addStatus(name, statusType, duration)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
gui.statusManager.removeStatus(name)
|
||||||
|
}()
|
||||||
|
|
||||||
|
ticker := time.NewTicker(time.Millisecond * TickIntervalMs)
|
||||||
|
tickCount := 0
|
||||||
|
endTick := duration * 1000 / TickIntervalMs
|
||||||
|
|
||||||
|
defer ticker.Stop()
|
||||||
|
for range ticker.C {
|
||||||
|
tickCount++
|
||||||
|
// If no duration, don't terminate early
|
||||||
|
if duration > 0 && tickCount >= endTick {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
appStatus := gui.statusManager.getStatusString()
|
||||||
|
if appStatus == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gui.renderString(gui.g, "appStatus", appStatus); err != nil {
|
||||||
|
gui.Log.Warn(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -252,8 +252,10 @@ func (gui *Gui) refreshContainersAndServices() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// keep track of current service selected so that we can reposition our cursor if it moves position in the list
|
// keep track of current service selected so that we can reposition our cursor if it moves position in the list
|
||||||
|
gui.DockerCommand.ServiceMutex.Lock()
|
||||||
originalSelectedLineIdx := gui.Panels.Services.SelectedIdx
|
originalSelectedLineIdx := gui.Panels.Services.SelectedIdx
|
||||||
selectedService, isServiceSelected := gui.Panels.Services.List.TryGet(originalSelectedLineIdx)
|
selectedService, isServiceSelected := gui.Panels.Services.List.TryGet(originalSelectedLineIdx)
|
||||||
|
gui.DockerCommand.ServiceMutex.Unlock()
|
||||||
|
|
||||||
containers, services, err := gui.DockerCommand.RefreshContainersAndServices(
|
containers, services, err := gui.DockerCommand.RefreshContainersAndServices(
|
||||||
gui.Panels.Services.List.GetAllItems(),
|
gui.Panels.Services.List.GetAllItems(),
|
||||||
|
|
@ -364,10 +366,8 @@ func (gui *Gui) handleCopyContainerId(g *gocui.Gui, v *gocui.View) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = gui.WithStaticWaitingStatus(fmt.Sprintf(gui.Tr.CopyContainerIdStatus, utils.TruncateWithEllipsis(ctr.ID, 10)), time.Second*2)
|
formattedStatusText := fmt.Sprintf("Copied %s to clipboard", utils.TruncateWithEllipsis(ctr.ID, 10))
|
||||||
if err != nil {
|
go gui.Notify(formattedStatusText, "info", 3)()
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return gui.OSCommand.CopyToClipboard(ctr.ID)
|
return gui.OSCommand.CopyToClipboard(ctr.ID)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/events"
|
"github.com/docker/docker/api/types/events"
|
||||||
|
|
@ -147,9 +146,7 @@ func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand
|
||||||
State: initialState,
|
State: initialState,
|
||||||
Config: config,
|
Config: config,
|
||||||
Tr: tr,
|
Tr: tr,
|
||||||
statusManager: &statusManager{
|
statusManager: &statusManager{},
|
||||||
lock: &sync.Mutex{},
|
|
||||||
},
|
|
||||||
taskManager: tasks.NewTaskManager(log, tr),
|
taskManager: tasks.NewTaskManager(log, tr),
|
||||||
ErrorChan: errorChan,
|
ErrorChan: errorChan,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -187,7 +187,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ViewName: "containers",
|
ViewName: "containers",
|
||||||
Key: gocui.KeyCtrlO,
|
Key: 'y',
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
Handler: gui.handleCopyContainerId,
|
Handler: gui.handleCopyContainerId,
|
||||||
Description: gui.Tr.CopyContainerId,
|
Description: gui.Tr.CopyContainerId,
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,6 @@ type TranslationSet struct {
|
||||||
ViewLogs string
|
ViewLogs string
|
||||||
UpProject string
|
UpProject string
|
||||||
DownProject string
|
DownProject string
|
||||||
CopyContainerId string
|
|
||||||
CopyContainerIdStatus string
|
|
||||||
ServicesTitle string
|
ServicesTitle string
|
||||||
ContainersTitle string
|
ContainersTitle string
|
||||||
StandaloneContainersTitle string
|
StandaloneContainersTitle string
|
||||||
|
|
@ -111,6 +109,7 @@ type TranslationSet struct {
|
||||||
FilterList string
|
FilterList string
|
||||||
OpenInBrowser string
|
OpenInBrowser string
|
||||||
SortContainersByState string
|
SortContainersByState string
|
||||||
|
CopyContainerId string
|
||||||
|
|
||||||
LogsTitle string
|
LogsTitle string
|
||||||
ConfigTitle string
|
ConfigTitle string
|
||||||
|
|
@ -199,8 +198,6 @@ func englishSet() TranslationSet {
|
||||||
ViewLogs: "view logs",
|
ViewLogs: "view logs",
|
||||||
UpProject: "up project",
|
UpProject: "up project",
|
||||||
DownProject: "down project",
|
DownProject: "down project",
|
||||||
CopyContainerId: "copy container id",
|
|
||||||
CopyContainerIdStatus: "Copied %s to clipboard",
|
|
||||||
RemoveImage: "remove image",
|
RemoveImage: "remove image",
|
||||||
RemoveVolume: "remove volume",
|
RemoveVolume: "remove volume",
|
||||||
RemoveNetwork: "remove network",
|
RemoveNetwork: "remove network",
|
||||||
|
|
@ -220,6 +217,7 @@ func englishSet() TranslationSet {
|
||||||
FilterList: "filter list",
|
FilterList: "filter list",
|
||||||
OpenInBrowser: "open in browser (first port is http)",
|
OpenInBrowser: "open in browser (first port is http)",
|
||||||
SortContainersByState: "sort containers by state",
|
SortContainersByState: "sort containers by state",
|
||||||
|
CopyContainerId: "copy container ID",
|
||||||
|
|
||||||
GlobalTitle: "Global",
|
GlobalTitle: "Global",
|
||||||
MainTitle: "Main",
|
MainTitle: "Main",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue