diff --git a/internal/webserver/handler_messages.go b/internal/webserver/handler_messages.go index b45674b..5190ce6 100644 --- a/internal/webserver/handler_messages.go +++ b/internal/webserver/handler_messages.go @@ -5,12 +5,16 @@ import ( "io" "net/http" "strings" + "strconv" "gitlab.com/offline-twitter/twitter_offline_engine/pkg/persistence" "gitlab.com/offline-twitter/twitter_offline_engine/pkg/scraper" ) -type MessageData persistence.DMChatView +type MessageData struct { + persistence.DMChatView + LatestPollingTimestamp int +} func (t MessageData) Tweet(id scraper.TweetID) scraper.Tweet { return t.Tweets[id] @@ -38,25 +42,47 @@ func (app *Application) Messages(w http.ResponseWriter, r *http.Request) { parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/") room_id := scraper.DMChatRoomID(parts[0]) - if len(parts) == 2 && parts[1] == "send" { - body, err := io.ReadAll(r.Body) - panic_if(err) - var message_data struct { - Text string `json:"text"` + + if r.URL.Query().Has("scrape") { + // TODO: where is this going to be used? + app.background_dm_polling_scrape() + } + + chat_view_data := MessageData{DMChatView: app.Profile.GetChatRoomsPreview(app.ActiveUser.ID)} // Get message list previews + + if room_id != "" { + // First send a message, if applicable + if len(parts) == 2 && parts[1] == "send" { + body, err := io.ReadAll(r.Body) + panic_if(err) + var message_data struct { + Text string `json:"text"` + } + panic_if(json.Unmarshal(body, &message_data)) + trove := scraper.SendDMMessage(room_id, message_data.Text, 0) + app.Profile.SaveDMTrove(trove, false) + go app.Profile.SaveDMTrove(trove, true) + } + chat_view_data.ActiveRoomID = room_id + chat_view_data.LatestPollingTimestamp = -1 + if latest_timestamp_str := r.URL.Query().Get("latest_timestamp"); latest_timestamp_str != "" { + var err error + chat_view_data.LatestPollingTimestamp, err = strconv.Atoi(latest_timestamp_str) + panic_if(err) + } + chat_contents := app.Profile.GetChatRoomContents(room_id, chat_view_data.LatestPollingTimestamp) + chat_view_data.MergeWith(chat_contents.DMTrove) + chat_view_data.MessageIDs = chat_contents.MessageIDs + 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.Unix()) + } + + if r.URL.Query().Has("poll") { + app.buffered_render_tweet_htmx(w, "messages-with-poller", chat_view_data) + return } - panic_if(json.Unmarshal(body, &message_data)) - trove := scraper.SendDMMessage(room_id, message_data.Text, 0) - app.Profile.SaveDMTrove(trove, false) - go app.Profile.SaveDMTrove(trove, true) } - chat_view := app.Profile.GetChatRoomsPreview(app.ActiveUser.ID) - if strings.Trim(r.URL.Path, "/") != "" { - chat_view.ActiveRoomID = room_id - chat_contents := app.Profile.GetChatRoomContents(room_id) - chat_view.MergeWith(chat_contents.DMTrove) - chat_view.MessageIDs = chat_contents.MessageIDs - } - - app.buffered_render_tweet_page(w, "tpl/messages.tpl", MessageData(chat_view)) + app.buffered_render_tweet_page(w, "tpl/messages.tpl", chat_view_data) } diff --git a/internal/webserver/server_test.go b/internal/webserver/server_test.go index bd8a5a1..a48a718 100644 --- a/internal/webserver/server_test.go +++ b/internal/webserver/server_test.go @@ -608,4 +608,30 @@ func TestMessagesRoom(t *testing.T) { require.NoError(err) assert.Len(cascadia.QueryAll(root, selector(".chat-list .chat")), 2) // Chat list still renders assert.Len(cascadia.QueryAll(root, selector("#chat-view .dm-message-and-reacts-container")), 5) + + // Should have the poller at the bottom + node := cascadia.Query(root, selector("#new-messages-poller")) + assert.NotNil(node) + assert.Contains(node.Attr, html.Attribute{Key: "hx-get", Val: "/messages/1488963321701171204-1178839081222115328?poll&latest_timestamp=1686025129144"}) +} + +// Loading the page since +func TestMessagesRoomPollForUpdates(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?poll&latest_timestamp=1686025129141", 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-and-reacts-container")), 3) } diff --git a/internal/webserver/tpl/tweet_page_includes/chat_view.tpl b/internal/webserver/tpl/tweet_page_includes/chat_view.tpl index cf14862..7fd4169 100644 --- a/internal/webserver/tpl/tweet_page_includes/chat_view.tpl +++ b/internal/webserver/tpl/tweet_page_includes/chat_view.tpl @@ -1,57 +1,69 @@ +{{define "messages-with-poller"}} + {{range .MessageIDs}} + {{$message := (index $.DMTrove.Messages .)}} + {{$user := (user $message.SenderID)}} + {{$is_us := (eq $message.SenderID (active_user).ID)}} +
+ {{end}} + + +{{end}} + {{define "chat-view"}} +