From 14f93ee1c0f3d941594faf020d74653cc93fb4a5 Mon Sep 17 00:00:00 2001 From: ayntgl <90600421+ayntgl@users.noreply.github.com> Date: Fri, 1 Oct 2021 22:25:11 +0400 Subject: [PATCH] feat: mark unread channels (#65) * feat: distinguish unread messages, channels, and guilds * feat: add UI acknowledgement functionality * fix: return if tree node by reference not found * feat: mark pre-unread messages with bold tag --- discord.go | 50 ++++++++++++++++++++++++++++++++------------ renderer.go | 12 ----------- ui.go | 60 +++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 82 insertions(+), 40 deletions(-) diff --git a/discord.go b/discord.go index 94ab505..d860acf 100644 --- a/discord.go +++ b/discord.go @@ -48,7 +48,15 @@ func onSessionReady(_ *discordgo.Session, r *discordgo.Ready) { }) for _, c := range r.PrivateChannels { - cn := tview.NewTreeNode(generateChannelRepr(c)).SetReference(c.ID) + var tag string + if isUnread(c) { + tag = "[::b]" + } else { + tag = "[::d]" + } + + cn := tview.NewTreeNode(tag + generateChannelRepr(c) + "[::-]"). + SetReference(c.ID) dmNode.AddChild(cn) } @@ -89,12 +97,27 @@ func onSessionReady(_ *discordgo.Session, r *discordgo.Ready) { channelsTree.SetCurrentNode(n) } -func onSessionMessageCreate(_ *discordgo.Session, m *discordgo.MessageCreate) { - if selectedChannel == nil { - selectedChannel = &discordgo.Channel{ID: ""} +func isUnread(c *discordgo.Channel) bool { + if c.LastMessageID == "" { + return false } - if selectedChannel.ID != m.ChannelID { + for _, rs := range session.State.ReadState { + if c.ID == rs.ID { + return c.LastMessageID != rs.LastMessageID + } + } + + return false +} + +func onSessionMessageCreate(_ *discordgo.Session, m *discordgo.MessageCreate) { + c, err := session.State.Channel(m.ChannelID) + if err != nil { + return + } + + if selectedChannel == nil || selectedChannel.ID != m.ChannelID { if conf.Notifications { for _, u := range m.Mentions { if u.ID == session.State.User.ID { @@ -102,10 +125,6 @@ func onSessionMessageCreate(_ *discordgo.Session, m *discordgo.MessageCreate) { if err != nil { return } - c, err := session.State.Channel(m.ChannelID) - if err != nil { - return - } go beeep.Alert(fmt.Sprintf("%s (#%s)", g.Name, c.Name), m.ContentWithMentionsReplaced(), "") return @@ -113,11 +132,16 @@ func onSessionMessageCreate(_ *discordgo.Session, m *discordgo.MessageCreate) { } } - return + cn := getTreeNodeByReference(c.ID) + if cn == nil { + return + } + cn.SetText("[::b]" + generateChannelRepr(c) + "[::-]") + app.Draw() + } else { + selectedChannel.Messages = append(selectedChannel.Messages, m.Message) + renderMessage(m.Message) } - - selectedChannel.Messages = append(selectedChannel.Messages, m.Message) - renderMessage(m.Message) } type loginResponse struct { diff --git a/renderer.go b/renderer.go index 51b602c..91065f9 100644 --- a/renderer.go +++ b/renderer.go @@ -13,18 +13,6 @@ var italicRegex = regexp.MustCompile(`(?m)\*(.*?)\*`) var underlineRegex = regexp.MustCompile(`(?m)__(.*?)__`) var strikeThroughRegex = regexp.MustCompile(`(?m)~~(.*?)~~`) -func renderMessages(cID string) { - ms, err := session.ChannelMessages(cID, conf.GetMessagesLimit, "", "", "") - if err != nil { - return - } - - for i := len(ms) - 1; i >= 0; i-- { - selectedChannel.Messages = append(selectedChannel.Messages, ms[i]) - renderMessage(ms[i]) - } -} - func renderMessage(m *discordgo.Message) { var b strings.Builder diff --git a/ui.go b/ui.go index f834c95..079603c 100644 --- a/ui.go +++ b/ui.go @@ -62,7 +62,7 @@ func onChannelsTreeSelected(n *tview.TreeNode) { // Unhighlight the already-highlighted regions. messagesTextView.Highlight() - if len(n.GetChildren()) != 0 { + if len(n.GetChildren()) != 0 || n.GetText() == "Direct Messages" { n.SetExpanded(!n.IsExpanded()) } else { cID := n.GetReference().(string) @@ -86,18 +86,28 @@ func onChannelsTreeSelected(n *tview.TreeNode) { messagesTextView.SetTitle(generateChannelRepr(c)) } + if strings.HasPrefix(n.GetText(), "[::b]") { + n.SetText("[::d]" + generateChannelRepr(c) + "[::-]") + } + messagesTextView.Clear() - go renderMessages(c.ID) + + ms, err := session.ChannelMessages(cID, conf.GetMessagesLimit, "", "", "") + if err != nil { + return + } + + for i := len(ms) - 1; i >= 0; i-- { + selectedChannel.Messages = append(selectedChannel.Messages, ms[i]) + go renderMessage(ms[i]) + } + + if len(ms) != 0 && isUnread(c) { + go session.ChannelMessageAck(c.ID, c.LastMessageID, "") + } } } -func newTextChannelTreeNode(c *discordgo.Channel) *tview.TreeNode { - n := tview.NewTreeNode("[::d]" + generateChannelRepr(c) + "[::-]"). - SetReference(c.ID) - - return n -} - func createTopLevelChannelsTreeNodes( n *tview.TreeNode, cs []*discordgo.Channel, @@ -105,11 +115,20 @@ func createTopLevelChannelsTreeNodes( for _, c := range cs { if (c.Type == discordgo.ChannelTypeGuildText || c.Type == discordgo.ChannelTypeGuildNews) && (c.ParentID == "") { - if p, err := session.State.UserChannelPermissions(session.State.User.ID, c.ID); err != nil || p&discordgo.PermissionViewChannel != discordgo.PermissionViewChannel { + p, err := session.State.UserChannelPermissions(session.State.User.ID, c.ID) + if err != nil || p&discordgo.PermissionViewChannel != discordgo.PermissionViewChannel { continue } - cn := newTextChannelTreeNode(c) + var tag string + if isUnread(c) { + tag = "[::b]" + } else { + tag = "[::d]" + } + + cn := tview.NewTreeNode(tag + generateChannelRepr(c) + "[::-]"). + SetReference(c.ID) n.AddChild(cn) continue } @@ -123,7 +142,8 @@ func createCategoryChannelsTreeNodes( CategoryLoop: for _, c := range cs { if c.Type == discordgo.ChannelTypeGuildCategory { - if p, err := session.State.UserChannelPermissions(session.State.User.ID, c.ID); err != nil || p&discordgo.PermissionViewChannel != discordgo.PermissionViewChannel { + p, err := session.State.UserChannelPermissions(session.State.User.ID, c.ID) + if err != nil || p&discordgo.PermissionViewChannel != discordgo.PermissionViewChannel { continue } @@ -147,12 +167,22 @@ func createSecondLevelChannelsTreeNodes(cs []*discordgo.Channel) { for _, c := range cs { if (c.Type == discordgo.ChannelTypeGuildText || c.Type == discordgo.ChannelTypeGuildNews) && (c.ParentID != "") { - if p, err := session.State.UserChannelPermissions(session.State.User.ID, c.ID); err != nil || p&discordgo.PermissionViewChannel != discordgo.PermissionViewChannel { + p, err := session.State.UserChannelPermissions(session.State.User.ID, c.ID) + if err != nil || p&discordgo.PermissionViewChannel != discordgo.PermissionViewChannel { continue } - if pn := getTreeNodeByReference(c.ParentID); pn != nil { - cn := newTextChannelTreeNode(c) + var tag string + if isUnread(c) { + tag = "[::b]" + } else { + tag = "[::d]" + } + + pn := getTreeNodeByReference(c.ParentID) + if pn != nil { + cn := tview.NewTreeNode(tag + generateChannelRepr(c) + "[::-]"). + SetReference(c.ID) pn.AddChild(cn) } }