commit bf7a913

Angus Gibson  ·  2024-10-10 04:44:34 +0000 UTC
parent 17006ee
Show members with latest messages first in member completion

Fixes: https://todo.sr.ht/~delthas/senpai/139
4 files changed,  +66, -25
M app.go
M app.go
+1, -1
1@@ -216,7 +216,7 @@ func NewApp(cfg Config) (app *App, err error) {
2 }
3 
4 func (app *App) Close() {
5-	app.win.Exit() // tell all instances of app.ircLoop to stop when possible
6+	app.win.Exit()       // tell all instances of app.ircLoop to stop when possible
7 	app.events <- event{ // tell app.eventLoop to stop
8 		src:     "*",
9 		content: nil,
+21, -1
 1@@ -4,12 +4,30 @@ import (
 2 	"fmt"
 3 	"os"
 4 	"path/filepath"
 5+	"sort"
 6 	"strings"
 7 
 8 	"git.sr.ht/~delthas/senpai/irc"
 9 	"git.sr.ht/~delthas/senpai/ui"
10 )
11 
12+type members []irc.Member
13+
14+func (m members) Len() int {
15+	return len(m)
16+}
17+
18+func (m members) Less(i, j int) bool {
19+	if c := m[i].LastActive.Compare(m[j].LastActive); c != 0 {
20+		return c > 0
21+	}
22+	return strings.ToLower(m[i].Name.Name) < strings.ToLower(m[j].Name.Name)
23+}
24+
25+func (m members) Swap(i, j int) {
26+	m[i], m[j] = m[j], m[i]
27+}
28+
29 type completionAsync func(e irc.Event) []ui.Completion
30 
31 func (app *App) completionsChannelMembers(cs []ui.Completion, cursorIdx int, text []rune) []ui.Completion {
32@@ -27,7 +45,9 @@ func (app *App) completionsChannelMembers(cs []ui.Completion, cursorIdx int, tex
33 	netID, buffer := app.win.CurrentBuffer()
34 	s := app.sessions[netID] // is not nil
35 	wordCf := s.Casemap(string(word))
36-	for _, name := range s.Names(buffer) {
37+	names := members(s.Names(buffer))
38+	sort.Sort(names)
39+	for _, name := range names {
40 		if strings.HasPrefix(s.Casemap(name.Name.Name), wordCf) {
41 			nickComp := []rune(name.Name.Name)
42 			if start == 0 {
+43, -23
  1@@ -90,14 +90,19 @@ type User struct {
  2 	Disconnected bool    // can only be true for monitored users.
  3 }
  4 
  5+type ChannelMember struct {
  6+	Membership string
  7+	LastActive time.Time
  8+}
  9+
 10 // Channel is a joined channel.
 11 type Channel struct {
 12-	Name      string           // the name of the channel.
 13-	Members   map[*User]string // the set of members associated with their membership.
 14-	Topic     string           // the topic of the channel, or "" if absent.
 15-	TopicWho  *Prefix          // the name of the last user who set the topic.
 16-	TopicTime time.Time        // the last time the topic has been changed.
 17-	Read      time.Time        // the time until which messages were read.
 18+	Name      string                  // the name of the channel.
 19+	Members   map[*User]ChannelMember // the set of members associated with their membership.
 20+	Topic     string                  // the topic of the channel, or "" if absent.
 21+	TopicWho  *Prefix                 // the name of the last user who set the topic.
 22+	TopicTime time.Time               // the last time the topic has been changed.
 23+	Read      time.Time               // the time until which messages were read.
 24 	Pinned    bool
 25 	Muted     bool
 26 
 27@@ -304,13 +309,14 @@ func (s *Session) Names(target string) []Member {
 28 	if s.IsChannel(target) {
 29 		if c, ok := s.channels[s.Casemap(target)]; ok {
 30 			names = make([]Member, 0, len(c.Members))
 31-			for u, pl := range c.Members {
 32+			for u, m := range c.Members {
 33 				names = append(names, Member{
 34-					PowerLevel:   pl,
 35+					PowerLevel:   m.Membership,
 36 					Name:         u.Name.Copy(),
 37 					Away:         u.Away,
 38 					Disconnected: u.Disconnected,
 39 					Self:         s.nickCf == s.casemap(u.Name.Name),
 40+					LastActive:   m.LastActive,
 41 				})
 42 			}
 43 		}
 44@@ -948,7 +954,7 @@ func (s *Session) handleMessageRegistered(msg Message, playback bool) (Event, er
 45 		if s.IsMe(nickCf) {
 46 			s.channels[channelCf] = Channel{
 47 				Name:    msg.Params[0],
 48-				Members: map[*User]string{},
 49+				Members: map[*User]ChannelMember{},
 50 			}
 51 			if _, ok := s.enabledCaps["away-notify"]; ok {
 52 				// Only try to know who is away if the list is
 53@@ -960,7 +966,7 @@ func (s *Session) handleMessageRegistered(msg Message, playback bool) (Event, er
 54 			if _, ok := s.users[nickCf]; !ok {
 55 				s.users[nickCf] = &User{Name: msg.Prefix.Copy()}
 56 			}
 57-			c.Members[s.users[nickCf]] = ""
 58+			c.Members[s.users[nickCf]] = ChannelMember{}
 59 			return UserJoinEvent{
 60 				User:    msg.Prefix.Name,
 61 				Channel: c.Name,
 62@@ -1136,7 +1142,9 @@ func (s *Session) handleMessageRegistered(msg Message, playback bool) (Event, er
 63 				if _, ok := s.users[nickCf]; !ok {
 64 					s.users[nickCf] = &User{Name: name.Name.Copy()}
 65 				}
 66-				c.Members[s.users[nickCf]] = name.PowerLevel
 67+				m := c.Members[s.users[nickCf]]
 68+				m.Membership = name.PowerLevel
 69+				c.Members[s.users[nickCf]] = m
 70 			}
 71 
 72 			s.channels[channelCf] = c
 73@@ -1262,23 +1270,24 @@ func (s *Session) handleMessageRegistered(msg Message, playback bool) (Event, er
 74 				}
 75 				nickCf := s.Casemap(change.Param)
 76 				user := s.users[nickCf]
 77-				membership, ok := c.Members[user]
 78+				m, ok := c.Members[user]
 79 				if !ok {
 80 					continue
 81 				}
 82 				var newMembership []byte
 83 				if change.Enable {
 84-					newMembership = append([]byte(membership), s.prefixSymbols[i])
 85+					newMembership = append([]byte(m.Membership), s.prefixSymbols[i])
 86 					sort.Slice(newMembership, func(i, j int) bool {
 87 						i = strings.IndexByte(s.prefixSymbols, newMembership[i])
 88 						j = strings.IndexByte(s.prefixSymbols, newMembership[j])
 89 						return i < j
 90 					})
 91-				} else if j := strings.IndexByte(membership, s.prefixSymbols[i]); j >= 0 {
 92-					newMembership = []byte(membership)
 93+				} else if j := strings.IndexByte(m.Membership, s.prefixSymbols[i]); j >= 0 {
 94+					newMembership = []byte(m.Membership)
 95 					newMembership = append(newMembership[:j], newMembership[j+1:]...)
 96 				}
 97-				c.Members[user] = string(newMembership)
 98+				m.Membership = string(newMembership)
 99+				c.Members[user] = m
100 			}
101 			s.channels[channelCf] = c
102 			return ModeChangeEvent{
103@@ -1325,15 +1334,26 @@ func (s *Session) handleMessageRegistered(msg Message, playback bool) (Event, er
104 			return nil, err
105 		}
106 
107-		if playback {
108-			return s.newMessageEvent(msg)
109-		}
110-
111 		targetCf := s.casemap(target)
112 		nickCf := s.casemap(msg.Prefix.Name)
113-		s.typings.Done(targetCf, nickCf)
114-
115-		return s.newMessageEvent(msg)
116+		if !playback {
117+			s.typings.Done(targetCf, nickCf)
118+		}
119+		ev, err := s.newMessageEvent(msg)
120+		if err != nil {
121+			return nil, err
122+		}
123+		if c, ok := s.channels[targetCf]; ok {
124+			if u, ok := s.users[nickCf]; ok {
125+				if m, ok := c.Members[u]; ok {
126+					if ev.Time.After(m.LastActive) {
127+						m.LastActive = ev.Time
128+						c.Members[u] = m
129+					}
130+				}
131+			}
132+		}
133+		return ev, nil
134 	case "TAGMSG":
135 		if playback {
136 			return nil, nil
+1, -0
1@@ -467,6 +467,7 @@ type Member struct {
2 	Away         bool
3 	Disconnected bool
4 	Self         bool // Added by senpai
5+	LastActive   time.Time
6 }
7 
8 type members struct {