Explorar o código

refactor: move util/{discord,ui} to main pkg

ayntgl %!s(int64=4) %!d(string=hai) anos
pai
achega
834d7342af
Modificáronse 4 ficheiros con 180 adicións e 195 borrados
  1. 48 3
      discord.go
  2. 132 26
      ui.go
  3. 0 53
      util/discord.go
  4. 0 113
      util/ui.go

+ 48 - 3
discord.go

@@ -8,7 +8,6 @@ import (
 	"strings"
 
 	"github.com/ayntgl/discordgo"
-	"github.com/ayntgl/discordo/util"
 	"github.com/gen2brain/beeep"
 	"github.com/rivo/tview"
 )
@@ -94,11 +93,11 @@ func onSessionMessageCreate(_ *discordgo.Session, m *discordgo.MessageCreate) {
 			}
 		}
 
-		cn := util.GetTreeNodeByReference(channelsTree, c.ID)
+		cn := getTreeNodeByReference(channelsTree, c.ID)
 		if cn == nil {
 			return
 		}
-		cn.SetText("[::b]" + util.ChannelToString(c) + "[::-]")
+		cn.SetText("[::b]" + channelToString(c) + "[::-]")
 		app.Draw()
 	} else {
 		selectedChannel.Messages = append(selectedChannel.Messages, m.Message)
@@ -110,6 +109,26 @@ func onSessionMessageCreate(_ *discordgo.Session, m *discordgo.MessageCreate) {
 	}
 }
 
+// channelToString constructs a string representation of the given channel. The string representation may vary for different channel types.
+func channelToString(c *discordgo.Channel) string {
+	var repr string
+	if c.Name != "" {
+		repr = "#" + c.Name
+	} else if len(c.Recipients) == 1 {
+		rp := c.Recipients[0]
+		repr = rp.Username + "#" + rp.Discriminator
+	} else {
+		rps := make([]string, len(c.Recipients))
+		for i, r := range c.Recipients {
+			rps[i] = r.Username + "#" + r.Discriminator
+		}
+
+		repr = strings.Join(rps, ", ")
+	}
+
+	return repr
+}
+
 type loginResponse struct {
 	MFA    bool   `json:"mfa"`
 	SMS    bool   `json:"sms"`
@@ -366,3 +385,29 @@ func parseMarkdown(md string) string {
 
 	return res
 }
+
+// channelIsUnread returns `true` if the given channel is marked as read by the client user, otherwise `false`.
+func channelIsUnread(s *discordgo.State, c *discordgo.Channel) bool {
+	if c.LastMessageID == "" {
+		return false
+	}
+
+	for _, rs := range s.ReadState {
+		if c.ID == rs.ID {
+			return c.LastMessageID != rs.LastMessageID
+		}
+	}
+
+	return false
+}
+
+// findMessageByID returns the index and the `*Message` struct of the current message if the given message ID *mID* is equal to the current message ID. If the given message ID *mID* is not found in the given slice *ms*, `-1` and `nil` are returned instead.
+func findMessageByID(ms []*discordgo.Message, mID string) (int, *discordgo.Message) {
+	for i, m := range ms {
+		if m.ID == mID {
+			return i, m
+		}
+	}
+
+	return -1, nil
+}

+ 132 - 26
ui.go

@@ -6,7 +6,6 @@ import (
 
 	"github.com/atotto/clipboard"
 	"github.com/ayntgl/discordgo"
-	"github.com/ayntgl/discordo/util"
 	"github.com/gdamore/tcell/v2"
 	"github.com/rivo/tview"
 )
@@ -17,13 +16,13 @@ var (
 )
 
 func onAppInputCapture(e *tcell.EventKey) *tcell.EventKey {
-	if util.HasKeybinding(conf.Keybindings.FocusChannelsTree, e.Name()) {
+	if hasKeybinding(conf.Keybindings.FocusChannelsTree, e.Name()) {
 		app.SetFocus(channelsTree)
 		return nil
-	} else if util.HasKeybinding(conf.Keybindings.FocusMessagesView, e.Name()) {
+	} else if hasKeybinding(conf.Keybindings.FocusMessagesView, e.Name()) {
 		app.SetFocus(messagesView)
 		return nil
-	} else if util.HasKeybinding(conf.Keybindings.FocusMessageInputField, e.Name()) {
+	} else if hasKeybinding(conf.Keybindings.FocusMessageInputField, e.Name()) {
 		app.SetFocus(messageInputField)
 		return nil
 	}
@@ -67,11 +66,11 @@ func onChannelsTreeSelected(n *tview.TreeNode) {
 
 				for _, c := range cs {
 					tag := "[::d]"
-					if util.ChannelIsUnread(session.State, c) {
+					if channelIsUnread(session.State, c) {
 						tag = "[::b]"
 					}
 
-					cn := tview.NewTreeNode(tag + util.ChannelToString(c) + "[::-]").
+					cn := tview.NewTreeNode(tag + channelToString(c) + "[::-]").
 						SetReference(c.ID).
 						Collapse()
 					n.AddChild(cn)
@@ -87,11 +86,11 @@ func onChannelsTreeSelected(n *tview.TreeNode) {
 				})
 
 				// Top-level channels
-				util.CreateTopLevelChannelsNodes(channelsTree, session.State, n, g.Channels)
+				createTopLevelChannelsNodes(channelsTree, session.State, n, g.Channels)
 				// Category channels
-				util.CreateCategoryChannelsNodes(channelsTree, session.State, n, g.Channels)
+				createCategoryChannelsNodes(channelsTree, session.State, n, g.Channels)
 				// Second-level channels
-				util.CreateSecondLevelChannelsNodes(channelsTree, session.State, g.Channels)
+				createSecondLevelChannelsNodes(channelsTree, session.State, g.Channels)
 			}
 		}
 
@@ -107,18 +106,18 @@ func onChannelsTreeSelected(n *tview.TreeNode) {
 
 		switch c.Type {
 		case discordgo.ChannelTypeGuildText, discordgo.ChannelTypeGuildNews:
-			title := util.ChannelToString(c)
+			title := channelToString(c)
 			if c.Topic != "" {
 				title += " - " + c.Topic
 			}
 
 			messagesView.SetTitle(title)
 		case discordgo.ChannelTypeDM, discordgo.ChannelTypeGroupDM:
-			messagesView.SetTitle(util.ChannelToString(c))
+			messagesView.SetTitle(channelToString(c))
 		}
 
 		if strings.HasPrefix(n.GetText(), "[::b]") {
-			n.SetText("[::d]" + util.ChannelToString(c) + "[::-]")
+			n.SetText("[::d]" + channelToString(c) + "[::-]")
 		}
 
 		go func() {
@@ -134,13 +133,66 @@ func onChannelsTreeSelected(n *tview.TreeNode) {
 			// Scroll to the end of the text after the messages have been written to the TextView.
 			messagesView.ScrollToEnd()
 
-			if len(ms) != 0 && util.ChannelIsUnread(session.State, c) {
+			if len(ms) != 0 && channelIsUnread(session.State, c) {
 				session.ChannelMessageAck(c.ID, c.LastMessageID, "")
 			}
 		}()
 	}
 }
 
+// createTopLevelChannelsNodes builds and creates `*tview.TreeNode`s for top-level (channels that have an empty parent ID and of type GUILD_TEXT, GUILD_NEWS) channels. If the client user does not have the VIEW_CHANNEL permission for a channel, the channel is excluded from the parent.
+func createTopLevelChannelsNodes(treeView *tview.TreeView, 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 !hasPermission(s, c.ID, discordgo.PermissionViewChannel) {
+				continue
+			}
+
+			n.AddChild(createChannelNode(s, c))
+			continue
+		}
+	}
+}
+
+// createCategoryChannelsNodes builds and creates `*tview.TreeNode`s for category (type: GUILD_CATEGORY) channels. If the client user does not have the VIEW_CHANNEL permission for a channel, the channel is excluded from the parent.
+func createCategoryChannelsNodes(treeView *tview.TreeView, s *discordgo.State, n *tview.TreeNode, cs []*discordgo.Channel) {
+CategoryLoop:
+	for _, c := range cs {
+		if c.Type == discordgo.ChannelTypeGuildCategory {
+			if !hasPermission(s, c.ID, discordgo.PermissionViewChannel) {
+				continue
+			}
+
+			for _, child := range cs {
+				if child.ParentID == c.ID {
+					n.AddChild(createChannelNode(s, c))
+					continue CategoryLoop
+				}
+			}
+
+			n.AddChild(createChannelNode(s, c))
+		}
+	}
+}
+
+// createSecondLevelChannelsNodes builds and creates `*tview.TreeNode`s for second-level (channels that have a non-empty parent ID and of type GUILD_TEXT, GUILD_NEWS) channels. If the client user does not have the VIEW_CHANNEL permission for a channel, the channel is excluded from the parent.
+func createSecondLevelChannelsNodes(treeView *tview.TreeView, s *discordgo.State, cs []*discordgo.Channel) {
+	for _, c := range cs {
+		if (c.Type == discordgo.ChannelTypeGuildText || c.Type == discordgo.ChannelTypeGuildNews) &&
+			(c.ParentID != "") {
+			if !hasPermission(s, c.ID, discordgo.PermissionViewChannel) {
+				continue
+			}
+
+			pn := getTreeNodeByReference(treeView, c.ParentID)
+			if pn != nil {
+				pn.AddChild(createChannelNode(s, c))
+			}
+		}
+	}
+}
+
 func newMessagesView() *tview.TextView {
 	textView := tview.NewTextView()
 	textView.
@@ -164,7 +216,7 @@ func onMessagesViewInputCapture(e *tcell.EventKey) *tcell.EventKey {
 		return nil
 	}
 
-	if util.HasKeybinding(conf.Keybindings.SelectPreviousMessage, e.Name()) {
+	if hasKeybinding(conf.Keybindings.SelectPreviousMessage, e.Name()) {
 		if len(messagesView.GetHighlights()) == 0 {
 			selectedMessage = len(ms) - 1
 		} else {
@@ -178,7 +230,7 @@ func onMessagesViewInputCapture(e *tcell.EventKey) *tcell.EventKey {
 			Highlight(ms[selectedMessage].ID).
 			ScrollToHighlight()
 		return nil
-	} else if util.HasKeybinding(conf.Keybindings.SelectNextMessage, e.Name()) {
+	} else if hasKeybinding(conf.Keybindings.SelectNextMessage, e.Name()) {
 		if len(messagesView.GetHighlights()) == 0 {
 			selectedMessage = len(ms) - 1
 		} else {
@@ -192,60 +244,60 @@ func onMessagesViewInputCapture(e *tcell.EventKey) *tcell.EventKey {
 			Highlight(ms[selectedMessage].ID).
 			ScrollToHighlight()
 		return nil
-	} else if util.HasKeybinding(conf.Keybindings.SelectFirstMessage, e.Name()) {
+	} else if hasKeybinding(conf.Keybindings.SelectFirstMessage, e.Name()) {
 		selectedMessage = 0
 		messagesView.
 			Highlight(ms[selectedMessage].ID).
 			ScrollToHighlight()
 		return nil
-	} else if util.HasKeybinding(conf.Keybindings.SelectLastMessage, e.Name()) {
+	} else if hasKeybinding(conf.Keybindings.SelectLastMessage, e.Name()) {
 		selectedMessage = len(ms) - 1
 		messagesView.
 			Highlight(ms[selectedMessage].ID).
 			ScrollToHighlight()
 		return nil
-	} else if util.HasKeybinding(conf.Keybindings.SelectMessageReference, e.Name()) {
+	} else if hasKeybinding(conf.Keybindings.SelectMessageReference, e.Name()) {
 		hs := messagesView.GetHighlights()
 		if len(hs) == 0 {
 			return nil
 		}
 
-		_, m := util.FindMessageByID(selectedChannel.Messages, hs[0])
+		_, m := findMessageByID(selectedChannel.Messages, hs[0])
 		if m.ReferencedMessage != nil {
-			selectedMessage, _ = util.FindMessageByID(selectedChannel.Messages, m.ReferencedMessage.ID)
+			selectedMessage, _ = findMessageByID(selectedChannel.Messages, m.ReferencedMessage.ID)
 			messagesView.
 				Highlight(m.ReferencedMessage.ID).
 				ScrollToHighlight()
 		}
 
 		return nil
-	} else if util.HasKeybinding(conf.Keybindings.ReplySelectedMessage, e.Name()) {
+	} else if hasKeybinding(conf.Keybindings.ReplySelectedMessage, e.Name()) {
 		hs := messagesView.GetHighlights()
 		if len(hs) == 0 {
 			return nil
 		}
 
-		_, m := util.FindMessageByID(selectedChannel.Messages, hs[0])
+		_, m := findMessageByID(selectedChannel.Messages, hs[0])
 		messageInputField.SetTitle("Replying to " + m.Author.String())
 		app.SetFocus(messageInputField)
 		return nil
-	} else if util.HasKeybinding(conf.Keybindings.MentionReplySelectedMessage, e.Name()) {
+	} else if hasKeybinding(conf.Keybindings.MentionReplySelectedMessage, e.Name()) {
 		hs := messagesView.GetHighlights()
 		if len(hs) == 0 {
 			return nil
 		}
 
-		_, m := util.FindMessageByID(selectedChannel.Messages, hs[0])
+		_, m := findMessageByID(selectedChannel.Messages, hs[0])
 		messageInputField.SetTitle("[@] Replying to " + m.Author.String())
 		app.SetFocus(messageInputField)
 		return nil
-	} else if util.HasKeybinding(conf.Keybindings.CopySelectedMessage, e.Name()) {
+	} else if hasKeybinding(conf.Keybindings.CopySelectedMessage, e.Name()) {
 		hs := messagesView.GetHighlights()
 		if len(hs) == 0 {
 			return nil
 		}
 
-		_, m := util.FindMessageByID(selectedChannel.Messages, hs[0])
+		_, m := findMessageByID(selectedChannel.Messages, hs[0])
 		err := clipboard.WriteAll(m.Content)
 		if err != nil {
 			return nil
@@ -342,3 +394,57 @@ func newLoginForm(onLoginFormLoginButtonSelected func(), mfa bool) *tview.Form {
 
 	return w
 }
+
+// getTreeNodeByReference walks the root `*TreeNode` of the given `*TreeView` *treeView* and returns the TreeNode whose reference is equal to the given reference *r*. If the `*TreeNode` is not found, `nil` is returned instead.
+func getTreeNodeByReference(treeView *tview.TreeView, r interface{}) (mn *tview.TreeNode) {
+	treeView.GetRoot().Walk(func(n, _ *tview.TreeNode) bool {
+		if n.GetReference() == r {
+			mn = n
+			return false
+		}
+
+		return true
+	})
+
+	return
+}
+
+// createChannelNode builds (encorporates unread channels in bold tag, otherwise dim, etc.) and returns a node according to the type of the given channel *c*.
+func createChannelNode(s *discordgo.State, c *discordgo.Channel) *tview.TreeNode {
+	var cn *tview.TreeNode
+	switch c.Type {
+	case discordgo.ChannelTypeGuildText, discordgo.ChannelTypeGuildNews:
+		tag := "[::d]"
+		if channelIsUnread(s, c) {
+			tag = "[::b]"
+		}
+
+		cn = tview.NewTreeNode(tag + channelToString(c) + "[::-]").
+			SetReference(c.ID)
+	case discordgo.ChannelTypeGuildCategory:
+		cn = tview.NewTreeNode(c.Name).
+			SetReference(c.ID)
+	}
+
+	return cn
+}
+
+// hasPermission returns a boolean that indicates whether the client user has the given permission *p* in the given channel ID *cID*.
+func hasPermission(s *discordgo.State, cID string, p int64) bool {
+	perm, err := s.UserChannelPermissions(s.User.ID, cID)
+	if err != nil {
+		return false
+	}
+
+	return perm&p == p
+}
+
+func hasKeybinding(sl []string, s string) bool {
+	for _, str := range sl {
+		if str == s {
+			return true
+		}
+	}
+
+	return false
+}

+ 0 - 53
util/discord.go

@@ -1,53 +0,0 @@
-package util
-
-import (
-	"strings"
-
-	"github.com/ayntgl/discordgo"
-)
-
-// ChannelToString constructs a string representation of the given channel. The string representation may vary for different channel types.
-func ChannelToString(c *discordgo.Channel) string {
-	var repr string
-	if c.Name != "" {
-		repr = "#" + c.Name
-	} else if len(c.Recipients) == 1 {
-		rp := c.Recipients[0]
-		repr = rp.Username + "#" + rp.Discriminator
-	} else {
-		rps := make([]string, len(c.Recipients))
-		for i, r := range c.Recipients {
-			rps[i] = r.Username + "#" + r.Discriminator
-		}
-
-		repr = strings.Join(rps, ", ")
-	}
-
-	return repr
-}
-
-// ChannelIsUnread returns `true` if the given channel is marked as read by the client user, otherwise `false`.
-func ChannelIsUnread(s *discordgo.State, c *discordgo.Channel) bool {
-	if c.LastMessageID == "" {
-		return false
-	}
-
-	for _, rs := range s.ReadState {
-		if c.ID == rs.ID {
-			return c.LastMessageID != rs.LastMessageID
-		}
-	}
-
-	return false
-}
-
-// FindMessageByID returns the index and the `*Message` struct of the current message if the given message ID *mID* is equal to the current message ID. If the given message ID *mID* is not found in the given slice *ms*, `-1` and `nil` are returned instead.
-func FindMessageByID(ms []*discordgo.Message, mID string) (int, *discordgo.Message) {
-	for i, m := range ms {
-		if m.ID == mID {
-			return i, m
-		}
-	}
-
-	return -1, nil
-}

+ 0 - 113
util/ui.go

@@ -1,113 +0,0 @@
-package util
-
-import (
-	"github.com/ayntgl/discordgo"
-	"github.com/rivo/tview"
-)
-
-// GetTreeNodeByReference walks the root `*TreeNode` of the given `*TreeView` *treeView* and returns the TreeNode whose reference is equal to the given reference *r*. If the `*TreeNode` is not found, `nil` is returned instead.
-func GetTreeNodeByReference(treeView *tview.TreeView, r interface{}) (mn *tview.TreeNode) {
-	treeView.GetRoot().Walk(func(n, _ *tview.TreeNode) bool {
-		if n.GetReference() == r {
-			mn = n
-			return false
-		}
-
-		return true
-	})
-
-	return
-}
-
-// CreateTopLevelChannelsNodes builds and creates `*tview.TreeNode`s for top-level (channels that have an empty parent ID and of type GUILD_TEXT, GUILD_NEWS) channels. If the client user does not have the VIEW_CHANNEL permission for a channel, the channel is excluded from the parent.
-func CreateTopLevelChannelsNodes(treeView *tview.TreeView, 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 !HasPermission(s, c.ID, discordgo.PermissionViewChannel) {
-				continue
-			}
-
-			n.AddChild(CreateChannelNode(s, c))
-			continue
-		}
-	}
-}
-
-// CreateCategoryChannelsNodes builds and creates `*tview.TreeNode`s for category (type: GUILD_CATEGORY) channels. If the client user does not have the VIEW_CHANNEL permission for a channel, the channel is excluded from the parent.
-func CreateCategoryChannelsNodes(treeView *tview.TreeView, s *discordgo.State, n *tview.TreeNode, cs []*discordgo.Channel) {
-CategoryLoop:
-	for _, c := range cs {
-		if c.Type == discordgo.ChannelTypeGuildCategory {
-			if !HasPermission(s, c.ID, discordgo.PermissionViewChannel) {
-				continue
-			}
-
-			for _, child := range cs {
-				if child.ParentID == c.ID {
-					n.AddChild(CreateChannelNode(s, c))
-					continue CategoryLoop
-				}
-			}
-
-			n.AddChild(CreateChannelNode(s, c))
-		}
-	}
-}
-
-// CreateSecondLevelChannelsNodes builds and creates `*tview.TreeNode`s for second-level (channels that have a non-empty parent ID and of type GUILD_TEXT, GUILD_NEWS) channels. If the client user does not have the VIEW_CHANNEL permission for a channel, the channel is excluded from the parent.
-func CreateSecondLevelChannelsNodes(treeView *tview.TreeView, s *discordgo.State, cs []*discordgo.Channel) {
-	for _, c := range cs {
-		if (c.Type == discordgo.ChannelTypeGuildText || c.Type == discordgo.ChannelTypeGuildNews) &&
-			(c.ParentID != "") {
-			if !HasPermission(s, c.ID, discordgo.PermissionViewChannel) {
-				continue
-			}
-
-			pn := GetTreeNodeByReference(treeView, c.ParentID)
-			if pn != nil {
-				pn.AddChild(CreateChannelNode(s, c))
-			}
-		}
-	}
-}
-
-// CreateChannelNode builds (encorporates unread channels in bold tag, otherwise dim, etc.) and returns a node according to the type of the given channel *c*.
-func CreateChannelNode(s *discordgo.State, c *discordgo.Channel) *tview.TreeNode {
-	var cn *tview.TreeNode
-	switch c.Type {
-	case discordgo.ChannelTypeGuildText, discordgo.ChannelTypeGuildNews:
-		tag := "[::d]"
-		if ChannelIsUnread(s, c) {
-			tag = "[::b]"
-		}
-
-		cn = tview.NewTreeNode(tag + ChannelToString(c) + "[::-]").
-			SetReference(c.ID)
-	case discordgo.ChannelTypeGuildCategory:
-		cn = tview.NewTreeNode(c.Name).
-			SetReference(c.ID)
-	}
-
-	return cn
-}
-
-// HasPermission returns a boolean that indicates whether the client user has the given permission *p* in the given channel ID *cID*.
-func HasPermission(s *discordgo.State, cID string, p int64) bool {
-	perm, err := s.UserChannelPermissions(s.User.ID, cID)
-	if err != nil {
-		return false
-	}
-
-	return perm&p == p
-}
-
-func HasKeybinding(sl []string, s string) bool {
-	for _, str := range sl {
-		if str == s {
-			return true
-		}
-	}
-
-	return false
-}