Split Timeline (Home page) into 2 feeds: offline feed and logged-in user follows feed
This commit is contained in:
parent
7000c1d8f2
commit
c19d36d053
@ -3,11 +3,18 @@ package webserver
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"gitlab.com/offline-twitter/twitter_offline_engine/pkg/persistence"
|
"gitlab.com/offline-twitter/twitter_offline_engine/pkg/persistence"
|
||||||
|
"gitlab.com/offline-twitter/twitter_offline_engine/pkg/scraper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (app *Application) Timeline(w http.ResponseWriter, r *http.Request) {
|
type TimelineData struct {
|
||||||
|
persistence.Feed
|
||||||
|
ActiveTab string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *Application) OfflineTimeline(w http.ResponseWriter, r *http.Request) {
|
||||||
app.traceLog.Printf("'Timeline' handler (path: %q)", r.URL.Path)
|
app.traceLog.Printf("'Timeline' handler (path: %q)", r.URL.Path)
|
||||||
|
|
||||||
c := persistence.NewTimelineCursor()
|
c := persistence.NewTimelineCursor()
|
||||||
@ -26,6 +33,56 @@ func (app *Application) Timeline(w http.ResponseWriter, r *http.Request) {
|
|||||||
// It's a Show More request
|
// It's a Show More request
|
||||||
app.buffered_render_htmx(w, "timeline", PageGlobalData{TweetTrove: feed.TweetTrove}, feed)
|
app.buffered_render_htmx(w, "timeline", PageGlobalData{TweetTrove: feed.TweetTrove}, feed)
|
||||||
} else {
|
} else {
|
||||||
app.buffered_render_page(w, "tpl/offline_timeline.tpl", PageGlobalData{TweetTrove: feed.TweetTrove}, feed)
|
app.buffered_render_page(
|
||||||
|
w,
|
||||||
|
"tpl/offline_timeline.tpl",
|
||||||
|
PageGlobalData{TweetTrove: feed.TweetTrove},
|
||||||
|
TimelineData{Feed: feed, ActiveTab: "Offline"},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *Application) Timeline(w http.ResponseWriter, r *http.Request) {
|
||||||
|
app.traceLog.Printf("'Timeline' handler (path: %q)", r.URL.Path)
|
||||||
|
|
||||||
|
parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
|
||||||
|
if len(parts) > 1 && parts[1] == "offline" {
|
||||||
|
app.OfflineTimeline(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c := persistence.Cursor{
|
||||||
|
Keywords: []string{},
|
||||||
|
ToUserHandles: []scraper.UserHandle{},
|
||||||
|
SinceTimestamp: scraper.TimestampFromUnix(0),
|
||||||
|
UntilTimestamp: scraper.TimestampFromUnix(0),
|
||||||
|
CursorPosition: persistence.CURSOR_START,
|
||||||
|
CursorValue: 0,
|
||||||
|
SortOrder: persistence.SORT_ORDER_NEWEST,
|
||||||
|
PageSize: 50,
|
||||||
|
|
||||||
|
FollowedByUserHandle: app.ActiveUser.Handle,
|
||||||
|
}
|
||||||
|
err := parse_cursor_value(&c, r)
|
||||||
|
if err != nil {
|
||||||
|
app.error_400_with_message(w, "invalid cursor (must be a number)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
feed, err := app.Profile.NextPage(c, app.ActiveUser.ID)
|
||||||
|
if err != nil && !errors.Is(err, persistence.ErrEndOfFeed) {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Header.Get("HX-Request") == "true" && c.CursorPosition == persistence.CURSOR_MIDDLE {
|
||||||
|
// It's a Show More request
|
||||||
|
app.buffered_render_htmx(w, "timeline", PageGlobalData{TweetTrove: feed.TweetTrove}, feed)
|
||||||
|
} else {
|
||||||
|
app.buffered_render_page(
|
||||||
|
w,
|
||||||
|
"tpl/offline_timeline.tpl",
|
||||||
|
PageGlobalData{TweetTrove: feed.TweetTrove},
|
||||||
|
TimelineData{Feed: feed, ActiveTab: "User feed"},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,7 +215,7 @@ func TestTimeline(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
resp := do_request(httptest.NewRequest("GET", "/timeline", nil))
|
resp := do_request(httptest.NewRequest("GET", "/timeline/offline", nil))
|
||||||
require.Equal(resp.StatusCode, 200)
|
require.Equal(resp.StatusCode, 200)
|
||||||
|
|
||||||
root, err := html.Parse(resp.Body)
|
root, err := html.Parse(resp.Body)
|
||||||
@ -231,7 +231,7 @@ func TestTimelineWithCursor(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
resp := do_request(httptest.NewRequest("GET", "/timeline?cursor=1631935701000", nil))
|
resp := do_request(httptest.NewRequest("GET", "/timeline/offline?cursor=1631935701000", nil))
|
||||||
require.Equal(resp.StatusCode, 200)
|
require.Equal(resp.StatusCode, 200)
|
||||||
|
|
||||||
root, err := html.Parse(resp.Body)
|
root, err := html.Parse(resp.Body)
|
||||||
@ -247,10 +247,34 @@ func TestTimelineWithCursorBadNumber(t *testing.T) {
|
|||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
// With a cursor but it sucks
|
// With a cursor but it sucks
|
||||||
resp := do_request(httptest.NewRequest("GET", "/timeline?cursor=asdf", nil))
|
resp := do_request(httptest.NewRequest("GET", "/timeline/offline?cursor=asdf", nil))
|
||||||
require.Equal(resp.StatusCode, 400)
|
require.Equal(resp.StatusCode, 400)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUserFeedTimeline(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 list
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
app.ServeHTTP(recorder, httptest.NewRequest("GET", "/timeline", nil))
|
||||||
|
resp := recorder.Result()
|
||||||
|
require.Equal(resp.StatusCode, 200)
|
||||||
|
|
||||||
|
root, err := html.Parse(resp.Body)
|
||||||
|
require.NoError(err)
|
||||||
|
title_node := cascadia.Query(root, selector("title"))
|
||||||
|
assert.Equal(title_node.FirstChild.Data, "Timeline | Offline Twitter")
|
||||||
|
|
||||||
|
tweet_nodes := cascadia.QueryAll(root, selector(".timeline > .tweet"))
|
||||||
|
assert.Len(tweet_nodes, 1)
|
||||||
|
}
|
||||||
|
|
||||||
// Search page
|
// Search page
|
||||||
// -----------
|
// -----------
|
||||||
|
|
||||||
|
@ -1,7 +1,18 @@
|
|||||||
{{define "title"}}Timeline{{end}}
|
{{define "title"}}Timeline{{end}}
|
||||||
|
|
||||||
{{define "main"}}
|
{{define "main"}}
|
||||||
|
<div class="timeline-header">
|
||||||
|
<div class="row tabs-container">
|
||||||
|
<a class="tab unstyled-link {{if (eq .ActiveTab "User feed")}}active-tab{{end}}" href="/timeline">
|
||||||
|
<span class="tab-inner">User feed</span>
|
||||||
|
</a>
|
||||||
|
<a class="tab unstyled-link {{if (eq .ActiveTab "Offline")}}active-tab{{end}}" href="/timeline/offline">
|
||||||
|
<span class="tab-inner">Offline timeline</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="timeline">
|
<div class="timeline">
|
||||||
{{template "timeline" .}}
|
{{template "timeline" .Feed}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -135,6 +135,7 @@ type Cursor struct {
|
|||||||
ToUserHandles []scraper.UserHandle // In reply to these users
|
ToUserHandles []scraper.UserHandle // In reply to these users
|
||||||
LikedByUserHandle scraper.UserHandle // Liked by this user
|
LikedByUserHandle scraper.UserHandle // Liked by this user
|
||||||
ListID scraper.ListID // Either tweeted or retweeted by users from this List
|
ListID scraper.ListID // Either tweeted or retweeted by users from this List
|
||||||
|
FollowedByUserHandle scraper.UserHandle // Either tweeted or retweeted by users followed by this user
|
||||||
SinceTimestamp scraper.Timestamp
|
SinceTimestamp scraper.Timestamp
|
||||||
UntilTimestamp scraper.Timestamp
|
UntilTimestamp scraper.Timestamp
|
||||||
FilterLinks Filter
|
FilterLinks Filter
|
||||||
@ -395,6 +396,11 @@ func (p Profile) NextPage(c Cursor, current_user_id scraper.UserID) (Feed, error
|
|||||||
where_clauses = append(where_clauses, "by_user_id in (select user_id from list_users where list_id = ?)")
|
where_clauses = append(where_clauses, "by_user_id in (select user_id from list_users where list_id = ?)")
|
||||||
bind_values = append(bind_values, c.ListID)
|
bind_values = append(bind_values, c.ListID)
|
||||||
}
|
}
|
||||||
|
if c.FollowedByUserHandle != "" {
|
||||||
|
where_clauses = append(where_clauses,
|
||||||
|
"by_user_id in (select followee_id from follows where follower_id = (select id from users where handle like ?))")
|
||||||
|
bind_values = append(bind_values, c.FollowedByUserHandle)
|
||||||
|
}
|
||||||
|
|
||||||
// Since and until timestamps
|
// Since and until timestamps
|
||||||
if c.SinceTimestamp.Unix() != 0 {
|
if c.SinceTimestamp.Unix() != 0 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user