feat: added homepage feed showing followed accounts

This commit is contained in:
2025-11-21 23:03:58 -03:00
parent 62a4347b96
commit 73360e6972
7 changed files with 114 additions and 5 deletions

View File

@@ -37,6 +37,10 @@ function fetchAndParse(url) {
window.onload = function () { window.onload = function () {
const url = window.location.pathname; const url = window.location.pathname;
const isTweet = url.indexOf("/status/") !== -1; const isTweet = url.indexOf("/status/") !== -1;
const isHomepage = url === "/" || url === "";
if (isHomepage) return;
const isIncompleteThread = const isIncompleteThread =
isTweet && document.querySelector(".timeline-item.more-replies") != null; isTweet && document.querySelector(".timeline-item.more-replies") != null;

View File

@@ -6,8 +6,9 @@ from os import getEnv
import jester import jester
import types, config, prefs, formatters, redis_cache, http_pool, auth import types, config, prefs, formatters, redis_cache, http_pool, auth, query
import views/[general, about] import views/[general, about, search, profile]
import karax/[karaxdsl, vdom]
import routes/[ import routes/[
preferences, timeline, status, media, search, rss, list, debug, preferences, timeline, status, media, search, rss, list, debug,
unsupported, embed, resolver, router_utils, follow] unsupported, embed, resolver, router_utils, follow]
@@ -98,7 +99,43 @@ settings:
routes: routes:
get "/": 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": get "/about":
resp renderMain(renderAbout(), request, cfg, themePrefs()) resp renderMain(renderAbout(), request, cfg, themePrefs())

View File

@@ -59,8 +59,11 @@ proc genQueryParam*(query: Query): string =
if i < query.fromUser.high: if i < query.fromUser.high:
param &= "OR " param &= "OR "
if query.fromUser.len > 0 and query.kind in {posts, media}: if query.fromUser.len > 0:
param &= "filter:self_threads OR -filter:replies " 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: if "nativeretweets" notin query.excludes:
param &= "include:nativeretweets " param &= "include:nativeretweets "

34
src/sass/homepage.scss Normal file
View File

@@ -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;
}
}

View File

@@ -7,6 +7,7 @@
@import 'inputs'; @import 'inputs';
@import 'timeline'; @import 'timeline';
@import 'search'; @import 'search';
@import 'homepage';
body { body {
// colors // colors

15
src/views/homepage.nim Normal file
View File

@@ -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")

View File

@@ -50,6 +50,21 @@ proc renderTweetSearch*(results: Timeline; prefs: Prefs; path: string;
renderTimelineTweets(results, prefs, path, pinned) 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 = proc renderUserSearch*(results: Result[User]; prefs: Prefs; path: string): VNode =
buildHtml(tdiv(class="timeline-container")): buildHtml(tdiv(class="timeline-container")):
renderSearchTabs(results.query) renderSearchTabs(results.query)