diff --git a/go.mod b/go.mod index 1cf876d..7e1cd6b 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/andybalholm/brotli v1.2.2 github.com/ayn2op/arikawa/v3 v3.0.0-20260708075022-9cc760fe507e github.com/ayn2op/ningen/v3 v3.0.1-0.20260708075321-0ba1ffc70bd6 - github.com/ayn2op/tview v0.0.0-20260803165347-d8123f4ec53c + github.com/ayn2op/tview v0.0.0-20260805051036-f8d695faec51 github.com/bogdanfinn/fhttp v0.6.8 github.com/bogdanfinn/tls-client v1.15.1 github.com/gdamore/tcell/v3 v3.4.1 @@ -56,7 +56,7 @@ require ( golang.org/x/crypto v0.54.0 // indirect golang.org/x/exp/shiny v0.0.0-20260727155853-b88d891fe743 // indirect golang.org/x/image v0.44.0 // indirect - golang.org/x/mobile v0.0.0-20260730202154-c700fe717e6e // indirect + golang.org/x/mobile v0.0.0-20260803200217-62cee1672c8e // indirect golang.org/x/net v0.57.0 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/term v0.45.0 // indirect diff --git a/go.sum b/go.sum index e51d353..dad288f 100644 --- a/go.sum +++ b/go.sum @@ -14,8 +14,8 @@ github.com/ayn2op/arikawa/v3 v3.0.0-20260708075022-9cc760fe507e h1:rsMifRXwxkbha github.com/ayn2op/arikawa/v3 v3.0.0-20260708075022-9cc760fe507e/go.mod h1:gqklpHK7WHYTzpd1ZY6Ho/p9GURBghzBCzRiBYpaUP8= github.com/ayn2op/ningen/v3 v3.0.1-0.20260708075321-0ba1ffc70bd6 h1:/khHm6G+GWwXpmwA8bJoaXoUhubqMEUG6spjZsnklIw= github.com/ayn2op/ningen/v3 v3.0.1-0.20260708075321-0ba1ffc70bd6/go.mod h1:vqLIvETLkjc2vIgQzVEBqpGNCrgNe7YJqS9TUAJsXRI= -github.com/ayn2op/tview v0.0.0-20260803165347-d8123f4ec53c h1:ApOfdF/YBJ+oy5zG5yJW/IbgCOoWcfJ/qukTnfFlUXc= -github.com/ayn2op/tview v0.0.0-20260803165347-d8123f4ec53c/go.mod h1:bJZdl/FfspPLEW3nuX6ubt7YbmDoSj7f2IoWH37w1NE= +github.com/ayn2op/tview v0.0.0-20260805051036-f8d695faec51 h1:hvyPcZcLG6dEAVXNpsMSWj93oJ1TIcDkJ+2eBcT1Yi0= +github.com/ayn2op/tview v0.0.0-20260805051036-f8d695faec51/go.mod h1:bJZdl/FfspPLEW3nuX6ubt7YbmDoSj7f2IoWH37w1NE= github.com/bdandy/go-errors v1.2.2 h1:WdFv/oukjTJCLa79UfkGmwX7ZxONAihKu4V0mLIs11Q= github.com/bdandy/go-errors v1.2.2/go.mod h1:NkYHl4Fey9oRRdbB1CoC6e84tuqQHiqrOcZpqFEkBxM= github.com/bdandy/go-socks4 v1.2.3 h1:Q6Y2heY1GRjCtHbmlKfnwrKVU/k81LS8mRGLRlmDlic= @@ -119,8 +119,8 @@ golang.org/x/exp/shiny v0.0.0-20260727155853-b88d891fe743 h1:rQLtGtwpYzQUyxCqrT3 golang.org/x/exp/shiny v0.0.0-20260727155853-b88d891fe743/go.mod h1:JRiBNgSZB8uVKtHGrvbGDQ8p9Ghn3+lkFSk1KqRIiPc= golang.org/x/image v0.44.0 h1:+tDekMZED9+LrtB3G5xzRggpVh9CARjZqROla3R3R+I= golang.org/x/image v0.44.0/go.mod h1:V8K3KE9KKKE+pLpQDOeN18w9oacNSvy1tDOirTu4xtY= -golang.org/x/mobile v0.0.0-20260730202154-c700fe717e6e h1:v5PGTbI7uer7FhpDeGryGytfbI8t7CDWkKukozvIsNU= -golang.org/x/mobile v0.0.0-20260730202154-c700fe717e6e/go.mod h1:YX+n47s+53POxN3dx9cIGxG3hGUm/lD64hvrRJFbcSA= +golang.org/x/mobile v0.0.0-20260803200217-62cee1672c8e h1:hi1b7Lmv+xvnrjeEv8BoDmU7H/FAOuH7hEHSvjMl2VM= +golang.org/x/mobile v0.0.0-20260803200217-62cee1672c8e/go.mod h1:YX+n47s+53POxN3dx9cIGxG3hGUm/lD64hvrRJFbcSA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= diff --git a/internal/ui/chat/attachments_picker.go b/internal/ui/chat/attachments_picker.go deleted file mode 100644 index 42c9f34..0000000 --- a/internal/ui/chat/attachments_picker.go +++ /dev/null @@ -1,80 +0,0 @@ -package chat - -import ( - "github.com/ayn2op/discordo/internal/config" - "github.com/ayn2op/discordo/internal/ui" - "github.com/ayn2op/tview" - "github.com/ayn2op/tview/help" - "github.com/ayn2op/tview/keybind" - "github.com/ayn2op/tview/picker" -) - -type attachmentItem struct { - label string - open tview.Cmd -} - -type attachmentsPicker struct { - *picker.Model - cfg *config.Config - chat *Model - items []attachmentItem -} - -var _ help.KeyMap = (*attachmentsPicker)(nil) - -func newAttachmentsPicker(cfg *config.Config, chat *Model) *attachmentsPicker { - ap := &attachmentsPicker{Model: picker.NewModel(), cfg: cfg, chat: chat} - ui.ConfigurePicker(ap.Model, cfg, "Attachments") - return ap -} - -func (ap *attachmentsPicker) SetItems(items []attachmentItem) { - ap.items = items - pickerItems := make(picker.Items, 0, len(items)) - for i, item := range items { - pickerItems = append(pickerItems, picker.Item{ - Text: item.label, - FilterText: item.label, - Reference: i, - }) - } - ap.Model.SetItems(pickerItems) -} - -func (ap *attachmentsPicker) close() tview.Cmd { - - ap.chat.RemoveLayer(attachmentsPickerLayerName) - return tview.SetFocus(ap.chat.messagesList) -} - -func (ap *attachmentsPicker) Update(msg tview.Msg) tview.Cmd { - switch msg := msg.(type) { - case picker.SelectedMsg: - index, ok := msg.Reference.(int) - if !ok { - return nil - } - if index < 0 || index >= len(ap.items) { - return nil - } - return tview.Sequence(ap.items[index].open, ap.close()) - case picker.CancelMsg: - return ap.close() - } - - return ap.Model.Update(msg) -} - -func (ap *attachmentsPicker) ShortHelp() []keybind.Keybind { - cfg := ap.cfg.Keybinds.Picker - return []keybind.Keybind{cfg.SelectUp.Keybind, cfg.SelectDown.Keybind, cfg.Select.Keybind, cfg.Cancel.Keybind} -} - -func (ap *attachmentsPicker) FullHelp() [][]keybind.Keybind { - cfg := ap.cfg.Keybinds.Picker - return [][]keybind.Keybind{ - {cfg.SelectUp.Keybind, cfg.SelectDown.Keybind, cfg.SelectTop.Keybind, cfg.SelectBottom.Keybind}, - {cfg.Select.Keybind, cfg.Cancel.Keybind}, - } -} diff --git a/internal/ui/chat/attachmentspicker/model.go b/internal/ui/chat/attachmentspicker/model.go new file mode 100644 index 0000000..14d4d2c --- /dev/null +++ b/internal/ui/chat/attachmentspicker/model.go @@ -0,0 +1,44 @@ +package attachmentspicker + +import ( + "github.com/ayn2op/discordo/internal/config" + "github.com/ayn2op/discordo/internal/ui" + "github.com/ayn2op/tview" + "github.com/ayn2op/tview/picker" +) + +type Item struct { + Label string + Open tview.Cmd +} + +type Model struct{ *picker.Model } + +func NewModel(cfg *config.Config) *Model { + m := &Model{Model: picker.NewModel()} + ui.ConfigurePicker(m.Model, cfg, "Attachments") + return m +} + +func (m *Model) SetItems(items []Item) { + pickerItems := make(picker.Items, len(items)) + for i, item := range items { + pickerItems[i] = picker.Item{Text: item.Label, Reference: item.Open} + } + m.Model.SetItems(pickerItems) +} + +func (m *Model) Update(msg tview.Msg) tview.Cmd { + switch msg := msg.(type) { + case picker.SelectedMsg: + open, ok := msg.Reference.(tview.Cmd) + if !ok { + return nil + } + return func() tview.Msg { return SelectedMsg{Open: open} } + case picker.CancelMsg: + return func() tview.Msg { return CancelMsg{} } + } + + return m.Model.Update(msg) +} diff --git a/internal/ui/chat/attachmentspicker/msg.go b/internal/ui/chat/attachmentspicker/msg.go new file mode 100644 index 0000000..d65b75b --- /dev/null +++ b/internal/ui/chat/attachmentspicker/msg.go @@ -0,0 +1,9 @@ +package attachmentspicker + +import "github.com/ayn2op/tview" + +type SelectedMsg struct { + Open tview.Cmd +} + +type CancelMsg struct{} diff --git a/internal/ui/chat/channelspicker/keymap.go b/internal/ui/chat/channelspicker/keymap.go deleted file mode 100644 index 4aabfcc..0000000 --- a/internal/ui/chat/channelspicker/keymap.go +++ /dev/null @@ -1,21 +0,0 @@ -package channelspicker - -import ( - "github.com/ayn2op/tview/help" - "github.com/ayn2op/tview/keybind" -) - -var _ help.KeyMap = (*Model)(nil) - -func (m *Model) ShortHelp() []keybind.Keybind { - cfg := m.cfg.Keybinds.Picker - return []keybind.Keybind{cfg.SelectUp.Keybind, cfg.SelectDown.Keybind, cfg.Select.Keybind, cfg.Cancel.Keybind} -} - -func (m *Model) FullHelp() [][]keybind.Keybind { - cfg := m.cfg.Keybinds.Picker - return [][]keybind.Keybind{ - {cfg.SelectUp.Keybind, cfg.SelectDown.Keybind, cfg.SelectTop.Keybind, cfg.SelectBottom.Keybind}, - {cfg.Select.Keybind, cfg.Cancel.Keybind}, - } -} diff --git a/internal/ui/chat/keybinds.go b/internal/ui/chat/keybinds.go index 0899adc..1e240be 100644 --- a/internal/ui/chat/keybinds.go +++ b/internal/ui/chat/keybinds.go @@ -29,6 +29,9 @@ func (m *Model) activeKeyMap() help.KeyMap { if m.GetVisible(channelsPickerLayerName) { return m.channelsPicker } + if m.GetVisible(attachmentsPickerLayerName) { + return m.messagesList.attachmentsPicker + } switch m.app.Focused() { case m.guildsTree: diff --git a/internal/ui/chat/messages_list.go b/internal/ui/chat/messages_list.go index 666975e..0678eb1 100644 --- a/internal/ui/chat/messages_list.go +++ b/internal/ui/chat/messages_list.go @@ -25,6 +25,7 @@ import ( "github.com/ayn2op/discordo/internal/consts" "github.com/ayn2op/discordo/internal/markdown" "github.com/ayn2op/discordo/internal/ui" + "github.com/ayn2op/discordo/internal/ui/chat/attachmentspicker" "github.com/ayn2op/ningen/v3/discordmd" "github.com/ayn2op/tview" "github.com/ayn2op/tview/help" @@ -54,7 +55,7 @@ type messagesList struct { // itemByID caches rendered message TextViews. itemByID map[discord.MessageID]*tview.TextView - attachmentsPicker *attachmentsPicker + attachmentsPicker *attachmentspicker.Model } var _ help.KeyMap = (*messagesList)(nil) @@ -80,7 +81,7 @@ func newMessagesList(cfg *config.Config, chat *Model) *messagesList { renderer: markdown.NewRenderer(cfg), itemByID: make(map[discord.MessageID]*tview.TextView), } - ml.attachmentsPicker = newAttachmentsPicker(cfg, chat) + ml.attachmentsPicker = attachmentspicker.NewModel(cfg) ui.ConfigureBox(ml.Box, &cfg.Theme) ml.SetTitle("Messages") @@ -1166,23 +1167,23 @@ func messageURLs(msg discord.Message) []string { } func (ml *messagesList) showAttachmentsList(urls []string, attachments []discord.Attachment) tview.Cmd { - var items []attachmentItem + var items []attachmentspicker.Item for _, a := range attachments { attachment := a open := ml.openURL(attachment.URL) if strings.HasPrefix(attachment.ContentType, "image/") { open = ml.openAttachment(attachment) } - items = append(items, attachmentItem{ - label: attachment.Filename, - open: open, + items = append(items, attachmentspicker.Item{ + Label: attachment.Filename, + Open: open, }) } for _, u := range urls { url := u - items = append(items, attachmentItem{ - label: url, - open: ml.openURL(url), + items = append(items, attachmentspicker.Item{ + Label: url, + Open: ml.openURL(url), }) } ml.attachmentsPicker.SetItems(items) diff --git a/internal/ui/chat/model.go b/internal/ui/chat/model.go index ea2b989..f4d0ced 100644 --- a/internal/ui/chat/model.go +++ b/internal/ui/chat/model.go @@ -18,6 +18,7 @@ import ( clientgateway "github.com/ayn2op/discordo/internal/gateway" "github.com/ayn2op/discordo/internal/http" "github.com/ayn2op/discordo/internal/ui" + "github.com/ayn2op/discordo/internal/ui/chat/attachmentspicker" "github.com/ayn2op/discordo/internal/ui/chat/channelspicker" "github.com/ayn2op/ningen/v3" "github.com/ayn2op/ningen/v3/states/read" @@ -170,6 +171,11 @@ func (m *Model) closePicker() { m.channelsPicker.Refresh() } +func (m *Model) closeAttachmentsPicker() tview.Cmd { + m.RemoveLayer(attachmentsPickerLayerName) + return tview.SetFocus(m.messagesList) +} + func (m *Model) navigateToChannel(channelID discord.ChannelID) tview.Cmd { channel, err := m.state.Cabinet.Channel(channelID) if err != nil { @@ -354,6 +360,10 @@ func (m *Model) Update(msg tview.Msg) tview.Cmd { case channelspicker.CancelMsg: m.closePicker() return nil + case attachmentspicker.SelectedMsg: + return tview.Sequence(msg.Open, m.closeAttachmentsPicker()) + case attachmentspicker.CancelMsg: + return m.closeAttachmentsPicker() case QuitMsg: return m.closeState() case tview.KeyMsg: