95 lines
2.3 KiB
Go
95 lines
2.3 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
tea "charm.land/bubbletea/v2"
|
|
|
|
"gitea.oxpoe.qzz.io/oxpoe/tia-love/internal/docker"
|
|
"gitea.oxpoe.qzz.io/oxpoe/tia-love/internal/i18n"
|
|
)
|
|
|
|
// eventMsg — satu event Docker dari stream (volume rendah; 1 event = 1 render).
|
|
type eventMsg struct {
|
|
id int64
|
|
ev docker.Event
|
|
}
|
|
type eventsClosedMsg struct{ id int64 }
|
|
type eventsRetryMsg struct{ id int64 }
|
|
type refreshTickMsg struct{ gen int64 }
|
|
|
|
const (
|
|
activityMax = 20
|
|
refreshDebounce = 500 * time.Millisecond
|
|
eventsRetryDelay = 5 * time.Second
|
|
)
|
|
|
|
// startEvents — (re)buka stream event daemon. Dipanggil dari New (init), `r`,
|
|
// dan retry otomatis saat koneksi putus.
|
|
func (m *Model) startEvents() tea.Cmd {
|
|
if m.eventsCancel != nil {
|
|
m.eventsCancel()
|
|
m.eventsCancel = nil
|
|
}
|
|
m.eventsCh = nil
|
|
if m.cl == nil {
|
|
return nil
|
|
}
|
|
m.eventsID++
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
m.eventsCancel = cancel
|
|
m.eventsCh = m.cl.StreamEvents(ctx)
|
|
return waitEvent(m.eventsID, m.eventsCh)
|
|
}
|
|
|
|
func waitEvent(id int64, ch <-chan docker.Event) tea.Cmd {
|
|
return func() tea.Msg {
|
|
ev, ok := <-ch
|
|
if !ok {
|
|
return eventsClosedMsg{id: id}
|
|
}
|
|
return eventMsg{id: id, ev: ev}
|
|
}
|
|
}
|
|
|
|
func refreshAfter(gen int64) tea.Cmd {
|
|
return tea.Tick(refreshDebounce, func(time.Time) tea.Msg { return refreshTickMsg{gen: gen} })
|
|
}
|
|
|
|
func eventsRetry(id int64) tea.Cmd {
|
|
return tea.Tick(eventsRetryDelay, func(time.Time) tea.Msg { return eventsRetryMsg{id: id} })
|
|
}
|
|
|
|
// evTypeKey — jenis event → kunci katalog bagian (untuk label terjemahan).
|
|
func evTypeKey(t string) string {
|
|
switch t {
|
|
case "image":
|
|
return "images"
|
|
case "volume":
|
|
return "volumes"
|
|
case "network":
|
|
return "networks"
|
|
default:
|
|
return "containers"
|
|
}
|
|
}
|
|
|
|
// activityLines — event terbaru di atas (untuk panel Aktivitas di beranda).
|
|
func (m Model) activityLines(w, h int) []string {
|
|
if len(m.activity) == 0 {
|
|
return []string{dim(i18n.T("activity.waiting"))}
|
|
}
|
|
out := make([]string, 0, h)
|
|
for i := len(m.activity) - 1; i >= 0 && len(out) < h; i-- {
|
|
ev := m.activity[i]
|
|
label := strings.ToLower(i18n.T("section." + evTypeKey(ev.Type)))
|
|
out = append(out, dim(ev.At.Format("15:04:05"))+" "+
|
|
dim("["+label+"]")+" "+
|
|
text("'"+truncate(ev.Actor, 20)+"'")+" "+
|
|
dim(truncate(ev.Action, 24)))
|
|
}
|
|
return out
|
|
}
|