From 1335ac814b44fc1544d470f25772514bb2ce851e Mon Sep 17 00:00:00 2001 From: Rustam Gilyazov <16064414+rusq@users.noreply.github.com> Date: Mon, 28 Oct 2024 20:36:57 +1000 Subject: [PATCH] fix ui glitches --- cmd/slackdump/internal/dump/wizard.go | 4 +- cmd/slackdump/internal/ui/cfgui/cfgui.go | 47 ++++++++++++++++++- cmd/slackdump/internal/ui/cfgui/model.go | 18 +++---- cmd/slackdump/internal/ui/dumpui/model.go | 39 ++++++++------- .../internal/ui/updaters/commands.go | 16 +++++-- go.mod | 4 +- go.sum | 4 ++ 7 files changed, 97 insertions(+), 35 deletions(-) diff --git a/cmd/slackdump/internal/dump/wizard.go b/cmd/slackdump/internal/dump/wizard.go index 953edcc2..c96609ba 100644 --- a/cmd/slackdump/internal/dump/wizard.go +++ b/cmd/slackdump/internal/dump/wizard.go @@ -56,9 +56,9 @@ func (o *options) configuration() cfgui.Configuration { Name: "Input", Params: []cfgui.Parameter{ { - Name: "Channel IDs or URLs", + Name: "* Channel IDs or URLs", Value: entryList, - Description: "List of channel IDs or URLs to dump", + Description: "List of channel IDs or URLs to dump (REQUIRED)", Inline: true, Updater: updaters.NewString(&entryList, "", true, validateEntryList), }, diff --git a/cmd/slackdump/internal/ui/cfgui/cfgui.go b/cmd/slackdump/internal/ui/cfgui/cfgui.go index ed0c79a7..4985577c 100644 --- a/cmd/slackdump/internal/ui/cfgui/cfgui.go +++ b/cmd/slackdump/internal/ui/cfgui/cfgui.go @@ -4,13 +4,56 @@ import ( "context" tea "github.com/charmbracelet/bubbletea" + "github.com/rusq/slackdump/v3/cmd/slackdump/internal/ui/updaters" ) +// programWrap wraps the UI model to implement the tea.Model interface with +// tea.Quit message emitted when the user presses ESC or Ctrl+C. +type programWrap struct { + m *Model + finishing bool +} + +func newProgramWrap(m *Model) tea.Model { + return programWrap{m: m} +} + +func (m programWrap) Init() tea.Cmd { + return m.m.Init() +} + +func (m programWrap) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var cmds []tea.Cmd + + switch msg := msg.(type) { + case updaters.WMClose: + if msg.WndID == ModelID { + m.finishing = true + cmds = append(cmds, tea.Quit) + } + } + + mod, cmd := m.m.Update(msg) + if mod, ok := mod.(*Model); ok { + m.m = mod + } + cmds = append(cmds, cmd) + + return m, tea.Batch(cmds...) +} + +func (m programWrap) View() string { + if m.finishing { + return "" + } + return m.m.View() +} + // Global initialises and runs the configuration UI. func Global(ctx context.Context) error { m := NewConfigUI(DefaultStyle(), globalConfig) m.SetFocus(true) - p := tea.NewProgram(m) + p := tea.NewProgram(newProgramWrap(m)) _, err := p.Run() return err } @@ -20,7 +63,7 @@ func GlobalConfig() Configuration { } func Local(ctx context.Context, cfgFn func() Configuration) error { - p := tea.NewProgram(NewConfigUI(DefaultStyle(), cfgFn)) + p := tea.NewProgram(newProgramWrap(NewConfigUI(DefaultStyle(), cfgFn))) _, err := p.Run() return err } diff --git a/cmd/slackdump/internal/ui/cfgui/model.go b/cmd/slackdump/internal/ui/cfgui/model.go index fbe124ac..0ba8344a 100644 --- a/cmd/slackdump/internal/ui/cfgui/model.go +++ b/cmd/slackdump/internal/ui/cfgui/model.go @@ -12,6 +12,8 @@ import ( "github.com/rusq/slackdump/v3/cmd/slackdump/internal/ui/updaters" ) +const ModelID = "cfgui" + const ( sEmpty = "" sTrue = "[x]" @@ -86,9 +88,13 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case updaters.WMClose: // child sends a close message - m.state = selecting - m.child = nil - cmds = append(cmds, refreshCfgCmd) + if msg.WndID == updaters.ModelID { + m.state = selecting + m.child = nil + cmds = append(cmds, refreshCfgCmd) + } else if msg.WndID == ModelID { + m.finished = true + } case tea.KeyMsg: switch { case key.Matches(msg, m.keymap.Up): @@ -126,12 +132,8 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cmds = append(cmds, m.child.Init()) } case key.Matches(msg, m.keymap.Quit): - // child is active - if m.state != selecting { - break - } m.finished = true - return m, tea.Quit + cmds = append(cmds, updaters.CmdClose(ModelID)) } } diff --git a/cmd/slackdump/internal/ui/dumpui/model.go b/cmd/slackdump/internal/ui/dumpui/model.go index 985172c7..f9ee6951 100644 --- a/cmd/slackdump/internal/ui/dumpui/model.go +++ b/cmd/slackdump/internal/ui/dumpui/model.go @@ -8,6 +8,7 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/rusq/slackdump/v3/cmd/slackdump/internal/ui" + "github.com/rusq/slackdump/v3/cmd/slackdump/internal/ui/updaters" ) type Model struct { @@ -44,21 +45,22 @@ func (m *Model) Init() tea.Cmd { } func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var cmds []tea.Cmd child := m.items[m.cursor].Model - if child != nil && child.IsFocused() { + if !m.focused { + if wmclose, ok := msg.(updaters.WMClose); ok && wmclose.WndID == "cfgui" { + child.Reset() + child.SetFocus(false) + m.SetFocus(true) + return m, nil + } ch, cmd := child.Update(msg) - m.items[m.cursor].Model = ch.(FocusModel) - if cmd != nil && cmd() != nil { - if _, ok := cmd().(tea.QuitMsg); ok { - // if child quit, we need to set focus back to the menu. - m.SetFocus(true) - child.SetFocus(false) - child.Reset() // reset the configuration from finished state. - return m, nil - } + if ch, ok := ch.(FocusModel); ok { + m.items[m.cursor].Model = ch } - return m, cmd + cmds = append(cmds, cmd) + return m, tea.Batch(cmds...) } switch msg := msg.(type) { @@ -67,6 +69,7 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case key.Matches(msg, m.Keymap.Quit): m.finishing = true m.Cancelled = true + m.Selected = m.items[m.cursor] return m, tea.Quit case key.Matches(msg, m.Keymap.Up): for { @@ -91,19 +94,21 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.items[m.cursor].Separator || (dfn != nil && dfn() != nil) { return m, nil } - m.Selected = m.items[m.cursor] if child := m.items[m.cursor].Model; child != nil { + // If there is a child model, focus it. m.SetFocus(false) child.SetFocus(true) - return m, nil + cmds = append(cmds, child.Init()) + } else { + // otherwise, return selected item and quit + m.Selected = m.items[m.cursor] + m.finishing = true + cmds = append(cmds, tea.Quit) } - - m.finishing = true - return m, tea.Quit } } - return m, nil + return m, tea.Batch(cmds...) } func (m *Model) SetFocus(b bool) { diff --git a/cmd/slackdump/internal/ui/updaters/commands.go b/cmd/slackdump/internal/ui/updaters/commands.go index 3ca49c49..84650554 100644 --- a/cmd/slackdump/internal/ui/updaters/commands.go +++ b/cmd/slackdump/internal/ui/updaters/commands.go @@ -2,15 +2,23 @@ package updaters import tea "github.com/charmbracelet/bubbletea" -type WMClose struct{} +const ModelID = "updater" + +type WMClose struct { + // WndID is the window ID to close. If empty, the current window + // will be closed. + WndID string +} // OnClose defines the global command to close the program. It is set // by default to [CmdClose], but if running standalone, one must set it // to [tea.Quit], otherwise the program will not exit. -var OnClose = CmdClose +var OnClose = CmdClose(ModelID) -func CmdClose() tea.Msg { - return WMClose{} +func CmdClose(id string) func() tea.Msg { + return func() tea.Msg { + return WMClose{id} + } } // WMError is sent when an error occurs, for example, a validation error, diff --git a/go.mod b/go.mod index 39415fea..61cf3b94 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/charmbracelet/lipgloss v0.13.1 github.com/davecgh/go-spew v1.1.1 github.com/enescakir/emoji v1.0.0 - github.com/fatih/color v1.17.0 + github.com/fatih/color v1.18.0 github.com/go-chi/chi/v5 v5.0.12 github.com/go-playground/locales v0.14.1 github.com/go-playground/universal-translator v0.18.1 @@ -48,7 +48,7 @@ require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/catppuccin/go v0.2.0 // indirect github.com/charmbracelet/x/ansi v0.4.0 // indirect - github.com/charmbracelet/x/exp/strings v0.0.0-20241017213443-f2394f742aee // indirect + github.com/charmbracelet/x/exp/strings v0.0.0-20241025155609-902b1d1de0be // indirect github.com/charmbracelet/x/term v0.2.0 // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect github.com/denisbrodbeck/machineid v1.0.1 // indirect diff --git a/go.sum b/go.sum index e18ada94..f8dc4966 100644 --- a/go.sum +++ b/go.sum @@ -24,6 +24,8 @@ github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAM github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/charmbracelet/x/exp/strings v0.0.0-20241017213443-f2394f742aee h1:/tPkdvFmQ+5LOvHYMiVbqjW5JQdehwRHPFfzYClDU18= github.com/charmbracelet/x/exp/strings v0.0.0-20241017213443-f2394f742aee/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ= +github.com/charmbracelet/x/exp/strings v0.0.0-20241025155609-902b1d1de0be h1:iNAmt6rYy0dDis7zrsD2thCIVMc/EFNmuqRQ/AZ0VUQ= +github.com/charmbracelet/x/exp/strings v0.0.0-20241025155609-902b1d1de0be/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ= github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0= github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -42,6 +44,8 @@ github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6 github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc= github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc= github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=