commit c527c58

delthas  ·  2024-07-03 01:35:07 +0000 UTC
parent 2b6e0a2
Fetch 2 pages of history of the foreground buffer

This makes scrolling the backlog much smoother, as there are always
some messages waiting above the cursor when scrolling. Similarly to
e.g. an Android RecyclerView.
3 files changed,  +31, -10
M app.go
M app.go
+23, -5
 1@@ -41,6 +41,8 @@ type bound struct {
 2 
 3 	firstMessage string
 4 	lastMessage  string
 5+
 6+	complete bool
 7 }
 8 
 9 // Compare returns 0 if line is within bounds, -1 if before, 1 if after.
10@@ -267,6 +269,7 @@ func (app *App) eventLoop() {
11 					s.ReadSet(buffer, timestamp)
12 				}
13 			}
14+			app.maybeRequestHistory()
15 			app.setStatus()
16 			app.updatePrompt()
17 			app.setBufferNumbers()
18@@ -523,7 +526,6 @@ func (app *App) handleMouseEvent(ev vaxis.Mouse) {
19 				app.win.ScrollMemberUpBy(4)
20 			} else {
21 				app.win.ScrollUpBy(4)
22-				app.requestHistory()
23 			}
24 		}
25 		if ev.Button == vaxis.MouseWheelDown {
26@@ -686,7 +688,6 @@ func (app *App) handleKeyEvent(ev vaxis.Key) {
27 		app.win.Resize()
28 	} else if keyMatches(ev, 'u', vaxis.ModCtrl) || keyMatches(ev, vaxis.KeyPgUp, 0) {
29 		app.win.ScrollUp()
30-		app.requestHistory()
31 	} else if keyMatches(ev, 'd', vaxis.ModCtrl) || keyMatches(ev, vaxis.KeyPgDown, 0) {
32 		app.win.ScrollDown()
33 	} else if keyMatches(ev, 'n', vaxis.ModCtrl) {
34@@ -859,18 +860,22 @@ func (app *App) handleLinkEvent(ev *events.EventClickLink) {
35 	}()
36 }
37 
38-// requestHistory is a wrapper around irc.Session.RequestHistory to only request
39+// maybeRequestHistory is a wrapper around irc.Session.RequestHistory to only request
40 // history when needed.
41-func (app *App) requestHistory() {
42+func (app *App) maybeRequestHistory() {
43 	if app.win.HasOverlay() {
44 		return
45 	}
46 	netID, buffer := app.win.CurrentBuffer()
47+	if app.messageBounds[boundKey{netID, buffer}].complete {
48+		return
49+	}
50 	s := app.sessions[netID]
51 	if s == nil {
52 		return
53 	}
54-	if app.win.IsAtTop() && buffer != "" {
55+	_, h := app.win.Size()
56+	if l := app.win.LinesAboveOffset(); l < h*2 && buffer != "" {
57 		if bound, ok := app.messageBounds[boundKey{netID, buffer}]; ok {
58 			s.NewHistoryRequest(buffer).
59 				WithLimit(200).
60@@ -1184,9 +1189,22 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
61 			}
62 		}
63 		app.win.AddLines(netID, ev.Target, linesBefore, linesAfter)
64+
65 		if !boundsNew.IsZero() {
66 			app.messageBounds[boundKey{netID, ev.Target}] = boundsNew
67 		}
68+		if len(ev.Messages) < 10 {
69+			// We're getting a non-full page: mark as complete to avoid indefinitely fetching the history.
70+			// This should ideally be equal to the CHATHISTORY LIMIT, but it can be non advertised, or
71+			// a full page could sometimes be less than a limit (because it could be filtered).
72+			// It is also not zero, because bounds are inclusive, and not one, because we truncate based on
73+			// the second of the message (because some bouncers have a second-level resolution).
74+			// Be safe and pick 10 messages: less messages means that this was not a full page and we are done
75+			// with fetching the backlog.
76+			b := app.messageBounds[boundKey{netID, ev.Target}]
77+			b.complete = true
78+			app.messageBounds[boundKey{netID, ev.Target}] = b
79+		}
80 	case irc.SearchEvent:
81 		app.win.OpenOverlay("Press Escape to close the search results")
82 		lines := make([]ui.Line, 0, len(ev.Messages))
+6, -3
 1@@ -445,7 +445,7 @@ func (bs *BufferList) AddLine(netID, title string, line Line) {
 2 	n := len(b.lines)
 3 	line.At = line.At.UTC()
 4 
 5-	if !line.Mergeable && current.openedOnce {
 6+	if !line.Mergeable && b.openedOnce {
 7 		line.Body = line.Body.ParseURLs()
 8 	}
 9 
10@@ -649,9 +649,12 @@ func (bs *BufferList) ScrollDownHighlight() bool {
11 	return b.scrollAmt != 0
12 }
13 
14-func (bs *BufferList) IsAtTop() bool {
15+// LinesAboveOffset returns a rough approximate of the number of lines
16+// above the offset (that is, starting from the bottom of the screen,
17+// up to the first line).
18+func (bs *BufferList) LinesAboveOffset() int {
19 	b := bs.cur()
20-	return b.isAtTop
21+	return len(b.lines) - b.scrollAmt
22 }
23 
24 func (bs *BufferList) at(netID, title string) (int, *buffer) {
+2, -2
 1@@ -326,8 +326,8 @@ func (ui *UI) ScrollMemberDownBy(n int) {
 2 	ui.memberOffset += n
 3 }
 4 
 5-func (ui *UI) IsAtTop() bool {
 6-	return ui.bs.IsAtTop()
 7+func (ui *UI) LinesAboveOffset() int {
 8+	return ui.bs.LinesAboveOffset()
 9 }
10 
11 func (ui *UI) OpenOverlay(hint string) {