Просмотр исходного кода

feat: merge channels & guilds TreeView for ease-of-use (#37)

* feat: merge channels & guilds TreeView for ease-of-use

* docs: update README preview
rigormorrtiss 4 лет назад
Родитель
Сommit
93a422bc9e
5 измененных файлов с 51 добавлено и 76 удалено
  1. BIN
      .github/preview.png
  2. 3 4
      README.md
  3. 36 41
      discordo.go
  4. 2 6
      ui/flex.go
  5. 10 25
      ui/treeviews.go

BIN
.github/preview.png


+ 3 - 4
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
 

+ 36 - 41
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("")
-
-	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
-	})
+func onGuildsTreeViewSelected(n *tview.TreeNode) {
+	switch n.GetLevel() {
+	case 1:
+		if len(n.GetChildren()) != 0 {
+			n.SetExpanded(!n.IsExpanded())
+			return
+		}
 
-	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)
+		selectedChannel = nil
+		n.ClearChildren()
+		messagesTextView.
+			Clear().
+			SetTitle("")
 
-	channelsTreeView.SetCurrentNode(rootN)
-}
+		gID := n.GetReference().(string)
+		g, _ := session.State.Guild(gID)
 
-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 {
+		cs := g.Channels
+		sort.Slice(cs, func(i, j int) bool {
+			return cs[i].Position < cs[j].Position
+		})
+
+		// 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())
 		}
 	}
 }

+ 2 - 6
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

+ 10 - 25
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)
 			}