From 987a2e870af472f169475fadea4a7baf4585d9ed Mon Sep 17 00:00:00 2001
From: Andrey Petrov <andrey.petrov@shazow.net>
Date: Thu, 30 Jul 2020 12:52:32 -0400
Subject: [PATCH] chat/message: Set LastMsg during render of self public
 messages, fix sorting

Also fixed chat tests
---
 chat/message/message.go |  3 +++
 chat/message/user.go    | 30 ++++++++++++++++++++++--------
 chat/room_test.go       | 15 ++++++++++-----
 3 files changed, 35 insertions(+), 13 deletions(-)

diff --git a/chat/message/message.go b/chat/message/message.go
index 01de0ce..79244eb 100644
--- a/chat/message/message.go
+++ b/chat/message/message.go
@@ -131,6 +131,9 @@ func (m PublicMsg) RenderFor(cfg UserConfig) string {
 
 // RenderSelf renders the message for when it's echoing your own message.
 func (m PublicMsg) RenderSelf(cfg UserConfig) string {
+	if cfg.Theme == nil {
+		return fmt.Sprintf("[%s] %s", m.from.Name(), m.body)
+	}
 	return fmt.Sprintf("[%s] %s", cfg.Theme.ColorName(m.from), m.body)
 }
 
diff --git a/chat/message/user.go b/chat/message/user.go
index d4cc304..8e522d2 100644
--- a/chat/message/user.go
+++ b/chat/message/user.go
@@ -62,6 +62,12 @@ func (u *User) Joined() time.Time {
 	return u.joined
 }
 
+func (u *User) LastMsg() time.Time {
+	u.mu.Lock()
+	defer u.mu.Unlock()
+	return u.lastMsg
+}
+
 func (u *User) Config() UserConfig {
 	u.mu.Lock()
 	defer u.mu.Unlock()
@@ -161,6 +167,10 @@ func (u *User) render(m Message) string {
 	switch m := m.(type) {
 	case PublicMsg:
 		if u == m.From() {
+			u.mu.Lock()
+			u.lastMsg = m.Timestamp()
+			u.mu.Unlock()
+
 			if !cfg.Echo {
 				return ""
 			}
@@ -204,9 +214,6 @@ func (u *User) writeMsg(m Message) error {
 
 // HandleMsg will render the message to the screen, blocking.
 func (u *User) HandleMsg(m Message) error {
-	u.mu.Lock()
-	u.lastMsg = m.Timestamp()
-	u.mu.Unlock()
 	return u.writeMsg(m)
 }
 
@@ -248,7 +255,9 @@ func init() {
 	// TODO: Seed random?
 }
 
-// RecentActiveUsers is a slice of *Users that knows how to be sorted by the time of the last message.
+// RecentActiveUsers is a slice of *Users that knows how to be sorted by the
+// time of the last message. If no message has been sent, then fall back to the
+// time joined instead.
 type RecentActiveUsers []*User
 
 func (a RecentActiveUsers) Len() int      { return len(a) }
@@ -259,10 +268,15 @@ func (a RecentActiveUsers) Less(i, j int) bool {
 	a[j].mu.Lock()
 	defer a[j].mu.Unlock()
 
-	if a[i].lastMsg.IsZero() {
-		return a[i].joined.Before(a[j].joined)
-	} else {
-		return a[i].lastMsg.Before(a[j].lastMsg)
+	ai := a[i].lastMsg
+	if ai.IsZero() {
+		ai = a[i].joined
 	}
 
+	aj := a[j].lastMsg
+	if aj.IsZero() {
+		aj = a[j].joined
+	}
+
+	return ai.After(aj)
 }
diff --git a/chat/room_test.go b/chat/room_test.go
index c316e15..44c8f0c 100644
--- a/chat/room_test.go
+++ b/chat/room_test.go
@@ -388,17 +388,22 @@ func TestRoomNamesPrefix(t *testing.T) {
 		}
 	}
 
+	sendMsg := func(from *Member, body string) {
+		// lastMsg is set during render of self messags, so we can't use NewMsg
+		from.HandleMsg(message.NewPublicMsg(body, from.User))
+	}
+
 	// Inject some activity
-	members[2].HandleMsg(message.NewMsg("hi")) // aac
-	members[0].HandleMsg(message.NewMsg("hi")) // aaa
-	members[3].HandleMsg(message.NewMsg("hi")) // foo
-	members[1].HandleMsg(message.NewMsg("hi")) // aab
+	sendMsg(members[2], "hi") // aac
+	sendMsg(members[0], "hi") // aaa
+	sendMsg(members[3], "hi") // foo
+	sendMsg(members[1], "hi") // aab
 
 	if got, want := r.NamesPrefix("a"), []string{"aab", "aaa", "aac"}; !reflect.DeepEqual(got, want) {
 		t.Errorf("got: %q; want: %q", got, want)
 	}
 
-	members[2].HandleMsg(message.NewMsg("hi")) // aac
+	sendMsg(members[2], "hi") // aac
 	if got, want := r.NamesPrefix("a"), []string{"aac", "aab", "aaa"}; !reflect.DeepEqual(got, want) {
 		t.Errorf("got: %q; want: %q", got, want)
 	}