Mentions are now displayed in the guildlist
MODIFY
readstate/readmarkers.go
readstate/readmarkers.go
@@ -12,6 +12,7 @@ import (
var (
data = make(map[string]uint64)
+ mentions = make(map[string]bool)
readStateMutex = &sync.Mutex{}
timerMutex = &sync.Mutex{}
ackTimers = make(map[string]*time.Timer)
@@ -37,6 +38,10 @@ func Load(sessionState *discordgo.State) {
}
data[channelState.ID] = parsed
+
+ if channelState.MentionCount > 0 {
+ mentions[channelState.ID] = true
+ }
}
}
@@ -57,14 +62,16 @@ func ClearReadStateFor(channelID string) {
// anything to the Discord API. The update will only be applied if the new
// message ID is greater than the old one.
func UpdateReadLocal(channelID string, lastMessageID string) bool {
+ readStateMutex.Lock()
+ defer readStateMutex.Unlock()
+
+ delete(mentions, channelID)
+
parsed, parseError := strconv.ParseUint(lastMessageID, 10, 64)
if parseError != nil {
return false
}
- readStateMutex.Lock()
- defer readStateMutex.Unlock()
-
old, isPresent := data[channelID]
if !isPresent || old < parsed {
data[channelID] = parsed
@@ -81,6 +88,8 @@ func UpdateRead(session *discordgo.Session, channel *discordgo.Channel, lastMess
readStateMutex.Lock()
defer readStateMutex.Unlock()
+ delete(mentions, channel.ID)
+
// Avoid unnecessary traffic
if hasBeenReadWithoutLocking(channel, lastMessageID) {
return nil
@@ -159,6 +168,28 @@ func HasGuildBeenRead(guildID string) bool {
return true
}
+// HasGuildBeenMentioned checks whether any channel in the guild mentioned
+// the currently logged in user.
+func HasGuildBeenMentioned(guildID string) bool {
+ if IsGuildMuted(guildID) {
+ return false
+ }
+
+ realGuild, cacheError := state.Guild(guildID)
+ if cacheError == nil {
+ readStateMutex.Lock()
+ defer readStateMutex.Unlock()
+
+ for _, channel := range realGuild.Channels {
+ if hasBeenMentionedWithoutLocking(channel.ID) {
+ return true
+ }
+ }
+ }
+
+ return false
+}
+
func isChannelMuted(channel *discordgo.Channel) bool {
//optimization for the case of guild channels, as the handling for
//private channels will be unnecessarily slower.
@@ -237,6 +268,28 @@ func HasBeenRead(channel *discordgo.Channel, lastMessageID string) bool {
return hasBeenReadWithoutLocking(channel, lastMessageID)
}
+// HasBeenMentioned checks whether the currently logged in user has been
+// mentioned in this channel.
+func HasBeenMentioned(channelID string) bool {
+ readStateMutex.Lock()
+ defer readStateMutex.Unlock()
+
+ return hasBeenMentionedWithoutLocking(channelID)
+}
+
+func hasBeenMentionedWithoutLocking(channelID string) bool {
+ mentioned, ok := mentions[channelID]
+ return ok && mentioned
+}
+
+// MarkAsMentioned sets the given channel ID to mentioned.
+func MarkAsMentioned(channelID string) {
+ readStateMutex.Lock()
+ defer readStateMutex.Unlock()
+
+ mentions[channelID] = true
+}
+
// hasBeenReadWithoutLocking checks whether the passed channel has an unread Message or not.
// The difference to HasBeenRead is, that no locking happens. This is inteded to be used
// for recursive calls to this method and avoiding lock overhead and deadlocks.

MODIFY
ui/channeltree.go
ui/channeltree.go
@@ -148,15 +148,7 @@ CATEGORY_LOOP:
}
func createTopLevelChannelNodes(channelTree *ChannelTree, channel *discordgo.Channel) {
- channelNode := createChannelNode(channel)
- if !readstate.HasBeenRead(channel, channel.LastMessageID) {
- channelTree.channelStates[channelNode] = channelUnread
- if vtxxx {
- channelNode.SetAttributes(tcell.AttrBlink)
- } else {
- channelNode.SetColor(config.GetTheme().AttentionColor)
- }
- }
+ channelNode := channelTree.createTextChannelNode(channel)
channelTree.GetRoot().AddChild(channelNode)
}
@@ -167,19 +159,10 @@ func createChannelCategoryNode(channelTree *ChannelTree, channel *discordgo.Chan
}
func createSecondLevelChannelNodes(channelTree *ChannelTree, channel *discordgo.Channel) {
- channelNode := createChannelNode(channel)
for _, node := range channelTree.GetRoot().GetChildren() {
channelID, ok := node.GetReference().(string)
if ok && channelID == channel.ParentID {
- if !readstate.HasBeenRead(channel, channel.LastMessageID) {
- channelTree.channelStates[channelNode] = channelUnread
- if vtxxx {
- channelNode.SetAttributes(tcell.AttrBlink)
- } else {
- channelNode.SetColor(config.GetTheme().AttentionColor)
- }
- }
-
+ channelNode := channelTree.createTextChannelNode(channel)
node.AddChild(channelNode)
break
}
@@ -208,6 +191,25 @@ func createChannelNode(channel *discordgo.Channel) *tview.TreeNode {
return channelNode
}
+func (channelTree *ChannelTree) createTextChannelNode(channel *discordgo.Channel) *tview.TreeNode {
+ channelNode := createChannelNode(channel)
+
+ if !readstate.HasBeenRead(channel, channel.LastMessageID) {
+ channelTree.channelStates[channelNode] = channelUnread
+ if vtxxx {
+ channelNode.SetAttributes(tcell.AttrBlink)
+ } else {
+ channelNode.SetColor(config.GetTheme().AttentionColor)
+ }
+ }
+
+ if readstate.HasBeenMentioned(channel.ID) {
+ channelTree.markNodeAsMentioned(channelNode, channel.ID)
+ }
+
+ return channelNode
+}
+
// AddOrUpdateChannel either adds a new node for the given channel or updates
// its current node.
func (channelTree *ChannelTree) AddOrUpdateChannel(channel *discordgo.Channel) {
@@ -342,22 +344,25 @@ func (channelTree *ChannelTree) MarkChannelAsMentioned(channelID string) {
channelTree.GetRoot().Walk(func(node, parent *tview.TreeNode) bool {
referenceChannelID, ok := node.GetReference().(string)
if ok && referenceChannelID == channelID {
+ channelTree.markNodeAsMentioned(node, channelID)
+ return false
+ }
+
+ return true
+ })
+}
+
+func (channelTree *ChannelTree) markNodeAsMentioned(node *tview.TreeNode, channelID string) {
channelTree.channelStates[node] = channelMentioned
channel, stateError := channelTree.state.Channel(channelID)
if stateError == nil {
- node.SetText("(@You) " + tviewutil.Escape(channel.Name))
+ node.SetText("(@) " + tviewutil.Escape(channel.Name))
}
if vtxxx {
node.SetAttributes(tcell.AttrBlink)
} else {
node.SetColor(config.GetTheme().AttentionColor)
}
-
- return false
- }
-
- return true
- })
}
// MarkChannelAsLoaded marks a channel as loaded and therefore marks all other

MODIFY
ui/guildlist.go
ui/guildlist.go
@@ -52,7 +52,7 @@ func NewGuildList(guilds []*discordgo.Guild) *GuildList {
guildNode.SetReference(guild.ID)
root.AddChild(guildNode)
- guildList.UpdateNodeState(guildNode, false)
+ guildList.UpdateNodeState(guild, guildNode, false)
guildNode.SetSelectable(true)
}
@@ -64,12 +64,26 @@ func NewGuildList(guilds []*discordgo.Guild) *GuildList {
return guildList
}
+// UpdateNodeStateByGuild updates the state of a guilds node accordingly
+// to its readstate, unless the node is selected.
+//
+// FIXME selected should probably be removed here, but bugs will occur
+// so I'll do it someday ... :D
+func (g *GuildList) UpdateNodeStateByGuild(guild *discordgo.Guild, selected bool) {
+ for _, node := range g.GetRoot().GetChildren() {
+ if node.GetReference().(string) == guild.ID {
+ g.UpdateNodeState(guild, node, selected)
+ break
+ }
+ }
+}
+
// UpdateNodeState updates the state of a node accordingly to its
// readstate, unless the node is selected.
//
// FIXME selected should probably be removed here, but bugs will occur
// so I'll do it someday ... :D
-func (g *GuildList) UpdateNodeState(node *tview.TreeNode, selected bool) {
+func (g *GuildList) UpdateNodeState(guild *discordgo.Guild, node *tview.TreeNode, selected bool) {
if selected {
if vtxxx {
node.SetAttributes(tcell.AttrUnderline)
@@ -77,7 +91,7 @@ func (g *GuildList) UpdateNodeState(node *tview.TreeNode, selected bool) {
node.SetColor(tview.Styles.ContrastBackgroundColor)
}
} else {
- if !readstate.HasGuildBeenRead(node.GetReference().(string)) {
+ if !readstate.HasGuildBeenRead(guild.ID) {
if vtxxx {
node.SetAttributes(tcell.AttrBlink)
} else {
@@ -88,6 +102,12 @@ func (g *GuildList) UpdateNodeState(node *tview.TreeNode, selected bool) {
node.SetColor(tview.Styles.PrimaryTextColor)
}
}
+
+ if readstate.HasGuildBeenMentioned(guild.ID) {
+ node.SetText("(@) " + tviewutil.Escape(guild.Name))
+ } else {
+ node.SetText(tviewutil.Escape(guild.Name))
+ }
}
// SetOnGuildSelect sets the handler for when a guild is selected.

MODIFY
ui/window.go
ui/window.go
@@ -174,6 +174,7 @@ func NewWindow(doRestart chan bool, app *tview.Application, session *discordgo.S
window.guildList = guildList
window.guildList.UpdateUnreadGuildCount()
guildList.SetOnGuildSelect(func(node *tview.TreeNode, guildID string) {
+ //Update previously selected guild.
if window.selectedGuild != nil && window.selectedGuildNode != nil {
window.updateServerReadStatus(window.selectedGuildNode, false)
}
@@ -1460,9 +1461,20 @@ func (window *Window) sendMessage(targetChannelID, message string) {
}
func (window *Window) updateServerReadStatus(guildNode *tview.TreeNode, isSelected bool) {
- window.guildList.UpdateNodeState(guildNode, isSelected)
+ guild, cacheError := window.session.State.Guild(guildNode.GetReference().(string))
+ if cacheError == nil {
+ window.guildList.UpdateNodeState(guild, guildNode, isSelected)
+ window.guildList.UpdateUnreadGuildCount()
+ }
+}
+
+func (window *Window) updateServerReadStatusByID(guildID string, isSelected bool) {
+ guild, cacheError := window.session.State.Guild(guildID)
+ if cacheError == nil && guild != nil {
+ window.guildList.UpdateNodeStateByGuild(guild, isSelected)
window.guildList.UpdateUnreadGuildCount()
}
+}
// prepareMessage prepares a message for being sent to the discord API.
// This will do all necessary escaping and resolving of channel-mentions,
@@ -1866,7 +1878,10 @@ func (window *Window) startMessageHandlerRoutines(input, edit, delete chan *disc
}
} else if channel.Type == discordgo.ChannelTypeGuildText {
if discordutil.MentionsCurrentUserExplicitly(window.session.State, message) {
+ readstate.MarkAsMentioned(channel.ID)
window.app.QueueUpdateDraw(func() {
+ isCurrentGuild := window.selectedGuild != nil && window.selectedGuild.ID == channel.GuildID
+ window.updateServerReadStatusByID(channel.GuildID, isCurrentGuild)
window.channelTree.MarkChannelAsMentioned(channel.ID)
})
} else if !readstate.IsGuildChannelMuted(channel) {
@@ -2703,7 +2718,7 @@ func (window *Window) LoadChannel(channel *discordgo.Channel) error {
//Unlike with the channel, where we can assume it is read, we gotta check
//whether there is still an unread channel and mark the server accordingly.
- wasSelectedGuild := window.selectedGuild != nil && window.selectedGuild.ID != channel.GuildID
+ wasSelectedGuild := window.selectedGuild != nil && window.selectedGuild.ID == channel.GuildID
if channel.GuildID == "" {
window.selectedGuild = nil
@@ -2729,9 +2744,7 @@ func (window *Window) LoadChannel(channel *discordgo.Channel) error {
guild, cacheError := window.session.State.Guild(channel.GuildID)
window.app.QueueUpdateDraw(func() {
- if wasSelectedGuild {
- window.updateServerReadStatus(window.selectedGuildNode, false)
- }
+ window.updateServerReadStatusByID(channel.GuildID, wasSelectedGuild)
if cacheError == nil {
window.selectedGuild = guild

Mentions are now displayed in the guildlist