diff options
author | delthas <delthas@dille.cc> | 2022-02-10 15:44:05 +0100 |
---|---|---|
committer | delthas <delthas@dille.cc> | 2022-02-11 12:18:45 +0100 |
commit | b46a755bfc2afbce7276a2ce075d956ab7dc5b01 (patch) | |
tree | cad716b91edbcf5382ef131defc6728cbc25f8b3 | |
parent | Rename ColorGrey to ColorGray for consistency (diff) |
Add support for the soju.im/read capability and READ command
See: https://github.com/emersion/soju/blob/c7f0634ec8ee94425547b159bc36705582151012/doc/read.md
-rw-r--r-- | app.go | 10 | ||||
-rw-r--r-- | commands.go | 2 | ||||
-rw-r--r-- | irc/events.go | 5 | ||||
-rw-r--r-- | irc/session.go | 33 | ||||
-rw-r--r-- | ui/buffers.go | 34 | ||||
-rw-r--r-- | ui/ui.go | 9 |
6 files changed, 93 insertions, 0 deletions
@@ -221,6 +221,12 @@ func (app *App) eventLoop() { } if !app.pasting { + if netID, buffer, timestamp := app.win.UpdateRead(); buffer != "" { + s := app.sessions[netID] + if s != nil { + s.ReadSet(buffer, timestamp) + } + } app.setStatus() app.updatePrompt() app.setBufferNumbers() @@ -741,6 +747,7 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) { if _, added := app.win.AddBuffer(netID, "", buffer); added { app.monitor[netID][buffer] = struct{}{} s.MonitorAdd(buffer) + s.ReadGet(buffer) s.NewHistoryRequest(buffer). WithLimit(500). Before(msg.TimeOrNow()) @@ -763,6 +770,7 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) { continue } s.MonitorAdd(target) + s.ReadGet(target) app.win.AddBuffer(netID, "", target) // CHATHISTORY BEFORE excludes its bound, so add 1ms // (precision of the time tag) to include that last message. @@ -809,6 +817,8 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) { if !bounds.IsZero() { app.messageBounds[boundKey{netID, ev.Target}] = bounds } + case irc.ReadEvent: + app.win.SetRead(netID, ev.Target, ev.Timestamp) case irc.BouncerNetworkEvent: _, added := app.win.AddBuffer(ev.ID, ev.Name, "") if added { diff --git a/commands.go b/commands.go index 18528db..dd1a82d 100644 --- a/commands.go +++ b/commands.go @@ -343,6 +343,7 @@ func commandDoMsg(app *App, args []string) (err error) { if buffer != "" && !s.IsChannel(target) { app.monitor[netID][buffer] = struct{}{} s.MonitorAdd(buffer) + s.ReadGet(buffer) app.win.AddBuffer(netID, "", buffer) } @@ -452,6 +453,7 @@ func commandDoQuery(app *App, args []string) (err error) { return fmt.Errorf("cannot query a channel, use JOIN instead") } s.MonitorAdd(target) + s.ReadGet(target) i, _ := app.win.AddBuffer(netID, "", target) s.NewHistoryRequest(target).WithLimit(200).Before(time.Now()) app.win.JumpBufferIndex(i) diff --git a/irc/events.go b/irc/events.go index f8f002f..7c33f70 100644 --- a/irc/events.go +++ b/irc/events.go @@ -94,6 +94,11 @@ type HistoryTargetsEvent struct { Targets map[string]time.Time } +type ReadEvent struct { + Target string + Timestamp time.Time +} + type BouncerNetworkEvent struct { ID string Name string diff --git a/irc/session.go b/irc/session.go index 96632bf..ca2c2d9 100644 --- a/irc/session.go +++ b/irc/session.go @@ -60,6 +60,7 @@ var SupportedCapabilities = map[string]struct{}{ "draft/chathistory": {}, "draft/event-playback": {}, "soju.im/bouncer-networks": {}, + "soju.im/read": {}, } // Values taken by the "@+typing=" client tag. TypingUnspec means the value or @@ -426,6 +427,18 @@ func (s *Session) TypingStop(target string) { s.out <- NewMessage("TAGMSG", target).WithTag("+typing", "done") } +func (s *Session) ReadGet(target string) { + if _, ok := s.enabledCaps["soju.im/read"]; ok { + s.out <- NewMessage("READ", target) + } +} + +func (s *Session) ReadSet(target string, timestamp time.Time) { + if _, ok := s.enabledCaps["soju.im/read"]; ok { + s.out <- NewMessage("READ", target, formatTimestamp(timestamp)) + } +} + func (s *Session) MonitorAdd(target string) { targetCf := s.casemap(target) if _, ok := s.monitors[targetCf]; !ok { @@ -1237,6 +1250,26 @@ func (s *Session) handleMessageRegistered(msg Message, playback bool) (Event, er Time: msg.TimeOrNow(), }, nil } + case "READ": + if len(msg.Params) < 2 { + break + } + var target, timestamp string + if err := msg.ParseParams(&target, ×tamp); err != nil { + return nil, err + } + if !strings.HasPrefix(timestamp, "timestamp=") { + return nil, nil + } + timestamp = strings.TrimPrefix(timestamp, "timestamp=") + t, ok := parseTimestamp(timestamp) + if !ok { + return nil, nil + } + return ReadEvent{ + Target: target, + Timestamp: t, + }, nil case "BOUNCER": if len(msg.Params) < 3 { break diff --git a/ui/buffers.go b/ui/buffers.go index 4cc0b45..4c40e19 100644 --- a/ui/buffers.go +++ b/ui/buffers.go @@ -182,6 +182,7 @@ type buffer struct { title string highlights int unread bool + read time.Time lines []Line topic string @@ -389,6 +390,39 @@ func (bs *BufferList) SetTopic(netID, title string, topic string) { b.topic = topic } +func (bs *BufferList) SetRead(netID, title string, timestamp time.Time) { + idx := bs.idx(netID, title) + if idx < 0 { + return + } + b := &bs.list[idx] + if len(b.lines) > 0 && !b.lines[len(b.lines)-1].At.After(timestamp) { + b.highlights = 0 + b.unread = false + } + if b.read.Before(timestamp) { + b.read = timestamp + } +} + +func (bs *BufferList) UpdateRead() (netID, title string, timestamp time.Time) { + b := &bs.list[bs.current] + var line *Line + y := 0 + for i := len(b.lines) - 1; 0 <= i; i-- { + line = &b.lines[i] + if y >= b.scrollAmt { + break + } + y += len(line.NewLines(bs.tlInnerWidth)) + 1 + } + if line != nil && line.At.After(b.read) { + b.read = line.At + return b.netID, b.title, b.read + } + return "", "", time.Time{} +} + func (bs *BufferList) Current() (netID, title string) { b := &bs.list[bs.current] return b.netID, b.title @@ -3,6 +3,7 @@ package ui import ( "strings" "sync/atomic" + "time" "git.sr.ht/~taiite/senpai/irc" @@ -236,6 +237,14 @@ func (ui *UI) SetTopic(netID, buffer string, topic string) { ui.bs.SetTopic(netID, buffer, topic) } +func (ui *UI) SetRead(netID, buffer string, timestamp time.Time) { + ui.bs.SetRead(netID, buffer, timestamp) +} + +func (ui *UI) UpdateRead() (netID, buffer string, timestamp time.Time) { + return ui.bs.UpdateRead() +} + func (ui *UI) SetStatus(status string) { ui.status = status } |