diff --git a/public/js/infiniteScroll.js b/public/js/infiniteScroll.js index 1f6cde3..2562f51 100644 --- a/public/js/infiniteScroll.js +++ b/public/js/infiniteScroll.js @@ -37,6 +37,10 @@ function fetchAndParse(url) { window.onload = function () { const url = window.location.pathname; const isTweet = url.indexOf("/status/") !== -1; + const isHomepage = url === "/" || url === ""; + + if (isHomepage) return; + const isIncompleteThread = isTweet && document.querySelector(".timeline-item.more-replies") != null; diff --git a/src/nitter.nim b/src/nitter.nim index 24439d3..f1d176f 100644 --- a/src/nitter.nim +++ b/src/nitter.nim @@ -6,8 +6,9 @@ from os import getEnv import jester -import types, config, prefs, formatters, redis_cache, http_pool, auth -import views/[general, about] +import types, config, prefs, formatters, redis_cache, http_pool, auth, query +import views/[general, about, search, profile] +import karax/[karaxdsl, vdom] import routes/[ preferences, timeline, status, media, search, rss, list, debug, unsupported, embed, resolver, router_utils, follow] @@ -98,7 +99,43 @@ settings: routes: get "/": - resp renderMain(renderSearch(), request, cfg, themePrefs()) + let prefs = cookiePrefs() + + if prefs.following.len > 0: + let + cursor = getCursor() + + var homepageQuery = initQuery(params(request)) + if @"f".len == 0: + homepageQuery.kind = tweets + + homepageQuery.fromUser = prefs.following + + let + timeline = await getGraphTweetSearch(homepageQuery, cursor) + html = renderHomepageTimeline(timeline, prefs, getPath()) + + var users: seq[User] + for username in prefs.following: + try: + let user = await getCachedUser(username) + users.add(user) + except: + continue + + let homepageHtml = buildHtml(tdiv(class="homepage-container")): + tdiv(class="following-column"): + tdiv(class="profile-cards"): + for user in users: + let currentPath = if @"f".len > 0: "/?f=" & @"f" else: "/" + renderUserCard(user, prefs, currentPath) + + tdiv(class="timeline"): + html + + resp renderMain(homepageHtml, request, cfg, prefs, "Following") + else: + resp renderMain(renderSearch(), request, cfg, themePrefs()) get "/about": resp renderMain(renderAbout(), request, cfg, themePrefs()) diff --git a/src/query.nim b/src/query.nim index 06e1da2..bf3f00a 100644 --- a/src/query.nim +++ b/src/query.nim @@ -59,8 +59,11 @@ proc genQueryParam*(query: Query): string = if i < query.fromUser.high: param &= "OR " - if query.fromUser.len > 0 and query.kind in {posts, media}: - param &= "filter:self_threads OR -filter:replies " + if query.fromUser.len > 0: + if query.kind in {posts, media}: + param &= "filter:self_threads OR -filter:replies " + elif query.kind == tweets and query.fromUser.len > 1: + param &= "filter:self_threads OR -filter:replies " if "nativeretweets" notin query.excludes: param &= "include:nativeretweets " diff --git a/src/sass/homepage.scss b/src/sass/homepage.scss new file mode 100644 index 0000000..df2d0cd --- /dev/null +++ b/src/sass/homepage.scss @@ -0,0 +1,34 @@ +@import '_variables'; +@import '_mixins'; + +.homepage-container { + display: flex; + gap: 5px; + max-width: 1200px; + margin: 0 auto; +} + +.following-column { + width: 280px; + flex-shrink: 0; +} + +.profile-cards { + display: flex; + flex-direction: column; + gap: 5px; +} + +.homepage-container .timeline-container > .timeline > :first-child { + margin-top: 0; +} + +@media (max-width: 887px) { + .homepage-container { + max-width: 100%; + } + + .following-column { + display:none; + } +} diff --git a/src/sass/index.scss b/src/sass/index.scss index 0f5de15..94e3331 100644 --- a/src/sass/index.scss +++ b/src/sass/index.scss @@ -7,6 +7,7 @@ @import 'inputs'; @import 'timeline'; @import 'search'; +@import 'homepage'; body { // colors diff --git a/src/views/homepage.nim b/src/views/homepage.nim new file mode 100644 index 0000000..ee116bc --- /dev/null +++ b/src/views/homepage.nim @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: AGPL-3.0-only +import karax/[karaxdsl, vdom] + +import profile +import ".."/[types] + +proc renderHomepage*(users: seq[User]; prefs: Prefs; path: string): VNode = + buildHtml(tdiv(class="homepage-container")): + tdiv(class="following-column"): + tdiv(class="profile-cards"): + for user in users: + renderUserCard(user, prefs, path) + + tdiv(class="timeline"): + tdiv(id="timeline-container") diff --git a/src/views/search.nim b/src/views/search.nim index c2d39f5..4fb6d32 100644 --- a/src/views/search.nim +++ b/src/views/search.nim @@ -50,6 +50,21 @@ proc renderTweetSearch*(results: Timeline; prefs: Prefs; path: string; renderTimelineTweets(results, prefs, path, pinned) +proc renderHomepageTabs*(query: Query): VNode = + buildHtml(ul(class="tab")): + li(class=if query.kind == tweets: "tab-item active" else: "tab-item"): + a(href="/?f=tweets"): text "Tweets" + + li(class=if query.kind == replies: "tab-item active" else: "tab-item"): + a(href="/?f=replies"): text "Tweets & Replies" + +proc renderHomepageTimeline*(results: Timeline; prefs: Prefs; path: string): VNode = + let query = results.query + buildHtml(tdiv(class="timeline-container")): + renderHomepageTabs(query) + + renderTimelineTweets(results, prefs, path) + proc renderUserSearch*(results: Result[User]; prefs: Prefs; path: string): VNode = buildHtml(tdiv(class="timeline-container")): renderSearchTabs(results.query)