Rework static notification handling

This commit is contained in:
Clément PÉAU 2025-05-03 20:32:36 +02:00
parent 59f39d76cc
commit 70e96c6a49
15 changed files with 73 additions and 59 deletions

View file

@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
## Container
<pre>
<kbd></kbd>: copy container id
<kbd>y</kbd>: copy container ID
<kbd>d</kbd>: entfernen
<kbd>e</kbd>: hide/show stopped containers
<kbd>p</kbd>: pause

View file

@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
## Containers
<pre>
<kbd></kbd>: copy container id
<kbd>y</kbd>: copy container ID
<kbd>d</kbd>: remove
<kbd>e</kbd>: hide/show stopped containers
<kbd>p</kbd>: pause

View file

@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
## Contenedores
<pre>
<kbd></kbd>: copy container id
<kbd>y</kbd>: copy container ID
<kbd>d</kbd>: borrar
<kbd>e</kbd>: esconder/mostrar contenedores parados
<kbd>p</kbd>: pausa

View file

@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
## Conteneurs
<pre>
<kbd></kbd>: copy container id
<kbd>y</kbd>: copy container ID
<kbd>d</kbd>: supprimer
<kbd>e</kbd>: cacher/montrer les conteneurs arrêtés
<kbd>p</kbd>: pause

View file

@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
## Containers
<pre>
<kbd></kbd>: copy container id
<kbd>y</kbd>: copy container ID
<kbd>d</kbd>: verwijder
<kbd>e</kbd>: verberg gestopte containers
<kbd>p</kbd>: pause

View file

@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
## Kontenery
<pre>
<kbd></kbd>: copy container id
<kbd>y</kbd>: copy container ID
<kbd>d</kbd>: usuń
<kbd>e</kbd>: hide/show stopped containers
<kbd>p</kbd>: pause

View file

@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
## Contêineres
<pre>
<kbd></kbd>: copy container id
<kbd>y</kbd>: copy container ID
<kbd>d</kbd>: remover
<kbd>e</kbd>: ocultar/mostrar contêineres parados
<kbd>p</kbd>: pausar

View file

@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
## Konteynerler
<pre>
<kbd></kbd>: copy container id
<kbd>y</kbd>: copy container ID
<kbd>d</kbd>: kaldır
<kbd>e</kbd>: hide/show stopped containers
<kbd>p</kbd>: pause

View file

@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
## 容器
<pre>
<kbd></kbd>: copy container id
<kbd>y</kbd>: copy container ID
<kbd>d</kbd>: 移除
<kbd>e</kbd>: 隐藏/显示已停止的容器
<kbd>p</kbd>: 暂停

View file

@ -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}))
// 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)
}

View file

@ -1,7 +1,6 @@
package gui
import (
"sync"
"time"
"github.com/jesseduffield/gocui"
@ -16,15 +15,14 @@ type appStatus struct {
type statusManager struct {
statuses []appStatus
lock *sync.Mutex
}
const (
TickIntervalMs = 50
)
func (m *statusManager) removeStatus(name string) {
newStatuses := []appStatus{}
m.lock.Lock()
defer m.lock.Unlock()
for _, status := range m.statuses {
if status.name != name {
newStatuses = append(newStatuses, status)
@ -33,61 +31,40 @@ func (m *statusManager) removeStatus(name string) {
m.statuses = newStatuses
}
func (m *statusManager) addWaitingStatus(name string) {
m.lock.Lock()
defer m.lock.Unlock()
func (m *statusManager) addStatus(name string, statusType string, duration int) {
m.removeStatus(name)
newStatus := appStatus{
name: name,
statusType: "waiting",
duration: 0,
statusType: statusType,
duration: duration,
}
m.statuses = append([]appStatus{newStatus}, m.statuses...)
}
func (m *statusManager) getStatusString() string {
m.lock.Lock()
defer m.lock.Unlock()
if len(m.statuses) == 0 {
return ""
}
topStatus := m.statuses[0]
if topStatus.statusType == "waiting" {
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
func (gui *Gui) WithStaticWaitingStatus(name string, duration time.Duration) error {
return gui.WithWaitingStatus(name, func() error { time.Sleep(duration); return nil })
return topStatus.name
}
// 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 {
go func() {
gui.statusManager.addWaitingStatus(name)
go gui.Notify(name, "waiting", 0)()
defer func() {
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 {
gui.g.Update(func(g *gocui.Gui) error {
return gui.createErrorPanel(err.Error())
@ -97,3 +74,37 @@ func (gui *Gui) WithWaitingStatus(name string, f func() error) error {
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)
}
}
}
}

View file

@ -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
gui.DockerCommand.ServiceMutex.Lock()
originalSelectedLineIdx := gui.Panels.Services.SelectedIdx
selectedService, isServiceSelected := gui.Panels.Services.List.TryGet(originalSelectedLineIdx)
gui.DockerCommand.ServiceMutex.Unlock()
containers, services, err := gui.DockerCommand.RefreshContainersAndServices(
gui.Panels.Services.List.GetAllItems(),
@ -364,10 +366,8 @@ func (gui *Gui) handleCopyContainerId(g *gocui.Gui, v *gocui.View) error {
return nil
}
err = gui.WithStaticWaitingStatus(fmt.Sprintf(gui.Tr.CopyContainerIdStatus, utils.TruncateWithEllipsis(ctr.ID, 10)), time.Second*2)
if err != nil {
return err
}
formattedStatusText := fmt.Sprintf("Copied %s to clipboard", utils.TruncateWithEllipsis(ctr.ID, 10))
go gui.Notify(formattedStatusText, "info", 3)()
return gui.OSCommand.CopyToClipboard(ctr.ID)
}

View file

@ -4,7 +4,6 @@ import (
"context"
"os"
"strings"
"sync"
"time"
"github.com/docker/docker/api/types/events"
@ -147,11 +146,9 @@ func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand
State: initialState,
Config: config,
Tr: tr,
statusManager: &statusManager{
lock: &sync.Mutex{},
},
taskManager: tasks.NewTaskManager(log, tr),
ErrorChan: errorChan,
statusManager: &statusManager{},
taskManager: tasks.NewTaskManager(log, tr),
ErrorChan: errorChan,
}
deadlock.Opts.Disable = !gui.Config.Debug

View file

@ -187,7 +187,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
},
{
ViewName: "containers",
Key: gocui.KeyCtrlO,
Key: 'y',
Modifier: gocui.ModNone,
Handler: gui.handleCopyContainerId,
Description: gui.Tr.CopyContainerId,

View file

@ -67,8 +67,6 @@ type TranslationSet struct {
ViewLogs string
UpProject string
DownProject string
CopyContainerId string
CopyContainerIdStatus string
ServicesTitle string
ContainersTitle string
StandaloneContainersTitle string
@ -111,6 +109,7 @@ type TranslationSet struct {
FilterList string
OpenInBrowser string
SortContainersByState string
CopyContainerId string
LogsTitle string
ConfigTitle string
@ -199,8 +198,6 @@ func englishSet() TranslationSet {
ViewLogs: "view logs",
UpProject: "up project",
DownProject: "down project",
CopyContainerId: "copy container id",
CopyContainerIdStatus: "Copied %s to clipboard",
RemoveImage: "remove image",
RemoveVolume: "remove volume",
RemoveNetwork: "remove network",
@ -220,6 +217,7 @@ func englishSet() TranslationSet {
FilterList: "filter list",
OpenInBrowser: "open in browser (first port is http)",
SortContainersByState: "sort containers by state",
CopyContainerId: "copy container ID",
GlobalTitle: "Global",
MainTitle: "Main",