chat/message: Fix RecentActiveUsers sort order
[ssh-chat] / chat / message / user.go
index d094bde6e8d53ca6d1d87aa744490d525288e173..d4cc304c539c995076dcc27e02dba1adefc84538 100644 (file)
@@ -16,7 +16,6 @@ const messageBuffer = 5
 const messageTimeout = 5 * time.Second
 const reHighlight = `\b(%s)\b`
 const timestampTimeout = 30 * time.Minute
-const timestampLayout = "2006-01-02 15:04:05 UTC"
 
 var ErrUserClosed = errors.New("user closed")
 
@@ -158,17 +157,37 @@ func (u *User) SetHighlight(s string) error {
 
 func (u *User) render(m Message) string {
        cfg := u.Config()
+       var out string
        switch m := m.(type) {
        case PublicMsg:
-               return m.RenderFor(cfg) + Newline
+               if u == m.From() {
+                       if !cfg.Echo {
+                               return ""
+                       }
+                       out += m.RenderSelf(cfg)
+               } else {
+                       out += m.RenderFor(cfg)
+               }
        case *PrivateMsg:
+               out += m.Render(cfg.Theme)
                if cfg.Bell {
-                       return m.Render(cfg.Theme) + Bel + Newline
+                       out += Bel
                }
-               return m.Render(cfg.Theme) + Newline
+       case *CommandMsg:
+               out += m.RenderSelf(cfg)
        default:
-               return m.Render(cfg.Theme) + Newline
+               out += m.Render(cfg.Theme)
+       }
+       if cfg.Timeformat != nil {
+               ts := m.Timestamp()
+               if cfg.Timezone != nil {
+                       ts = ts.In(cfg.Timezone)
+               } else {
+                       ts = ts.UTC()
+               }
+               return cfg.Theme.Timestamp(ts.Format(*cfg.Timeformat)) + "  " + out + Newline
        }
+       return out + Newline
 }
 
 // writeMsg renders the message and attempts to write it, will Close the user
@@ -186,20 +205,8 @@ 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()
-       cfg := u.config
-       lastMsg := u.lastMsg
        u.lastMsg = m.Timestamp()
-       injectTimestamp := !lastMsg.IsZero() && cfg.Timestamp && u.lastMsg.Sub(lastMsg) > timestampTimeout
        u.mu.Unlock()
-
-       if injectTimestamp {
-               // Inject a timestamp at most once every timestampTimeout between message intervals
-               ts := NewSystemMsg(fmt.Sprintf("Timestamp: %s", m.Timestamp().UTC().Format(timestampLayout)), u)
-               if err := u.writeMsg(ts); err != nil {
-                       return err
-               }
-       }
-
        return u.writeMsg(m)
 }
 
@@ -219,11 +226,13 @@ func (u *User) Send(m Message) error {
 
 // Container for per-user configurations.
 type UserConfig struct {
-       Highlight *regexp.Regexp
-       Bell      bool
-       Quiet     bool
-       Timestamp bool
-       Theme     *Theme
+       Highlight  *regexp.Regexp
+       Bell       bool
+       Quiet      bool
+       Echo       bool // Echo shows your own messages after sending, disabled for bots
+       Timeformat *string
+       Timezone   *time.Location
+       Theme      *Theme
 }
 
 // Default user configuration to use
@@ -231,10 +240,29 @@ var DefaultUserConfig UserConfig
 
 func init() {
        DefaultUserConfig = UserConfig{
-               Bell:      true,
-               Quiet:     false,
-               Timestamp: false,
+               Bell:  true,
+               Echo:  true,
+               Quiet: false,
        }
 
        // TODO: Seed random?
 }
+
+// RecentActiveUsers is a slice of *Users that knows how to be sorted by the time of the last message.
+type RecentActiveUsers []*User
+
+func (a RecentActiveUsers) Len() int      { return len(a) }
+func (a RecentActiveUsers) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a RecentActiveUsers) Less(i, j int) bool {
+       a[i].mu.Lock()
+       defer a[i].mu.Unlock()
+       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)
+       }
+
+}