Make notifications paginated

This commit is contained in:
Alessio 2024-11-01 23:23:03 -07:00
parent 90f453c207
commit b0cf2076e2
4 changed files with 73 additions and 9 deletions

View File

@ -2,10 +2,27 @@ package webserver
import ( import (
"net/http" "net/http"
"strconv"
) )
func (app *Application) Notifications(w http.ResponseWriter, r *http.Request) { 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
}
}
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) app.buffered_render_page(w, "tpl/notifications.tpl", PageGlobalData{TweetTrove: feed.TweetTrove}, feed)
} }
}

View File

@ -906,4 +906,14 @@ func TestNotifications(t *testing.T) {
root, err := html.Parse(resp.Body) root, err := html.Parse(resp.Body)
require.NoError(err) require.NoError(err)
assert.Len(cascadia.QueryAll(root, selector(".notification")), 6) 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)
} }

View File

@ -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 // Get the notifications
var notifications []Notification var notifications []Notification
err := p.DB.Select(&notifications, err := p.DB.Select(&notifications,
`select id, type, sent_at, sort_index, user_id, ifnull(action_user_id, 0) action_user_id, `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 ifnull(action_tweet_id, 0) action_tweet_id, ifnull(action_retweet_id, 0) action_retweet_id, has_detail, last_scraped_at
from notifications from notifications
where sort_index < ? or ? where (sort_index < ? or ?)
and user_id = ? and user_id = ?
order by sort_index desc order by sort_index desc
`, cursor, cursor == 0, u_id) limit ?
`, cursor, cursor == 0, u_id, count)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -510,8 +511,18 @@ func (p Profile) GetNotificationsForUser(u_id UserID, cursor int64) Feed {
// TODO: proper user id // TODO: proper user id
p.fill_content(&ret.TweetTrove, UserID(0)) p.fill_content(&ret.TweetTrove, UserID(0))
// TODO: // Set the bottom cursor value
// ret.CursorBottom = ?? 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 return ret
} }

View File

@ -325,7 +325,7 @@ func TestNotificationsFeed(t *testing.T) {
profile, err := persistence.LoadProfile("../../sample_data/profile") profile, err := persistence.LoadProfile("../../sample_data/profile")
require.NoError(err) 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.Notifications, 6)
assert.Len(feed.TweetTrove.Tweets, 3) assert.Len(feed.TweetTrove.Tweets, 3)
assert.Len(feed.TweetTrove.Retweets, 1) 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[4].NotificationID, NotificationID("FKncQJGVgAQAAAABSQ3bEaTgXL-G8wObqVY"))
assert.Equal(feed.Items[5].NotificationID, NotificationID("FKncQJGVgAQAAAABSQ3bEaTgXL8f40e77r4")) assert.Equal(feed.Items[5].NotificationID, NotificationID("FKncQJGVgAQAAAABSQ3bEaTgXL8f40e77r4"))
assert.Equal(feed.Items[5].TweetID, TweetID(1826778617705115868)) 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)
} }