refactor(ui/chat): migrate picker callbacks to event-based architecture (#769)

This commit is contained in:
ayn2op 2026-03-17 18:56:43 -04:00 • committed by GitHub
parent 3656acba48
commit 3f8a6533da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 93 additions and 69 deletions

View file

@ -7,6 +7,7 @@ import (
"github.com/ayn2op/tview"
"github.com/ayn2op/tview/help"
"github.com/ayn2op/tview/keybind"
"github.com/gdamore/tcell/v3"
)
type attachmentItem struct {
@ -39,8 +40,6 @@ func newAttachmentsPicker(cfg *config.Config, chatView *Model) *attachmentsPicke
SetFooterStyle(cfg.Theme.Footer.ActiveStyle.Style)
ap.SetTitle("Attachments")
ap.SetSelectedFunc(ap.onSelected)
ap.SetCancelFunc(ap.close)
ap.SetKeyMap(&picker.KeyMap{
Cancel: cfg.Keybinds.Picker.Cancel.Keybind,
Up: cfg.Keybinds.Picker.Up.Keybind,
@ -70,23 +69,32 @@ func (ap *attachmentsPicker) SetItems(items []attachmentItem) {
ap.Update()
}
func (ap *attachmentsPicker) onSelected(item picker.Item) {
index, ok := item.Reference.(int)
if !ok {
return
}
if index < 0 || index >= len(ap.items) {
return
}
ap.items[index].open()
ap.close()
}
func (ap *attachmentsPicker) close() {
ap.chatView.RemoveLayer(attachmentsListLayerName)
ap.chatView.app.SetFocus(ap.chatView.messagesList)
}
func (ap *attachmentsPicker) HandleEvent(event tcell.Event) tview.Command {
switch event := event.(type) {
case *picker.SelectedEvent:
index, ok := event.Reference.(int)
if !ok {
return nil
}
if index < 0 || index >= len(ap.items) {
return nil
}
ap.items[index].open()
ap.close()
return nil
case *picker.CancelEvent:
ap.close()
return nil
}
return ap.Picker.HandleEvent(event)
}
func (ap *attachmentsPicker) ShortHelp() []keybind.Keybind {
cfg := ap.cfg.Keybinds.Picker
return []keybind.Keybind{cfg.Up.Keybind, cfg.Down.Keybind, cfg.Select.Keybind, cfg.Cancel.Keybind}

View file

@ -11,6 +11,7 @@ import (
"github.com/ayn2op/tview/help"
"github.com/ayn2op/tview/keybind"
"github.com/diamondburned/arikawa/v3/discord"
"github.com/gdamore/tcell/v3"
)
type channelsPicker struct {
@ -34,7 +35,6 @@ func newChannelsPicker(cfg *config.Config, chatView *Model) *channelsPicker {
SetTitleStyle(cfg.Theme.Title.ActiveStyle.Style).
SetFooterStyle(cfg.Theme.Footer.ActiveStyle.Style)
cp.SetSelectedFunc(cp.onSelected)
cp.SetTitle("Channels")
cp.SetScrollBarVisibility(cfg.Theme.ScrollBar.Visibility.ScrollBarVisibility)
cp.SetScrollBar(tview.NewScrollBar().
@ -52,31 +52,39 @@ func newChannelsPicker(cfg *config.Config, chatView *Model) *channelsPicker {
return cp
}
func (cp *channelsPicker) onSelected(item picker.Item) {
channelID, ok := item.Reference.(discord.ChannelID)
if !ok || !channelID.IsValid() {
return
}
func (cp *channelsPicker) HandleEvent(event tcell.Event) tview.Command {
switch event := event.(type) {
case *picker.SelectedEvent:
channelID, ok := event.Reference.(discord.ChannelID)
if !ok || !channelID.IsValid() {
return nil
}
channel, err := cp.chatView.state.Cabinet.Channel(channelID)
if err != nil {
slog.Error("failed to get channel from state", "err", err, "channel_id", channelID)
return
}
channel, err := cp.chatView.state.Cabinet.Channel(channelID)
if err != nil {
slog.Error("failed to get channel from state", "err", err, "channel_id", channelID)
return nil
}
node := cp.chatView.guildsTree.findNodeByChannelID(channel.ID)
if node == nil {
slog.Error("failed to locate channel in tree", "channel_id", channel.ID)
return
}
node := cp.chatView.guildsTree.findNodeByChannelID(channel.ID)
if node == nil {
slog.Error("failed to locate channel in tree", "channel_id", channel.ID)
return nil
}
cp.chatView.guildsTree.expandPathToNode(node)
cp.chatView.guildsTree.SetCurrentNode(node)
if channel.Type != discord.GuildCategory {
cp.chatView.guildsTree.onSelected(node)
cp.chatView.guildsTree.expandPathToNode(node)
cp.chatView.guildsTree.SetCurrentNode(node)
if channel.Type != discord.GuildCategory {
cp.chatView.guildsTree.onSelected(node)
}
cp.chatView.closePicker()
cp.chatView.focusMessageInput()
return nil
case *picker.CancelEvent:
cp.chatView.closePicker()
return nil
}
cp.chatView.closePicker()
cp.chatView.focusMessageInput()
return cp.Picker.HandleEvent(event)
}
func (cp *channelsPicker) update() {

View file

@ -74,7 +74,6 @@ func NewModel(app *tview.Application, cfg *config.Config, token string) *Model {
m.messagesList = newMessagesList(cfg, m)
m.messageInput = newMessageInput(cfg, m)
m.channelsPicker = newChannelsPicker(cfg, m)
m.channelsPicker.SetCancelFunc(m.closePicker)
m.SetBackgroundLayerStyle(m.cfg.Theme.Dialog.BackgroundStyle.Style)
m.buildLayout()

34
pkg/picker/events.go Normal file
View file

@ -0,0 +1,34 @@
package picker
import (
"github.com/ayn2op/tview"
"github.com/gdamore/tcell/v3"
)
type SelectedEvent struct {
tcell.EventTime
Item
}
func newSelectedEvent(item Item) *SelectedEvent {
return &SelectedEvent{Item: item}
}
func (p *Picker) _select() tview.Command {
index := p.list.Cursor()
if index >= 0 && index < len(p.filtered) {
item := p.filtered[index]
return func() tcell.Event {
return newSelectedEvent(item)
}
}
return nil
}
type CancelEvent struct{ tcell.EventTime }
func cancel() tview.Command {
return func() tcell.Event {
return &CancelEvent{}
}
}

View file

@ -9,19 +9,15 @@ import (
"github.com/sahilm/fuzzy"
)
type (
SelectedFunc func(item Item)
CancelFunc func()
)
// bottom border + value
const inputHeight = 2
type Picker struct {
*flex.Model
input *tview.InputField
list *list.Model
onSelected SelectedFunc
onCancel CancelFunc
keyMap *KeyMap
keyMap *KeyMap
items Items
filtered Items
@ -49,8 +45,7 @@ func New() *Picker {
p.
SetDirection(flex.DirectionRow).
// bottom border + value
AddItem(p.input, 2, 0, true).
AddItem(p.input, inputHeight, 0, true).
AddItem(p.list, 0, 1, false)
p.Update()
@ -97,14 +92,6 @@ func (p *Picker) SetScrollBar(scrollBar *tview.ScrollBar) {
p.list.SetScrollBar(scrollBar)
}
func (p *Picker) SetSelectedFunc(onSelected SelectedFunc) {
p.onSelected = onSelected
}
func (p *Picker) SetCancelFunc(onCancel CancelFunc) {
p.onCancel = onCancel
}
func (p *Picker) ClearInput() {
p.input.SetText("")
}
@ -128,15 +115,6 @@ func (p *Picker) Update() {
p.onInputChanged("")
}
func (p *Picker) onListSelected(index int) {
if p.onSelected != nil {
if index >= 0 && index < len(p.filtered) {
item := p.filtered[index]
p.onSelected(item)
}
}
}
func (p *Picker) onInputChanged(text string) {
var fuzzied Items
if text == "" {
@ -167,14 +145,11 @@ func (p *Picker) HandleEvent(event tcell.Event) tview.Command {
case keybind.Matches(event, p.keyMap.Bottom):
p.list.HandleEvent(tcell.NewEventKey(tcell.KeyEnd, "", tcell.ModNone))
return nil
case keybind.Matches(event, p.keyMap.Select):
p.onListSelected(p.list.Cursor())
return nil
return p._select()
case keybind.Matches(event, p.keyMap.Cancel):
if p.onCancel != nil {
p.onCancel()
}
return nil
return cancel()
}
}