ui: separate guilds from TreeView (#33)
* feat(ui): add NewGuildsList() * fix(ui): remove redundant \n from guilds list * feat(ui): add guilds List to main Flex * refactor(ui): rename TreeView to channels TreeView * feat: separate guilds from TreeView * ui/flex: increase proportionate size of TreeView * main: sort guild channels according to channel positions * docs: update README preview * main: add keybinding for guilds List
This commit is contained in:
parent
c22a64cc5d
commit
4cf3fff3fe
6 changed files with 136 additions and 108 deletions
BIN
.github/preview.png
vendored
BIN
.github/preview.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 178 KiB |
|
|
@ -44,7 +44,7 @@ By default, Discordo utilizes OS-specific keyring to store credentials such as c
|
|||
|
||||
### Default Keybindings
|
||||
|
||||
- `Alt` + `1`: Sets the focus on the guilds dropdown.
|
||||
- `Alt` + `1`: Sets the focus on the guilds list.
|
||||
- `Alt` + `2`: Sets the focus on the channels treeview.
|
||||
- `Alt` + `3`: Sets the focus on the messages textview.
|
||||
- `Alt` + `4`: Sets the focus on the message inputfield.
|
||||
|
|
|
|||
207
discordo.go
207
discordo.go
|
|
@ -19,7 +19,8 @@ import (
|
|||
var (
|
||||
app *tview.Application
|
||||
loginForm *tview.Form
|
||||
guildsTreeView *tview.TreeView
|
||||
guildsList *tview.List
|
||||
channelsTreeView *tview.TreeView
|
||||
messagesTextView *tview.TextView
|
||||
messageInputField *tview.InputField
|
||||
mainFlex *tview.Flex
|
||||
|
|
@ -27,8 +28,9 @@ var (
|
|||
conf *util.Config
|
||||
discordSession *session.Session
|
||||
clientID discord.UserID
|
||||
currentGuild gateway.GuildCreateEvent
|
||||
currentChannel discord.Channel
|
||||
guilds []gateway.GuildCreateEvent
|
||||
guild gateway.GuildCreateEvent
|
||||
channel discord.Channel
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
@ -59,10 +61,11 @@ func main() {
|
|||
}
|
||||
|
||||
app = ui.NewApp(onAppInputCapture)
|
||||
guildsTreeView = ui.NewGuildsTreeView(onGuildsTreeViewSelected)
|
||||
guildsList = ui.NewGuildsList(onGuildsListSelected, conf.Theme)
|
||||
channelsTreeView = ui.NewChannelsTreeView(onChannelsTreeViewSelected)
|
||||
messagesTextView = ui.NewMessagesTextView(app)
|
||||
messageInputField = ui.NewMessageInputField(onMessageInputFieldInputCapture, conf.Theme)
|
||||
mainFlex = ui.NewMainFlex(guildsTreeView, messagesTextView, messageInputField)
|
||||
mainFlex = ui.NewMainFlex(guildsList, channelsTreeView, messagesTextView, messageInputField)
|
||||
|
||||
token := os.Getenv("DISCORDO_TOKEN")
|
||||
if t := util.GetPassword("token"); t != "" {
|
||||
|
|
@ -72,7 +75,7 @@ func main() {
|
|||
if token != "" {
|
||||
app.
|
||||
SetRoot(mainFlex, true).
|
||||
SetFocus(guildsTreeView)
|
||||
SetFocus(guildsList)
|
||||
|
||||
discordSession = newSession(token)
|
||||
} else {
|
||||
|
|
@ -88,13 +91,13 @@ func main() {
|
|||
func onAppInputCapture(e *tcell.EventKey) *tcell.EventKey {
|
||||
switch e.Name() {
|
||||
case "Alt+Rune[1]":
|
||||
app.SetFocus(guildsTreeView)
|
||||
app.SetFocus(guildsList)
|
||||
case "Alt+Rune[2]":
|
||||
app.SetFocus(messagesTextView)
|
||||
app.SetFocus(channelsTreeView)
|
||||
case "Alt+Rune[3]":
|
||||
if messageInputField != nil {
|
||||
app.SetFocus(messageInputField)
|
||||
}
|
||||
app.SetFocus(messagesTextView)
|
||||
case "Alt+Rune[4]":
|
||||
app.SetFocus(messageInputField)
|
||||
}
|
||||
|
||||
return e
|
||||
|
|
@ -108,7 +111,7 @@ func onMessageInputFieldInputCapture(e *tcell.EventKey) *tcell.EventKey {
|
|||
return nil
|
||||
}
|
||||
|
||||
discordSession.SendMessage(currentChannel.ID, t)
|
||||
discordSession.SendMessage(channel.ID, t)
|
||||
messageInputField.SetText("")
|
||||
case tcell.KeyCtrlV:
|
||||
text, _ := clipboard.ReadAll()
|
||||
|
|
@ -143,110 +146,98 @@ func newSession(token string) (s *session.Session) {
|
|||
return
|
||||
}
|
||||
|
||||
func onSessionReady(r *gateway.ReadyEvent) {
|
||||
clientID = r.User.ID
|
||||
guilds = r.Guilds
|
||||
for _, g := range r.Guilds {
|
||||
guildsList.AddItem(g.Name, "", 0, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func onSessionMessageCreate(m *gateway.MessageCreateEvent) {
|
||||
if currentChannel.ID == m.ChannelID {
|
||||
if channel.ID == m.ChannelID {
|
||||
util.WriteMessage(messagesTextView, clientID, m.Message)
|
||||
}
|
||||
}
|
||||
|
||||
func onSessionReady(r *gateway.ReadyEvent) {
|
||||
clientID = r.User.ID
|
||||
func onGuildsListSelected(i int, _ string, _ string, _ rune) {
|
||||
app.SetFocus(channelsTreeView)
|
||||
messagesTextView.SetTitle("")
|
||||
messagesTextView.Clear()
|
||||
|
||||
for _, g := range r.Guilds {
|
||||
gn := tview.NewTreeNode(g.Name).
|
||||
SetReference(g).
|
||||
Collapse()
|
||||
guildsTreeView.GetRoot().AddChild(gn)
|
||||
n := channelsTreeView.GetRoot()
|
||||
n.ClearChildren()
|
||||
|
||||
sort.Slice(g.Channels, func(i, j int) bool {
|
||||
return g.Channels[i].Position < g.Channels[j].Position
|
||||
})
|
||||
guild = guilds[i]
|
||||
sort.SliceStable(guild.Channels, func(i, j int) bool {
|
||||
return guild.Channels[i].Position < guild.Channels[j].Position
|
||||
})
|
||||
|
||||
for _, c := range g.Channels {
|
||||
switch c.Type {
|
||||
case discord.GuildCategory:
|
||||
cn := tview.NewTreeNode(c.Name).
|
||||
for _, c := range guild.Channels {
|
||||
switch c.Type {
|
||||
case discord.GuildCategory:
|
||||
cn := tview.NewTreeNode(c.Name).
|
||||
SetReference(c)
|
||||
n.AddChild(cn)
|
||||
case discord.GuildText, discord.GuildNews:
|
||||
if c.ParentID == 0 || c.ParentID == discord.NullChannelID {
|
||||
cn := tview.NewTreeNode("[::d]#" + c.Name + "[::-]").
|
||||
SetReference(c)
|
||||
gn.AddChild(cn)
|
||||
case discord.GuildText, discord.GuildNews:
|
||||
if c.ParentID == 0 || c.ParentID == discord.NullChannelID {
|
||||
cn := tview.NewTreeNode("[::d]#" + c.Name + "[::-]").
|
||||
SetReference(c)
|
||||
gn.AddChild(cn)
|
||||
}
|
||||
case discord.GuildStageVoice, discord.GuildVoice:
|
||||
if c.ParentID == 0 || c.ParentID == discord.NullChannelID {
|
||||
cn := tview.NewTreeNode("[::d]🔊" + c.Name + "[::-]").
|
||||
SetReference(c)
|
||||
gn.AddChild(cn)
|
||||
}
|
||||
n.AddChild(cn)
|
||||
}
|
||||
case discord.GuildStageVoice, discord.GuildVoice:
|
||||
if c.ParentID == 0 || c.ParentID == discord.NullChannelID {
|
||||
cn := tview.NewTreeNode("[::d]🔊" + c.Name + "[::-]").
|
||||
SetReference(c)
|
||||
n.AddChild(cn)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func onGuildsTreeViewSelected(n *tview.TreeNode) {
|
||||
switch r := n.GetReference().(type) {
|
||||
case gateway.GuildCreateEvent:
|
||||
currentGuild = r
|
||||
n.SetExpanded(!n.IsExpanded())
|
||||
case discord.Channel:
|
||||
switch r.Type {
|
||||
case discord.GuildCategory:
|
||||
if len(n.GetChildren()) == 0 {
|
||||
for _, c := range currentGuild.Channels {
|
||||
switch c.Type {
|
||||
case discord.GuildText, discord.GuildNews:
|
||||
if c.ParentID == r.ID {
|
||||
cn := tview.NewTreeNode("[::d]#" + c.Name + "[::-]").
|
||||
SetReference(c)
|
||||
n.AddChild(cn)
|
||||
}
|
||||
case discord.GuildStageVoice, discord.GuildVoice:
|
||||
if c.ParentID == r.ID {
|
||||
cn := tview.NewTreeNode("[::d]🔊" + c.Name + "[::-]").
|
||||
SetReference(c)
|
||||
n.AddChild(cn)
|
||||
}
|
||||
func onChannelsTreeViewSelected(n *tview.TreeNode) {
|
||||
r := n.GetReference().(discord.Channel)
|
||||
switch r.Type {
|
||||
case discord.GuildCategory:
|
||||
if len(n.GetChildren()) == 0 {
|
||||
for _, c := range guild.Channels {
|
||||
switch c.Type {
|
||||
case discord.GuildText, discord.GuildNews:
|
||||
if c.ParentID == r.ID {
|
||||
cn := tview.NewTreeNode("[::d]#" + c.Name + "[::-]").
|
||||
SetReference(c)
|
||||
n.AddChild(cn)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
n.SetExpanded(!n.IsExpanded())
|
||||
}
|
||||
case discord.GuildText, discord.GuildNews:
|
||||
if len(n.GetChildren()) == 0 {
|
||||
currentChannel = r
|
||||
app.SetFocus(messageInputField)
|
||||
messagesTextView.Clear()
|
||||
|
||||
title := "#" + r.Name
|
||||
if r.Topic != "" {
|
||||
title += " - " + r.Topic
|
||||
}
|
||||
messagesTextView.SetTitle(title)
|
||||
|
||||
for _, t := range currentGuild.Threads {
|
||||
if t.ParentID == currentChannel.ID {
|
||||
cn := tview.NewTreeNode("[::d]🗨 " + t.Name + "[::-]").
|
||||
SetReference(t)
|
||||
case discord.GuildStageVoice, discord.GuildVoice:
|
||||
if c.ParentID == r.ID {
|
||||
cn := tview.NewTreeNode("[::d]🔊" + c.Name + "[::-]").
|
||||
SetReference(c)
|
||||
n.AddChild(cn)
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
msgs, _ := discordSession.Messages(r.ID, conf.GetMessagesLimit)
|
||||
for i := len(msgs) - 1; i >= 0; i-- {
|
||||
util.WriteMessage(messagesTextView, clientID, msgs[i])
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
n.SetExpanded(!n.IsExpanded())
|
||||
}
|
||||
case discord.GuildNewsThread, discord.GuildPrivateThread, discord.GuildPublicThread:
|
||||
currentChannel = r
|
||||
} else {
|
||||
n.SetExpanded(!n.IsExpanded())
|
||||
}
|
||||
case discord.GuildText, discord.GuildNews:
|
||||
if len(n.GetChildren()) == 0 {
|
||||
channel = r
|
||||
app.SetFocus(messageInputField)
|
||||
messagesTextView.Clear()
|
||||
messagesTextView.SetTitle(r.Name)
|
||||
|
||||
title := "#" + r.Name
|
||||
if r.Topic != "" {
|
||||
title += " - " + r.Topic
|
||||
}
|
||||
messagesTextView.SetTitle(title)
|
||||
|
||||
for _, t := range guild.Threads {
|
||||
if t.ParentID == channel.ID {
|
||||
cn := tview.NewTreeNode("[::d]🗨 " + t.Name + "[::-]").
|
||||
SetReference(t)
|
||||
n.AddChild(cn)
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
msgs, _ := discordSession.Messages(r.ID, conf.GetMessagesLimit)
|
||||
|
|
@ -254,10 +245,24 @@ func onGuildsTreeViewSelected(n *tview.TreeNode) {
|
|||
util.WriteMessage(messagesTextView, clientID, msgs[i])
|
||||
}
|
||||
}()
|
||||
case discord.GuildStageVoice, discord.GuildVoice:
|
||||
messagesTextView.Clear()
|
||||
messagesTextView.SetTitle(r.Name)
|
||||
} else {
|
||||
n.SetExpanded(!n.IsExpanded())
|
||||
}
|
||||
case discord.GuildNewsThread, discord.GuildPrivateThread, discord.GuildPublicThread:
|
||||
channel = r
|
||||
app.SetFocus(messageInputField)
|
||||
messagesTextView.Clear()
|
||||
messagesTextView.SetTitle(r.Name)
|
||||
|
||||
go func() {
|
||||
msgs, _ := discordSession.Messages(r.ID, conf.GetMessagesLimit)
|
||||
for i := len(msgs) - 1; i >= 0; i-- {
|
||||
util.WriteMessage(messagesTextView, clientID, msgs[i])
|
||||
}
|
||||
}()
|
||||
case discord.GuildStageVoice, discord.GuildVoice:
|
||||
messagesTextView.Clear()
|
||||
messagesTextView.SetTitle(r.Name)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -278,8 +283,8 @@ func onLoginFormLoginButtonSelected() {
|
|||
|
||||
if l.Token != "" && !l.MFA {
|
||||
app.
|
||||
SetRoot(mainFlex, true).
|
||||
SetFocus(guildsTreeView)
|
||||
SetRoot(guildsList, true).
|
||||
SetFocus(channelsTreeView)
|
||||
|
||||
discordSession = newSession(l.Token)
|
||||
go util.SetPassword("token", l.Token)
|
||||
|
|
@ -297,7 +302,7 @@ func onLoginFormLoginButtonSelected() {
|
|||
|
||||
app.
|
||||
SetRoot(mainFlex, true).
|
||||
SetFocus(guildsTreeView)
|
||||
SetFocus(guildsList)
|
||||
|
||||
discordSession = newSession(l.Token)
|
||||
go util.SetPassword("token", l.Token)
|
||||
|
|
|
|||
10
ui/flex.go
10
ui/flex.go
|
|
@ -4,14 +4,18 @@ import (
|
|||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
func NewMainFlex(treeV *tview.TreeView, textV *tview.TextView, i *tview.InputField) (mf *tview.Flex) {
|
||||
func NewMainFlex(l *tview.List, treeV *tview.TreeView, textV *tview.TextView, i *tview.InputField) (mf *tview.Flex) {
|
||||
lf := tview.NewFlex().
|
||||
SetDirection(tview.FlexRow).
|
||||
AddItem(l, 0, 1, false).
|
||||
AddItem(treeV, 0, 3, false)
|
||||
rf := tview.NewFlex().
|
||||
SetDirection(tview.FlexRow).
|
||||
AddItem(textV, 0, 1, false).
|
||||
AddItem(i, 3, 1, false)
|
||||
mf = tview.NewFlex().
|
||||
AddItem(treeV, 25, 1, false).
|
||||
AddItem(rf, 0, 1, false)
|
||||
AddItem(lf, 0, 1, false).
|
||||
AddItem(rf, 0, 5, false)
|
||||
|
||||
return mf
|
||||
}
|
||||
|
|
|
|||
19
ui/lists.go
Normal file
19
ui/lists.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"github.com/rigormorrtiss/discordo/util"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
func NewGuildsList(onGuildsListSelected func(int, string, string, rune), t *util.Theme) (l *tview.List) {
|
||||
l = tview.NewList()
|
||||
l.
|
||||
SetSelectedFunc(onGuildsListSelected).
|
||||
ShowSecondaryText(false).
|
||||
SetBorder(true).
|
||||
SetBorderPadding(0, 0, 1, 0).
|
||||
SetTitle("Guilds").
|
||||
SetTitleAlign(tview.AlignLeft)
|
||||
|
||||
return
|
||||
}
|
||||
|
|
@ -4,15 +4,15 @@ import (
|
|||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
func NewGuildsTreeView(onGuildsTreeViewSelected func(*tview.TreeNode)) (treeV *tview.TreeView) {
|
||||
func NewChannelsTreeView(onChannelsTreeViewSelected func(*tview.TreeNode)) (treeV *tview.TreeView) {
|
||||
treeV = tview.NewTreeView()
|
||||
treeN := tview.NewTreeNode("")
|
||||
treeV.
|
||||
SetTopLevel(1).
|
||||
SetRoot(treeN).
|
||||
SetCurrentNode(treeN).
|
||||
SetSelectedFunc(onGuildsTreeViewSelected).
|
||||
SetTitle("Guilds").
|
||||
SetSelectedFunc(onChannelsTreeViewSelected).
|
||||
SetTitle("Channels").
|
||||
SetBorder(true).
|
||||
SetBorderPadding(0, 0, 1, 0).
|
||||
SetTitleAlign(tview.AlignLeft)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue