diff --git a/.github/preview.png b/.github/preview.png index 0ea1991..fa503b8 100644 Binary files a/.github/preview.png and b/.github/preview.png differ diff --git a/README.md b/README.md index c141053..b0eaba5 100644 --- a/README.md +++ b/README.md @@ -43,10 +43,9 @@ 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` + `c`: Sets the focus on the channels treeview. -- `Alt` + `m`: Sets the focus on the messages textview. -- `Alt` + `i`: Sets the focus on the message inputfield. +- `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. ### Clipboard diff --git a/discordo.go b/discordo.go index bdd67d6..76ee05d 100644 --- a/discordo.go +++ b/discordo.go @@ -16,7 +16,6 @@ var ( app *tview.Application loginForm *tview.Form guildsTreeView *tview.TreeView - channelsTreeView *tview.TreeView messagesTextView *tview.TextView messageInputField *tview.InputField mainFlex *tview.Flex @@ -37,10 +36,9 @@ func main() { EnableMouse(config.Mouse). SetInputCapture(onAppInputCapture) guildsTreeView = ui.NewGuildsTreeView(onGuildsTreeViewSelected) - channelsTreeView = ui.NewChannelsTreeView(onChannelsTreeViewSelected) messagesTextView = ui.NewMessagesTextView(app) messageInputField = ui.NewMessageInputField(onMessageInputFieldInputCapture) - mainFlex = ui.NewMainFlex(guildsTreeView, channelsTreeView, messagesTextView, messageInputField) + mainFlex = ui.NewMainFlex(guildsTreeView, messagesTextView, messageInputField) token := config.Token if t := util.GetPassword("token"); t != "" { @@ -72,8 +70,6 @@ func onAppInputCapture(e *tcell.EventKey) *tcell.EventKey { switch e.Name() { case "Alt+Rune[g]": app.SetFocus(guildsTreeView) - case "Alt+Rune[c]": - app.SetFocus(channelsTreeView) case "Alt+Rune[m]": app.SetFocus(messagesTextView) case "Alt+Rune[i]": @@ -145,14 +141,14 @@ func onSessionReady(_ *discordgo.Session, r *discordgo.Ready) { return false }) - rootN := guildsTreeView.GetRoot() + n := guildsTreeView.GetRoot() for _, g := range r.Guilds { gn := tview.NewTreeNode(g.Name). SetReference(g.ID) - rootN.AddChild(gn) + n.AddChild(gn) } - guildsTreeView.SetCurrentNode(rootN) + guildsTreeView.SetCurrentNode(n) } func onSessionMessageCreate(_ *discordgo.Session, m *discordgo.MessageCreate) { @@ -161,40 +157,41 @@ func onSessionMessageCreate(_ *discordgo.Session, m *discordgo.MessageCreate) { } } -func onGuildsTreeViewSelected(gn *tview.TreeNode) { - selectedChannel = nil - app.SetFocus(channelsTreeView) - messagesTextView. - Clear(). - SetTitle("") +func onGuildsTreeViewSelected(n *tview.TreeNode) { + switch n.GetLevel() { + case 1: + if len(n.GetChildren()) != 0 { + n.SetExpanded(!n.IsExpanded()) + return + } - gID := gn.GetReference().(string) - g, _ := session.State.Guild(gID) - cs := g.Channels - sort.Slice(cs, func(i, j int) bool { - return cs[i].Position < cs[j].Position - }) + selectedChannel = nil + n.ClearChildren() + messagesTextView. + Clear(). + SetTitle("") - rootN := channelsTreeView.GetRoot() - rootN.ClearChildren() - // Top-level channels - ui.CreateTopLevelChannelsTreeNodes(session.State, rootN, cs) - // Category channels - ui.CreateCategoryChannelsTreeNodes(session.State, rootN, cs) - // Second-level channels - ui.CreateSecondLevelChannelsTreeNodes(session.State, channelsTreeView, rootN, cs) + gID := n.GetReference().(string) + g, _ := session.State.Guild(gID) - channelsTreeView.SetCurrentNode(rootN) -} + cs := g.Channels + sort.Slice(cs, func(i, j int) bool { + return cs[i].Position < cs[j].Position + }) -func onChannelsTreeViewSelected(n *tview.TreeNode) { - cID := n.GetReference().(string) - c, _ := session.State.Channel(cID) - switch c.Type { - case discordgo.ChannelTypeGuildCategory: - n.SetExpanded(!n.IsExpanded()) - case discordgo.ChannelTypeGuildText, discordgo.ChannelTypeGuildNews: - if len(n.GetChildren()) == 0 { + // Top-level channels + ui.CreateTopLevelChannelsTreeNodes(session.State, n, cs) + // Category channels + ui.CreateCategoryChannelsTreeNodes(session.State, n, cs) + // Second-level channels + ui.CreateSecondLevelChannelsTreeNodes(session.State, guildsTreeView, cs) + default: + cID := n.GetReference().(string) + c, _ := session.State.Channel(cID) + + if c.Type == discordgo.ChannelTypeGuildCategory { + n.SetExpanded(!n.IsExpanded()) + } else if c.Type == discordgo.ChannelTypeGuildNews || c.Type == discordgo.ChannelTypeGuildText { selectedChannel = c app.SetFocus(messageInputField) @@ -207,8 +204,6 @@ func onChannelsTreeViewSelected(n *tview.TreeNode) { SetTitle(title) go writeMessages(c.ID) - } else { - n.SetExpanded(!n.IsExpanded()) } } } diff --git a/ui/flex.go b/ui/flex.go index 1bbe096..630dd13 100644 --- a/ui/flex.go +++ b/ui/flex.go @@ -5,17 +5,13 @@ import ( ) // NewMainFlex creates and returns a new main flex. -func NewMainFlex(gTreeV *tview.TreeView, cTreeV *tview.TreeView, textV *tview.TextView, i *tview.InputField) *tview.Flex { - lf := tview.NewFlex(). - SetDirection(tview.FlexRow). - AddItem(gTreeV, 0, 1, false). - AddItem(cTreeV, 0, 2, false) +func NewMainFlex(treeV *tview.TreeView, textV *tview.TextView, i *tview.InputField) *tview.Flex { rf := tview.NewFlex(). SetDirection(tview.FlexRow). AddItem(textV, 0, 1, false). AddItem(i, 3, 1, false) mf := tview.NewFlex(). - AddItem(lf, 0, 1, false). + AddItem(treeV, 0, 1, false). AddItem(rf, 0, 4, false) return mf diff --git a/ui/treeviews.go b/ui/treeviews.go index 226cbef..71bacd7 100644 --- a/ui/treeviews.go +++ b/ui/treeviews.go @@ -21,21 +21,6 @@ func NewGuildsTreeView(onGuildsTreeViewSelected func(*tview.TreeNode)) *tview.Tr return v } -// NewChannelsTreeView creates and returns a new channels treeview. -func NewChannelsTreeView(onChannelsTreeViewSelected func(*tview.TreeNode)) *tview.TreeView { - v := tview.NewTreeView() - v. - SetTopLevel(1). - SetRoot(tview.NewTreeNode("")). - SetSelectedFunc(onChannelsTreeViewSelected). - SetBorder(true). - SetBorderPadding(0, 0, 1, 0). - SetTitle("Channels"). - SetTitleAlign(tview.AlignLeft) - - return v -} - // NewTextChannelTreeNode creates and returns a new text channel treenode. func NewTextChannelTreeNode(c *discordgo.Channel) *tview.TreeNode { n := tview.NewTreeNode("[::d]#" + c.Name + "[::-]"). @@ -58,8 +43,8 @@ func GetTreeNodeByReference(r interface{}, treeV *tview.TreeView) (mn *tview.Tre return } -// CreateTopLevelChannelsTreeNodes creates treenodes for the top-level (orphan) channels. -func CreateTopLevelChannelsTreeNodes(s *discordgo.State, rootN *tview.TreeNode, cs []*discordgo.Channel) { +// CreateTopLevelChannelsTreeNodes creates TreeNodes for the top-level (orphan) channels. +func CreateTopLevelChannelsTreeNodes(s *discordgo.State, n *tview.TreeNode, cs []*discordgo.Channel) { for _, c := range cs { if (c.Type == discordgo.ChannelTypeGuildText || c.Type == discordgo.ChannelTypeGuildNews) && (c.ParentID == "") { if !util.HasPermission(s, s.User.ID, c.ID, discordgo.PermissionViewChannel) { @@ -67,14 +52,14 @@ func CreateTopLevelChannelsTreeNodes(s *discordgo.State, rootN *tview.TreeNode, } cn := NewTextChannelTreeNode(c) - rootN.AddChild(cn) + n.AddChild(cn) continue } } } -// CreateCategoryChannelsTreeNodes creates treenodes for the category channels. -func CreateCategoryChannelsTreeNodes(s *discordgo.State, rootN *tview.TreeNode, cs []*discordgo.Channel) { +// CreateCategoryChannelsTreeNodes creates TreeNodes for the category (parent) channels. +func CreateCategoryChannelsTreeNodes(s *discordgo.State, n *tview.TreeNode, cs []*discordgo.Channel) { CategoryLoop: for _, c := range cs { if c.Type == discordgo.ChannelTypeGuildCategory { @@ -86,27 +71,27 @@ CategoryLoop: if child.ParentID == c.ID { cn := tview.NewTreeNode(c.Name). SetReference(c.ID) - rootN.AddChild(cn) + n.AddChild(cn) continue CategoryLoop } } cn := tview.NewTreeNode(c.Name). SetReference(c.ID) - rootN.AddChild(cn) + n.AddChild(cn) } } } -// CreateSecondLevelChannelsTreeNodes creates treenodes for the second-level (category children) channels. -func CreateSecondLevelChannelsTreeNodes(s *discordgo.State, channelsTreeView *tview.TreeView, rootN *tview.TreeNode, cs []*discordgo.Channel) { +// CreateSecondLevelChannelsTreeNodes creates TreeNodes for the second-level (category children) channels. +func CreateSecondLevelChannelsTreeNodes(s *discordgo.State, treeV *tview.TreeView, cs []*discordgo.Channel) { for _, c := range cs { if (c.Type == discordgo.ChannelTypeGuildText || c.Type == discordgo.ChannelTypeGuildNews) && (c.ParentID != "") { if !util.HasPermission(s, s.User.ID, c.ID, discordgo.PermissionViewChannel) { continue } - if pn := GetTreeNodeByReference(c.ParentID, channelsTreeView); pn != nil { + if pn := GetTreeNodeByReference(c.ParentID, treeV); pn != nil { cn := NewTextChannelTreeNode(c) pn.AddChild(cn) }