diff --git a/internal/webserver/handler_notifications.go b/internal/webserver/handler_notifications.go index b36e7cf..32f45a2 100644 --- a/internal/webserver/handler_notifications.go +++ b/internal/webserver/handler_notifications.go @@ -2,10 +2,27 @@ package webserver import ( "net/http" + "strconv" ) func (app *Application) Notifications(w http.ResponseWriter, r *http.Request) { - feed := app.Profile.GetNotificationsForUser(app.ActiveUser.ID, 0) + cursor_val := 0 + cursor_param := r.URL.Query().Get("cursor") + if cursor_param != "" { + var err error + cursor_val, err = strconv.Atoi(cursor_param) + if err != nil { + app.error_400_with_message(w, r, "invalid cursor (must be a number)") + return + } + } - app.buffered_render_page(w, "tpl/notifications.tpl", PageGlobalData{TweetTrove: feed.TweetTrove}, feed) + feed := app.Profile.GetNotificationsForUser(app.ActiveUser.ID, int64(cursor_val), 50) // TODO: parameterizable + + if is_htmx(r) && cursor_val != 0 { + // It's a Show More request + app.buffered_render_htmx(w, "timeline", PageGlobalData{TweetTrove: feed.TweetTrove}, feed) + } else { + app.buffered_render_page(w, "tpl/notifications.tpl", PageGlobalData{TweetTrove: feed.TweetTrove}, feed) + } } diff --git a/internal/webserver/server_test.go b/internal/webserver/server_test.go index f86f6e1..efc17e6 100644 --- a/internal/webserver/server_test.go +++ b/internal/webserver/server_test.go @@ -906,4 +906,14 @@ func TestNotifications(t *testing.T) { root, err := html.Parse(resp.Body) require.NoError(err) assert.Len(cascadia.QueryAll(root, selector(".notification")), 6) + + // Show more + recorder = httptest.NewRecorder() + req = httptest.NewRequest("GET", "/notifications?cursor=1726604756351", 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(".notification")), 5) } diff --git a/pkg/persistence/compound_queries.go b/pkg/persistence/compound_queries.go index e9106ae..aa9c90d 100644 --- a/pkg/persistence/compound_queries.go +++ b/pkg/persistence/compound_queries.go @@ -402,17 +402,18 @@ func NewFeed() Feed { } } -func (p Profile) GetNotificationsForUser(u_id UserID, cursor int64) Feed { +func (p Profile) GetNotificationsForUser(u_id UserID, cursor int64, count int64) Feed { // Get the notifications var notifications []Notification err := p.DB.Select(¬ifications, `select id, type, sent_at, sort_index, user_id, ifnull(action_user_id, 0) action_user_id, ifnull(action_tweet_id, 0) action_tweet_id, ifnull(action_retweet_id, 0) action_retweet_id, has_detail, last_scraped_at from notifications - where sort_index < ? or ? + where (sort_index < ? or ?) and user_id = ? order by sort_index desc - `, cursor, cursor == 0, u_id) + limit ? + `, cursor, cursor == 0, u_id, count) if err != nil { panic(err) } @@ -510,8 +511,18 @@ func (p Profile) GetNotificationsForUser(u_id UserID, cursor int64) Feed { // TODO: proper user id p.fill_content(&ret.TweetTrove, UserID(0)) - // TODO: - // ret.CursorBottom = ?? - + // Set the bottom cursor value + ret.CursorBottom = Cursor{} + if len(ret.Items) < int(count) { + ret.CursorBottom.CursorPosition = CURSOR_END + } else { + ret.CursorBottom.CursorPosition = CURSOR_MIDDLE + last_item := ret.Items[len(ret.Items)-1] + last_notif, is_ok := ret.Notifications[last_item.NotificationID] + if !is_ok { + panic("last item isn't a notification???") + } + ret.CursorBottom.CursorValue = int(last_notif.SortIndex) // TODO: CursorValue should be int64 + } return ret } diff --git a/pkg/persistence/compound_queries_test.go b/pkg/persistence/compound_queries_test.go index 55c1ab9..291ca47 100644 --- a/pkg/persistence/compound_queries_test.go +++ b/pkg/persistence/compound_queries_test.go @@ -325,7 +325,7 @@ func TestNotificationsFeed(t *testing.T) { profile, err := persistence.LoadProfile("../../sample_data/profile") require.NoError(err) - feed := profile.GetNotificationsForUser(UserID(1488963321701171204), 12345678912345) + feed := profile.GetNotificationsForUser(UserID(1488963321701171204), 0, 6) assert.Len(feed.TweetTrove.Notifications, 6) assert.Len(feed.TweetTrove.Tweets, 3) assert.Len(feed.TweetTrove.Retweets, 1) @@ -352,4 +352,30 @@ func TestNotificationsFeed(t *testing.T) { assert.Equal(feed.Items[4].NotificationID, NotificationID("FKncQJGVgAQAAAABSQ3bEaTgXL-G8wObqVY")) assert.Equal(feed.Items[5].NotificationID, NotificationID("FKncQJGVgAQAAAABSQ3bEaTgXL8f40e77r4")) assert.Equal(feed.Items[5].TweetID, TweetID(1826778617705115868)) + + assert.Equal(feed.CursorBottom.CursorPosition, persistence.CURSOR_MIDDLE) + assert.Equal(feed.CursorBottom.CursorValue, 1723494244885) + + // Paginated version + // ----------------- + + // Limit 3, after sort_index of the 1st one above + feed = profile.GetNotificationsForUser(UserID(1488963321701171204), 1726604756351, 3) + assert.Len(feed.TweetTrove.Notifications, 3) + + assert.Len(feed.Items, 3) + assert.Equal(feed.Items[0].NotificationID, NotificationID("FDzeDIfVUAIAAvsBiJONcqYgiLgXOolO9t0")) + assert.Equal(feed.Items[1].NotificationID, NotificationID("FKncQJGVgAQAAAABSQ3bEaTgXL8VBxefepo")) + assert.Equal(feed.Items[2].NotificationID, NotificationID("FKncQJGVgAQAAAABSQ3bEaTgXL_S11Ev36g")) + + assert.Equal(feed.CursorBottom.CursorPosition, persistence.CURSOR_MIDDLE) + assert.Equal(feed.CursorBottom.CursorValue, 1724251072880) + + // At end of feed + // -------------- + + // cursor = last notification's sort index + feed = profile.GetNotificationsForUser(UserID(1488963321701171204), 1723494244885, 3) + assert.Len(feed.Items, 0) + assert.Equal(feed.CursorBottom.CursorPosition, persistence.CURSOR_END) }