From af3e49646832563b35baa74e53b3aa730042cf5f Mon Sep 17 00:00:00 2001 From: Alessio Date: Sat, 4 May 2024 15:44:17 -0700 Subject: [PATCH] Add pagination to DM conversation and scrolling back using 'show more' button --- internal/webserver/handler_messages.go | 18 +++++++++++++-- internal/webserver/server_test.go | 21 ++++++++++++++++++ .../tpl/tweet_page_includes/chat_view.tpl | 22 +++++++++++++++++++ pkg/persistence/dm_queries.go | 7 +++++- 4 files changed, 65 insertions(+), 3 deletions(-) diff --git a/internal/webserver/handler_messages.go b/internal/webserver/handler_messages.go index 1ae85f5..4211b76 100644 --- a/internal/webserver/handler_messages.go +++ b/internal/webserver/handler_messages.go @@ -57,10 +57,18 @@ func (app *Application) message_detail(w http.ResponseWriter, r *http.Request) { if r.URL.Query().Get("scroll_bottom") != "0" { chat_view_data.ScrollBottom = true } - chat_contents := app.Profile.GetChatRoomContents(room_id, chat_view_data.LatestPollingTimestamp) - chat_view_data.MergeWith(chat_contents.DMTrove) + + c := persistence.NewConversationCursor(room_id) + c.SinceTimestamp = scraper.TimestampFromUnixMilli(int64(chat_view_data.LatestPollingTimestamp)) + if cursor_value := r.URL.Query().Get("cursor"); cursor_value != "" { + until_time, err := strconv.Atoi(cursor_value) + panic_if(err) // TODO: 400 not 500 + c.UntilTimestamp = scraper.TimestampFromUnixMilli(int64(until_time)) + } + chat_contents := app.Profile.GetChatRoomMessagesByCursor(c) chat_view_data.DMChatView.MergeWith(chat_contents.DMTrove) chat_view_data.MessageIDs = chat_contents.MessageIDs + chat_view_data.Cursor = chat_contents.Cursor if len(chat_view_data.MessageIDs) > 0 { last_message_id := chat_view_data.MessageIDs[len(chat_view_data.MessageIDs)-1] chat_view_data.LatestPollingTimestamp = int(chat_view_data.Messages[last_message_id].SentAt.UnixMilli()) @@ -72,6 +80,12 @@ func (app *Application) message_detail(w http.ResponseWriter, r *http.Request) { app.buffered_render_htmx(w, "messages-with-poller", global_data, chat_view_data) return } + + // Scrolling-back should add new messages to the top of the page + if r.URL.Query().Has("cursor") { + app.buffered_render_htmx(w, "messages-top", global_data, chat_view_data) + return + } } app.buffered_render_page(w, "tpl/messages.tpl", global_data, chat_view_data) diff --git a/internal/webserver/server_test.go b/internal/webserver/server_test.go index ec573b3..674425d 100644 --- a/internal/webserver/server_test.go +++ b/internal/webserver/server_test.go @@ -821,3 +821,24 @@ func TestMessagesRoomPollForUpdatesEmptyResult(t *testing.T) { Val: "/messages/1488963321701171204-1178839081222115328?poll&latest_timestamp=1686025129144&scroll_bottom=1", }) } + +// Scroll back in the messages +func TestMessagesPaginate(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + // Boilerplate for setting an active user + app := webserver.NewApp(profile) + app.IsScrapingDisabled = true + app.ActiveUser = scraper.User{ID: 1488963321701171204, Handle: "Offline_Twatter"} // Simulate a login + + // Chat detail + recorder := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/messages/1488963321701171204-1178839081222115328?cursor=1686025129142", nil) + req.Header.Set("HX-Request", "true") + app.ServeHTTP(recorder, req) + resp := recorder.Result() + root, err := html.Parse(resp.Body) + require.NoError(err) + assert.Len(cascadia.QueryAll(root, selector(".dm-message")), 2) +} diff --git a/internal/webserver/tpl/tweet_page_includes/chat_view.tpl b/internal/webserver/tpl/tweet_page_includes/chat_view.tpl index 04041c1..939a22c 100644 --- a/internal/webserver/tpl/tweet_page_includes/chat_view.tpl +++ b/internal/webserver/tpl/tweet_page_includes/chat_view.tpl @@ -106,6 +106,7 @@ {{end}}
{{if .ActiveRoomID}} + {{template "conversation-top" .}} {{template "messages-with-poller" .}} {{end}}
@@ -190,3 +191,24 @@ {{end}} > {{end}} + +{{define "conversation-top"}} +
+ {{if .Cursor.CursorPosition.IsEnd}} + +
Beginning of conversation
+ {{else}} + Show more + {{end}} +
+{{end}} + +{{/* convenience template for htmx requests */}} +{{define "messages-top"}} + {{template "conversation-top" .}} + {{template "messages" .}} +{{end}} diff --git a/pkg/persistence/dm_queries.go b/pkg/persistence/dm_queries.go index 45a8073..bc2094e 100644 --- a/pkg/persistence/dm_queries.go +++ b/pkg/persistence/dm_queries.go @@ -264,9 +264,14 @@ func (p Profile) GetChatRoomsPreview(id UserID) DMChatView { } // Get chat room detail, including participants and messages +// TODO: get rid of this function (behavior has been moved to GetChatRoomMessagesByCursor) func (p Profile) GetChatRoomContents(id DMChatRoomID, latest_timestamp int) DMChatView { c := NewConversationCursor(id) c.SinceTimestamp = TimestampFromUnixMilli(int64(latest_timestamp)) + return p.GetChatRoomMessagesByCursor(c) +} + +func (p Profile) GetChatRoomMessagesByCursor(c DMCursor) DMChatView { ret := p.NextDMPage(c) var room DMChatRoom @@ -274,7 +279,7 @@ func (p Profile) GetChatRoomContents(id DMChatRoomID, latest_timestamp int) DMCh select `+CHAT_ROOMS_ALL_SQL_FIELDS+` from chat_rooms where id = ? - `, id) + `, c.ConversationId) if err != nil { panic(err) }