commit 058532e

Hubert Hirtz  ·  2021-09-01 10:37:37 +0000 UTC
parent 1feb31d
Fetch missed messages on reconnect

just one step closer to proper CHATHISTORY support!! :=D ^v^
4 files changed,  +97, -52
M app.go
M app.go
+78, -10
  1@@ -24,6 +24,42 @@ const (
  2 	ircEvent
  3 )
  4 
  5+type bound struct {
  6+	first time.Time
  7+	last  time.Time
  8+
  9+	firstMessage string
 10+	lastMessage  string
 11+}
 12+
 13+// Compare returns 0 if line is within bounds, -1 if before, 1 if after.
 14+func (b *bound) Compare(line *ui.Line) int {
 15+	if line.At.Before(b.first) {
 16+		return -1
 17+	}
 18+	if line.At.After(b.last) {
 19+		return 1
 20+	}
 21+	if line.At.Equal(b.first) && line.Body.String() != b.firstMessage {
 22+		return -1
 23+	}
 24+	if line.At.Equal(b.last) && line.Body.String() != b.lastMessage {
 25+		return -1
 26+	}
 27+	return 0
 28+}
 29+
 30+// Update updates the bounds to include the given line.
 31+func (b *bound) Update(line *ui.Line) {
 32+	if line.At.Before(b.first) {
 33+		b.first = line.At
 34+		b.firstMessage = line.Body.String()
 35+	} else if line.At.After(b.last) {
 36+		b.last = line.At
 37+		b.lastMessage = line.Body.String()
 38+	}
 39+}
 40+
 41 type event struct {
 42 	src     source
 43 	content interface{}
 44@@ -38,13 +74,15 @@ type App struct {
 45 	cfg        Config
 46 	highlights []string
 47 
 48-	lastQuery string
 49+	lastQuery     string
 50+	messageBounds map[string]bound
 51 }
 52 
 53 func NewApp(cfg Config) (app *App, err error) {
 54 	app = &App{
 55-		cfg:    cfg,
 56-		events: make(chan event, eventChanSize),
 57+		cfg:           cfg,
 58+		events:        make(chan event, eventChanSize),
 59+		messageBounds: map[string]bound{},
 60 	}
 61 
 62 	if cfg.Highlights != nil {
 63@@ -521,10 +559,17 @@ func (app *App) handleIRCEvent(ev interface{}) {
 64 			})
 65 		}
 66 	case irc.SelfJoinEvent:
 67-		i := app.win.AddBuffer(ev.Channel)
 68-		app.s.NewHistoryRequest(ev.Channel).
 69-			WithLimit(200).
 70-			Before(msg.TimeOrNow())
 71+		i, added := app.win.AddBuffer(ev.Channel)
 72+		bounds, ok := app.messageBounds[ev.Channel]
 73+		if added || !ok {
 74+			app.s.NewHistoryRequest(ev.Channel).
 75+				WithLimit(200).
 76+				Before(msg.TimeOrNow())
 77+		} else {
 78+			app.s.NewHistoryRequest(ev.Channel).
 79+				WithLimit(200).
 80+				After(bounds.last)
 81+		}
 82 		if ev.Requested {
 83 			app.win.JumpBufferIndex(i)
 84 		}
 85@@ -604,16 +649,39 @@ func (app *App) handleIRCEvent(ev interface{}) {
 86 		if !app.s.IsChannel(msg.Params[0]) && !app.s.IsMe(ev.User) {
 87 			app.lastQuery = msg.Prefix.Name
 88 		}
 89+		bounds := app.messageBounds[ev.Target]
 90+		bounds.Update(&line)
 91+		app.messageBounds[ev.Target] = bounds
 92 	case irc.HistoryEvent:
 93-		var lines []ui.Line
 94+		var linesBefore []ui.Line
 95+		var linesAfter []ui.Line
 96+		bounds, hasBounds := app.messageBounds[ev.Target]
 97 		for _, m := range ev.Messages {
 98 			switch ev := m.(type) {
 99 			case irc.MessageEvent:
100 				_, line, _ := app.formatMessage(ev)
101-				lines = append(lines, line)
102+				if hasBounds {
103+					c := bounds.Compare(&line)
104+					if c < 0 {
105+						linesBefore = append(linesBefore, line)
106+					} else if c > 0 {
107+						linesAfter = append(linesAfter, line)
108+					}
109+				} else {
110+					linesAfter = append(linesAfter, line)
111+				}
112 			}
113 		}
114-		app.win.AddLines(ev.Target, lines)
115+		app.win.AddLines(ev.Target, linesBefore, linesAfter)
116+		if len(linesBefore) != 0 {
117+			bounds.Update(&linesBefore[0])
118+			bounds.Update(&linesBefore[len(linesBefore)-1])
119+		}
120+		if len(linesAfter) != 0 {
121+			bounds.Update(&linesAfter[0])
122+			bounds.Update(&linesAfter[len(linesAfter)-1])
123+		}
124+		app.messageBounds[ev.Target] = bounds
125 	case irc.ErrorEvent:
126 		if isBlackListed(msg.Command) {
127 			break
+6, -0
 1@@ -429,6 +429,12 @@ func (r *HistoryRequest) doRequest() {
 2 	r.s.out <- NewMessage("CHATHISTORY", args...)
 3 }
 4 
 5+func (r *HistoryRequest) After(t time.Time) {
 6+	r.command = "AFTER"
 7+	r.bounds = []string{formatTimestamp(t)}
 8+	r.doRequest()
 9+}
10+
11 func (r *HistoryRequest) Before(t time.Time) {
12 	r.command = "BEFORE"
13 	r.bounds = []string{formatTimestamp(t)}
+10, -39
 1@@ -235,16 +235,16 @@ func (bs *BufferList) Previous() {
 2 	bs.list[bs.current].unread = false
 3 }
 4 
 5-func (bs *BufferList) Add(title string) int {
 6+func (bs *BufferList) Add(title string) (i int, added bool) {
 7 	lTitle := strings.ToLower(title)
 8 	for i, b := range bs.list {
 9 		if strings.ToLower(b.title) == lTitle {
10-			return i
11+			return i, false
12 		}
13 	}
14 
15 	bs.list = append(bs.list, buffer{title: title})
16-	return len(bs.list) - 1
17+	return len(bs.list) - 1, true
18 }
19 
20 func (bs *BufferList) Remove(title string) (ok bool) {
21@@ -299,49 +299,20 @@ func (bs *BufferList) AddLine(title string, notify NotifyType, line Line) {
22 	}
23 }
24 
25-// "lines" needs to be sorted by their "At" field.
26-func (bs *BufferList) AddLines(title string, lines []Line) {
27+func (bs *BufferList) AddLines(title string, before, after []Line) {
28 	idx := bs.idx(title)
29 	if idx < 0 {
30 		return
31 	}
32 
33 	b := &bs.list[idx]
34-	limit := len(lines)
35-
36-	if 0 < len(b.lines) {
37-		// Compute "limit", the index first line of "lines" that should
38-		// not be added to the buffer.
39-		firstLineTime := b.lines[0].At.Unix()
40-		firstLineBody := b.lines[0].Body
41-		for i, l := range lines {
42-			historyLineTime := l.At.Unix()
43-			if historyLineTime < firstLineTime {
44-				// This line is behind the first line of the
45-				// buffer.
46-				continue
47-			}
48-			if historyLineTime > firstLineTime {
49-				// This line happened after the first line of
50-				// the buffer.
51-				limit = i
52-				break
53-			}
54-			if l.Body.string == firstLineBody.string {
55-				// This line happened at the same millisecond
56-				// as the first line of the buffer, and has the
57-				// same contents. Heuristic: it's the same
58-				// message.
59-				limit = i
60-				break
61-			}
62-		}
63+
64+	if len(before) != 0 {
65+		b.lines = append(before, b.lines...)
66 	}
67-	for i := 0; i < limit; i++ {
68-		lines[i].computeSplitPoints()
69+	if len(after) != 0 {
70+		b.lines = append(b.lines, after...)
71 	}
72-
73-	b.lines = append(lines[:limit], b.lines...)
74 }
75 
76 func (bs *BufferList) Current() (title string) {
77@@ -353,7 +324,7 @@ func (bs *BufferList) CurrentOldestTime() (t *time.Time) {
78 	if 0 < len(ls) {
79 		t = &ls[0].At
80 	}
81-	return
82+	return t
83 }
84 
85 func (bs *BufferList) ScrollUp(n int) {
+3, -3
 1@@ -148,7 +148,7 @@ func (ui *UI) IsAtTop() bool {
 2 	return ui.bs.IsAtTop()
 3 }
 4 
 5-func (ui *UI) AddBuffer(title string) int {
 6+func (ui *UI) AddBuffer(title string) (i int, added bool) {
 7 	return ui.bs.Add(title)
 8 }
 9 
10@@ -161,8 +161,8 @@ func (ui *UI) AddLine(buffer string, notify NotifyType, line Line) {
11 	ui.bs.AddLine(buffer, notify, line)
12 }
13 
14-func (ui *UI) AddLines(buffer string, lines []Line) {
15-	ui.bs.AddLines(buffer, lines)
16+func (ui *UI) AddLines(buffer string, before, after []Line) {
17+	ui.bs.AddLines(buffer, before, after)
18 }
19 
20 func (ui *UI) JumpBuffer(sub string) bool {