Преглед на файлове

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

ayn2op преди 1 месец
родител
ревизия
3f8a6533da
променени са 5 файла, в които са добавени 93 реда и са изтрити 69 реда
  1. 22 14
      internal/ui/chat/attachments_picker.go
  2. 30 22
      internal/ui/chat/channels_picker.go
  3. 0 1
      internal/ui/chat/model.go
  4. 34 0
      pkg/picker/events.go
  5. 7 32
      pkg/picker/picker.go

+ 22 - 14
internal/ui/chat/attachments_picker.go

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

+ 30 - 22
internal/ui/chat/channels_picker.go

@@ -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() {

+ 0 - 1
internal/ui/chat/model.go

@@ -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 - 0
pkg/picker/events.go

@@ -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{}
+	}
+}

+ 7 - 32
pkg/picker/picker.go

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