diff --git a/internal/webserver/handler_lists.go b/internal/webserver/handler_lists.go index 5c67351..76ef564 100644 --- a/internal/webserver/handler_lists.go +++ b/internal/webserver/handler_lists.go @@ -18,5 +18,5 @@ func (app *Application) Lists(w http.ResponseWriter, r *http.Request) { where is_followed = 1`) panic_if(err) - app.buffered_render_basic_page(w, "tpl/list.tpl", users) + app.buffered_render_basic_page(w, "tpl/list.tpl", ListData{Title: "Offline Follows", Users: users}) } diff --git a/internal/webserver/handler_tweet_detail.go b/internal/webserver/handler_tweet_detail.go index 21e1c6f..e9bf3dd 100644 --- a/internal/webserver/handler_tweet_detail.go +++ b/internal/webserver/handler_tweet_detail.go @@ -6,6 +6,7 @@ import ( "net/http" "strconv" "strings" + "context" "gitlab.com/offline-twitter/twitter_offline_engine/pkg/persistence" "gitlab.com/offline-twitter/twitter_offline_engine/pkg/scraper" @@ -151,3 +152,19 @@ func (app *Application) TweetDetail(w http.ResponseWriter, r *http.Request) { app.buffered_render_tweet_page(w, "tpl/tweet_detail.tpl", data) } + +type key string + +const TWEET_KEY = key("tweet") + +func add_tweet_to_context(ctx context.Context, tweet scraper.Tweet) context.Context { + return context.WithValue(ctx, TWEET_KEY, tweet) +} + +func get_tweet_from_context(ctx context.Context) scraper.Tweet { + tweet, is_ok := ctx.Value(TWEET_KEY).(scraper.Tweet) + if !is_ok { + panic("Tweet not found in context") + } + return tweet +} diff --git a/internal/webserver/handler_user_feed.go b/internal/webserver/handler_user_feed.go index a4b8c58..492e03d 100644 --- a/internal/webserver/handler_user_feed.go +++ b/internal/webserver/handler_user_feed.go @@ -1,6 +1,7 @@ package webserver import ( + "fmt" "errors" "net/http" "strings" @@ -49,6 +50,15 @@ func (app *Application) UserFeed(w http.ResponseWriter, r *http.Request) { panic_if(app.Profile.DownloadUserContentFor(&user)) } + if len(parts) > 1 && parts[1] == "followers" { + app.UserFollowers(w, r, user) + return + } + if len(parts) > 1 && parts[1] == "followees" { + app.UserFollowees(w, r, user) + return + } + if r.URL.Query().Has("scrape") { if app.IsScrapingDisabled { app.InfoLog.Printf("Would have scraped: %s", r.URL.Path) @@ -118,3 +128,21 @@ func (app *Application) UserFeed(w http.ResponseWriter, r *http.Request) { app.buffered_render_tweet_page(w, "tpl/user_feed.tpl", data) } } + +type ListData struct { + Title string + Users []scraper.User +} + +func (app *Application) UserFollowees(w http.ResponseWriter, r *http.Request, user scraper.User) { + app.buffered_render_basic_page(w, "tpl/list.tpl", ListData{ + Title: fmt.Sprintf("Followed by @%s", user.Handle), + Users: app.Profile.GetFollowees(user.ID), + }) +} +func (app *Application) UserFollowers(w http.ResponseWriter, r *http.Request, user scraper.User) { + app.buffered_render_basic_page(w, "tpl/list.tpl", ListData{ + Title: fmt.Sprintf("Followers of @%s", user.Handle), + Users: app.Profile.GetFollowers(user.ID), + }) +} diff --git a/internal/webserver/response_helpers.go b/internal/webserver/response_helpers.go index b0e6adb..17eec8d 100644 --- a/internal/webserver/response_helpers.go +++ b/internal/webserver/response_helpers.go @@ -2,7 +2,6 @@ package webserver import ( "bytes" - "context" "fmt" "html/template" "io" @@ -249,19 +248,3 @@ func cursor_to_query_params(c persistence.Cursor) string { result.Set("sort-order", c.SortOrder.String()) return result.Encode() } - -type key string - -const TWEET_KEY = key("tweet") - -func add_tweet_to_context(ctx context.Context, tweet scraper.Tweet) context.Context { - return context.WithValue(ctx, TWEET_KEY, tweet) -} - -func get_tweet_from_context(ctx context.Context) scraper.Tweet { - tweet, is_ok := ctx.Value(TWEET_KEY).(scraper.Tweet) - if !is_ok { - panic("Tweet not found in context") - } - return tweet -} diff --git a/internal/webserver/server_test.go b/internal/webserver/server_test.go index 6325eee..a57880f 100644 --- a/internal/webserver/server_test.go +++ b/internal/webserver/server_test.go @@ -79,7 +79,7 @@ func TestUserFeed(t *testing.T) { root, err := html.Parse(resp.Body) require.NoError(err) title_node := cascadia.Query(root, selector("title")) - assert.Equal(title_node.FirstChild.Data, "Offline Twitter | @Cernovich") + assert.Equal(title_node.FirstChild.Data, "@Cernovich | Offline Twitter") tweet_nodes := cascadia.QueryAll(root, selector(".timeline > .tweet")) assert.Len(tweet_nodes, 7) @@ -119,7 +119,7 @@ func TestUserFeedWithCursor(t *testing.T) { root, err := html.Parse(resp.Body) require.NoError(err) title_node := cascadia.Query(root, selector("title")) - assert.Equal(title_node.FirstChild.Data, "Offline Twitter | @Cernovich") + assert.Equal(title_node.FirstChild.Data, "@Cernovich | Offline Twitter") tweet_nodes := cascadia.QueryAll(root, selector(".timeline > .tweet")) assert.Len(tweet_nodes, 2) @@ -181,6 +181,34 @@ func TestUserFeedLikesTab(t *testing.T) { assert.Len(tweets, 4) } +// Followers and followees +// ----------------------- + +func TestUserFollowers(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + resp := do_request(httptest.NewRequest("GET", "/Offline_Twatter/followers", nil)) + require.Equal(resp.StatusCode, 200) + + root, err := html.Parse(resp.Body) + require.NoError(err) + assert.Len(cascadia.QueryAll(root, selector(".users-list-container > .user")), 2) +} + + +func TestUserFollowees(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + resp := do_request(httptest.NewRequest("GET", "/Offline_Twatter/followees", nil)) + require.Equal(resp.StatusCode, 200) + + root, err := html.Parse(resp.Body) + require.NoError(err) + assert.Len(cascadia.QueryAll(root, selector(".users-list-container > .user")), 1) +} + // Timeline page // ------------- @@ -194,7 +222,7 @@ func TestTimeline(t *testing.T) { root, err := html.Parse(resp.Body) require.NoError(err) title_node := cascadia.Query(root, selector("title")) - assert.Equal(title_node.FirstChild.Data, "Offline Twitter | Timeline") + assert.Equal(title_node.FirstChild.Data, "Timeline | Offline Twitter") tweet_nodes := cascadia.QueryAll(root, selector(".timeline > .tweet")) assert.Len(tweet_nodes, 18) @@ -210,7 +238,7 @@ func TestTimelineWithCursor(t *testing.T) { root, err := html.Parse(resp.Body) require.NoError(err) title_node := cascadia.Query(root, selector("title")) - assert.Equal(title_node.FirstChild.Data, "Offline Twitter | Timeline") + assert.Equal(title_node.FirstChild.Data, "Timeline | Offline Twitter") tweet_nodes := cascadia.QueryAll(root, selector(".timeline > .tweet")) assert.Len(tweet_nodes, 10) @@ -245,7 +273,7 @@ func TestSearch(t *testing.T) { root, err := html.Parse(resp.Body) require.NoError(err) title_node := cascadia.Query(root, selector("title")) - assert.Equal(title_node.FirstChild.Data, "Offline Twitter | Search") + assert.Equal(title_node.FirstChild.Data, "Search | Offline Twitter") tweet_nodes := cascadia.QueryAll(root, selector(".timeline > .tweet")) assert.Len(tweet_nodes, 1) diff --git a/internal/webserver/tpl/includes/base.tpl b/internal/webserver/tpl/includes/base.tpl index be7e25f..050d5d9 100644 --- a/internal/webserver/tpl/includes/base.tpl +++ b/internal/webserver/tpl/includes/base.tpl @@ -3,7 +3,7 @@
-