summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordelthas <delthas@dille.cc>2022-02-10 15:44:05 +0100
committerdelthas <delthas@dille.cc>2022-02-11 12:18:45 +0100
commitb46a755bfc2afbce7276a2ce075d956ab7dc5b01 (patch)
treecad716b91edbcf5382ef131defc6728cbc25f8b3
parentRename 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.go10
-rw-r--r--commands.go2
-rw-r--r--irc/events.go5
-rw-r--r--irc/session.go33
-rw-r--r--ui/buffers.go34
-rw-r--r--ui/ui.go9
6 files changed, 93 insertions, 0 deletions
diff --git a/app.go b/app.go
index 5d1c6c3..9fd9b1a 100644
--- a/app.go
+++ b/app.go
@@ -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, &timestamp); 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
diff --git a/ui/ui.go b/ui/ui.go
index 750644c..9f0b13d 100644
--- a/ui/ui.go
+++ b/ui/ui.go
@@ -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
}