feat: implement selectable messages (#38)
* feat: implement selectable messages * style: shorten long lines * feat: handle key down event for message select functionality * feat: add handlers for top and bottom navigation * style: shorten long lines * docs: add 's' to singular verb * docs: add missing period * fix: if selectedChannel is not initialized, do not handle key event * fix: prepend message to .Messages slice * fix: if selectedChannel is nil, do not handle events * feat: better default keybindings * fix: use != comparison operator
This commit is contained in:
parent
102a45b66a
commit
5d19c768fe
3 changed files with 140 additions and 22 deletions
15
README.md
15
README.md
|
|
@ -43,9 +43,18 @@ By default, Discordo utilizes OS-specific keyring to store credentials such as c
|
|||
|
||||
### Default Keybindings
|
||||
|
||||
- `Alt` + `g`: Sets the focus on the guilds TreeView.
|
||||
- `Alt` + `m`: Sets the focus on the messages TextView.
|
||||
- `Alt` + `i`: Sets the focus on the message InputField.
|
||||
Global:
|
||||
|
||||
- `Alt` + `1`: Sets the focus on the guilds TreeView.
|
||||
- `Alt` + `2`: Sets the focus on the messages TextView.
|
||||
- `Alt` + `3`: Sets the focus on the message InputField.
|
||||
|
||||
TextView:
|
||||
|
||||
- `k` or `Up`: Selects the message just before the currently selected message.
|
||||
- `j` or `Down`: Selects the message just after the currently selected message.
|
||||
- `g` or `Home`: Selects the first message rendered in the TextView.
|
||||
- `G` or `End`: Selects the last message rendered in the TextView.
|
||||
|
||||
### Clipboard
|
||||
|
||||
|
|
|
|||
140
discordo.go
140
discordo.go
|
|
@ -37,7 +37,10 @@ func main() {
|
|||
EnableMouse(config.Mouse).
|
||||
SetInputCapture(onAppInputCapture)
|
||||
guildsTreeView = ui.NewGuildsTreeView(onGuildsTreeViewSelected)
|
||||
messagesTextView = ui.NewMessagesTextView(app)
|
||||
messagesTextView = ui.NewMessagesTextView(
|
||||
app,
|
||||
onMessagesTextViewInputCapture,
|
||||
)
|
||||
messageInputField = ui.NewMessageInputField(onMessageInputFieldInputCapture)
|
||||
mainFlex = ui.NewMainFlex(
|
||||
guildsTreeView,
|
||||
|
|
@ -73,29 +76,123 @@ func main() {
|
|||
|
||||
func onAppInputCapture(e *tcell.EventKey) *tcell.EventKey {
|
||||
switch e.Name() {
|
||||
case "Alt+Rune[g]":
|
||||
case "Alt+Rune[1]":
|
||||
app.SetFocus(guildsTreeView)
|
||||
case "Alt+Rune[m]":
|
||||
case "Alt+Rune[2]":
|
||||
app.SetFocus(messagesTextView)
|
||||
case "Alt+Rune[i]":
|
||||
case "Alt+Rune[3]":
|
||||
app.SetFocus(messageInputField)
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func onMessagesTextViewInputCapture(e *tcell.EventKey) *tcell.EventKey {
|
||||
if selectedChannel == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch {
|
||||
case e.Key() == tcell.KeyUp || e.Rune() == 'k': // Up
|
||||
ms := selectedChannel.Messages
|
||||
hs := messagesTextView.GetHighlights()
|
||||
// Initially, no message is highlighted/selected; highlight the last
|
||||
// message in the TextView.
|
||||
if len(hs) == 0 {
|
||||
messagesTextView.
|
||||
Highlight(selectedChannel.LastMessageID).
|
||||
ScrollToHighlight()
|
||||
} else {
|
||||
// Find the index of the highlighted message in the
|
||||
// *discordgo.Channel.Messages slice.
|
||||
var idx int
|
||||
for i, v := range ms {
|
||||
if hs[0] == v.ID {
|
||||
idx = i
|
||||
}
|
||||
}
|
||||
// If the length of the *discordgo.Channel.Messages slice is
|
||||
// equal to the index of the message just after highlighted
|
||||
// message in the slice (this is the first-rendered message in
|
||||
// the TextView), do not handle the event.
|
||||
if len(ms) == idx+1 {
|
||||
return nil
|
||||
}
|
||||
// Highlight the message just before the currently highlighted
|
||||
// message.
|
||||
messagesTextView.
|
||||
Highlight(ms[idx+1].ID).
|
||||
ScrollToHighlight()
|
||||
}
|
||||
|
||||
return nil
|
||||
case e.Key() == tcell.KeyDown || e.Rune() == 'j': // Down
|
||||
ms := selectedChannel.Messages
|
||||
hs := messagesTextView.GetHighlights()
|
||||
// Initially, no message is highlighted/selected; highlight the last
|
||||
// message in the TextView.
|
||||
if len(hs) == 0 {
|
||||
messagesTextView.
|
||||
Highlight(ms[0].ID).
|
||||
ScrollToHighlight()
|
||||
} else {
|
||||
// Find the index of the highlighted message in the
|
||||
// *discordgo.Channel.Messages slice.
|
||||
var idx int
|
||||
for i, v := range selectedChannel.Messages {
|
||||
if v.Type == discordgo.MessageTypeDefault || v.Type == discordgo.MessageTypeReply {
|
||||
if hs[0] == v.ID {
|
||||
idx = i
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the index of the highlighted message in the slice is equal
|
||||
// to zero (this is the last-rendered message in the TextView),
|
||||
// do not handle the event.
|
||||
if idx == 0 {
|
||||
return nil
|
||||
}
|
||||
// Highlight the message just after the currently highlighted
|
||||
// message.
|
||||
messagesTextView.
|
||||
Highlight(ms[idx-1].ID).
|
||||
ScrollToHighlight()
|
||||
}
|
||||
|
||||
return nil
|
||||
case e.Key() == tcell.KeyHome || e.Rune() == 'g': // Top
|
||||
ms := selectedChannel.Messages
|
||||
// Highlight the last message in the selectedChannel.Messages slice
|
||||
// (the first message rendered in the TextView).
|
||||
messagesTextView.
|
||||
Highlight(ms[len(ms)-1].ID).
|
||||
ScrollToHighlight()
|
||||
case e.Key() == tcell.KeyEnd || e.Rune() == 'G': // Bottom
|
||||
ms := selectedChannel.Messages
|
||||
// Highlight the first message in the selectedChannel.Messages slice
|
||||
// (the last message rendered in the TextView).
|
||||
messagesTextView.
|
||||
Highlight(ms[0].ID).
|
||||
ScrollToHighlight()
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func onMessageInputFieldInputCapture(e *tcell.EventKey) *tcell.EventKey {
|
||||
switch e.Key() {
|
||||
case tcell.KeyEnter:
|
||||
if selectedChannel != nil {
|
||||
t := strings.TrimSpace(messageInputField.GetText())
|
||||
if t == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
messageInputField.SetText("")
|
||||
go session.ChannelMessageSend(selectedChannel.ID, t)
|
||||
if selectedChannel == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
t := strings.TrimSpace(messageInputField.GetText())
|
||||
if t == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
messageInputField.SetText("")
|
||||
go session.ChannelMessageSend(selectedChannel.ID, t)
|
||||
case tcell.KeyCtrlV:
|
||||
text, _ := clipboard.ReadAll()
|
||||
text = messageInputField.GetText() + text
|
||||
|
|
@ -157,13 +254,19 @@ func onSessionReady(_ *discordgo.Session, r *discordgo.Ready) {
|
|||
}
|
||||
|
||||
func onSessionMessageCreate(_ *discordgo.Session, m *discordgo.MessageCreate) {
|
||||
if selectedChannel != nil && selectedChannel.ID == m.ChannelID {
|
||||
util.WriteMessage(
|
||||
messagesTextView,
|
||||
m.Message,
|
||||
session.State.Ready.User.ID,
|
||||
)
|
||||
if selectedChannel == nil || selectedChannel.ID != m.ChannelID {
|
||||
return
|
||||
}
|
||||
|
||||
selectedChannel.Messages = append(
|
||||
[]*discordgo.Message{m.Message},
|
||||
selectedChannel.Messages...)
|
||||
|
||||
util.WriteMessage(
|
||||
messagesTextView,
|
||||
m.Message,
|
||||
session.State.Ready.User.ID,
|
||||
)
|
||||
}
|
||||
|
||||
func onGuildsTreeViewSelected(n *tview.TreeNode) {
|
||||
|
|
@ -219,6 +322,7 @@ func onGuildsTreeViewSelected(n *tview.TreeNode) {
|
|||
|
||||
func writeMessages(cID string) {
|
||||
msgs, _ := session.ChannelMessages(cID, config.GetMessagesLimit, "", "", "")
|
||||
selectedChannel.Messages = msgs
|
||||
for i := len(msgs) - 1; i >= 0; i-- {
|
||||
util.WriteMessage(
|
||||
messagesTextView,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
// NewMessagesTextView creates and returns a new messages textview.
|
||||
func NewMessagesTextView(app *tview.Application) *tview.TextView {
|
||||
func NewMessagesTextView(
|
||||
app *tview.Application,
|
||||
onMessagesTextViewInputCapture func(*tcell.EventKey) *tcell.EventKey,
|
||||
) *tview.TextView {
|
||||
v := tview.NewTextView()
|
||||
v.
|
||||
SetRegions(true).
|
||||
|
|
@ -15,6 +19,7 @@ func NewMessagesTextView(app *tview.Application) *tview.TextView {
|
|||
SetChangedFunc(func() {
|
||||
app.Draw()
|
||||
}).
|
||||
SetInputCapture(onMessagesTextViewInputCapture).
|
||||
SetBorder(true).
|
||||
SetBorderPadding(0, 0, 1, 0).
|
||||
SetTitleAlign(tview.AlignLeft)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue