From 4ef36c5641fd183b479090725e3bab8c290776dc Mon Sep 17 00:00:00 2001 From: Anirudh Oppiliappan Date: Thu, 28 Aug 2025 10:48:38 +0300 Subject: [PATCH] appview/pages: display home page to logged out users Change-Id: prszwlupsoqznnstnqpotltnnunvkpzv Signed-off-by: Anirudh Oppiliappan --- appview/db/timeline.go | 26 ++- appview/pages/pages.go | 4 + .../timeline/fragments/timeline.html | 116 ++++++++++++ .../timeline/fragments/trending.html | 25 +++ appview/pages/templates/timeline/home.html | 117 ++++++++++++ .../pages/templates/timeline/timeline.html | 174 +----------------- appview/state/router.go | 3 +- appview/state/state.go | 34 +++- 8 files changed, 311 insertions(+), 188 deletions(-) create mode 100644 appview/pages/templates/timeline/fragments/timeline.html create mode 100644 appview/pages/templates/timeline/fragments/trending.html create mode 100644 appview/pages/templates/timeline/home.html diff --git a/appview/db/timeline.go b/appview/db/timeline.go index b2b69dd7..a5907722 100644 --- a/appview/db/timeline.go +++ b/appview/db/timeline.go @@ -20,24 +20,22 @@ type TimelineEvent struct { *FollowStats } -const Limit = 50 - // TODO: this gathers heterogenous events from different sources and aggregates // them in code; if we did this entirely in sql, we could order and limit and paginate easily -func MakeTimeline(e Execer) ([]TimelineEvent, error) { +func MakeTimeline(e Execer, limit int) ([]TimelineEvent, error) { var events []TimelineEvent - repos, err := getTimelineRepos(e) + repos, err := getTimelineRepos(e, limit) if err != nil { return nil, err } - stars, err := getTimelineStars(e) + stars, err := getTimelineStars(e, limit) if err != nil { return nil, err } - follows, err := getTimelineFollows(e) + follows, err := getTimelineFollows(e, limit) if err != nil { return nil, err } @@ -51,15 +49,15 @@ func MakeTimeline(e Execer) ([]TimelineEvent, error) { }) // Limit the slice to 100 events - if len(events) > Limit { - events = events[:Limit] + if len(events) > limit { + events = events[:limit] } return events, nil } -func getTimelineRepos(e Execer) ([]TimelineEvent, error) { - repos, err := GetRepos(e, Limit) +func getTimelineRepos(e Execer, limit int) ([]TimelineEvent, error) { + repos, err := GetRepos(e, limit) if err != nil { return nil, err } @@ -104,8 +102,8 @@ func getTimelineRepos(e Execer) ([]TimelineEvent, error) { return events, nil } -func getTimelineStars(e Execer) ([]TimelineEvent, error) { - stars, err := GetStars(e, Limit) +func getTimelineStars(e Execer, limit int) ([]TimelineEvent, error) { + stars, err := GetStars(e, limit) if err != nil { return nil, err } @@ -131,8 +129,8 @@ func getTimelineStars(e Execer) ([]TimelineEvent, error) { return events, nil } -func getTimelineFollows(e Execer) ([]TimelineEvent, error) { - follows, err := GetFollows(e, Limit) +func getTimelineFollows(e Execer, limit int) ([]TimelineEvent, error) { + follows, err := GetFollows(e, limit) if err != nil { return nil, err } diff --git a/appview/pages/pages.go b/appview/pages/pages.go index d0866b46..505274c2 100644 --- a/appview/pages/pages.go +++ b/appview/pages/pages.go @@ -1262,6 +1262,10 @@ func (p *Pages) SingleString(w io.Writer, params SingleStringParams) error { return p.execute("strings/string", w, params) } +func (p *Pages) Home(w io.Writer, params TimelineParams) error { + return p.execute("timeline/home", w, params) +} + func (p *Pages) Static() http.Handler { if p.dev { return http.StripPrefix("/static/", http.FileServer(http.Dir("appview/pages/static"))) diff --git a/appview/pages/templates/timeline/fragments/timeline.html b/appview/pages/templates/timeline/fragments/timeline.html new file mode 100644 index 00000000..64523b60 --- /dev/null +++ b/appview/pages/templates/timeline/fragments/timeline.html @@ -0,0 +1,116 @@ +{{ define "timeline/fragments/timeline" }} +
+
+

Timeline

+
+ +
+ {{ range $i, $e := .Timeline }} +
+ {{ if ne $i 0 }} +
+ {{ end }} + {{ with $e }} +
+ {{ if .Repo }} + {{ template "timeline/fragments/repoEvent" (list $ .Repo .Source) }} + {{ else if .Star }} + {{ template "timeline/fragments/starEvent" (list $ .Star) }} + {{ else if .Follow }} + {{ template "timeline/fragments/followEvent" (list $ .Follow .Profile .FollowStats) }} + {{ end }} +
+ {{ end }} +
+ {{ end }} +
+
+{{ end }} + +{{ define "timeline/fragments/repoEvent" }} + {{ $root := index . 0 }} + {{ $repo := index . 1 }} + {{ $source := index . 2 }} + {{ $userHandle := resolve $repo.Did }} +
+ {{ template "user/fragments/picHandleLink" $repo.Did }} + {{ with $source }} + {{ $sourceDid := resolve .Did }} + forked + + {{ $sourceDid }}/{{ .Name }} + + to + {{ $repo.Name }} + {{ else }} + created + + {{ $repo.Name }} + + {{ end }} + {{ template "repo/fragments/time" $repo.Created }} +
+ {{ with $repo }} + {{ template "user/fragments/repoCard" (list $root . true) }} + {{ end }} +{{ end }} + +{{ define "timeline/fragments/starEvent" }} + {{ $root := index . 0 }} + {{ $star := index . 1 }} + {{ with $star }} + {{ $starrerHandle := resolve .StarredByDid }} + {{ $repoOwnerHandle := resolve .Repo.Did }} +
+ {{ template "user/fragments/picHandleLink" $starrerHandle }} + starred + + {{ $repoOwnerHandle | truncateAt30 }}/{{ .Repo.Name }} + + {{ template "repo/fragments/time" .Created }} +
+ {{ with .Repo }} + {{ template "user/fragments/repoCard" (list $root . true) }} + {{ end }} + {{ end }} +{{ end }} + +{{ define "timeline/fragments/followEvent" }} + {{ $root := index . 0 }} + {{ $follow := index . 1 }} + {{ $profile := index . 2 }} + {{ $stat := index . 3 }} + + {{ $userHandle := resolve $follow.UserDid }} + {{ $subjectHandle := resolve $follow.SubjectDid }} +
+ {{ template "user/fragments/picHandleLink" $userHandle }} + followed + {{ template "user/fragments/picHandleLink" $subjectHandle }} + {{ template "repo/fragments/time" $follow.FollowedAt }} +
+
+
+ +
+ +
+ + {{ $subjectHandle | truncateAt30 }} + + {{ with $profile }} + {{ with .Description }} +

{{.}}

+ {{ end }} + {{ end }} + {{ with $stat }} +
+ {{ i "users" "size-4" }} + {{ .Followers }} followers + + {{ .Following }} following +
+ {{ end }} +
+
+{{ end }} diff --git a/appview/pages/templates/timeline/fragments/trending.html b/appview/pages/templates/timeline/fragments/trending.html new file mode 100644 index 00000000..fb92e74b --- /dev/null +++ b/appview/pages/templates/timeline/fragments/trending.html @@ -0,0 +1,25 @@ +{{ define "timeline/fragments/trending" }} +
+
+

+ Trending + {{ i "trending-up" "size-4 flex-shrink-0" }} +

+
+
+ {{ range $index, $repo := .Repos }} +
+ {{ template "user/fragments/repoCard" (list $ $repo true) }} +
+ {{ else }} +
+
+ No trending repositories this week +
+
+ {{ end }} +
+
+{{ end }} + + diff --git a/appview/pages/templates/timeline/home.html b/appview/pages/templates/timeline/home.html new file mode 100644 index 00000000..e9274ee8 --- /dev/null +++ b/appview/pages/templates/timeline/home.html @@ -0,0 +1,117 @@ +{{ define "title" }}tangled · tightly-knit social coding{{ end }} + +{{ define "extrameta" }} + + + + +{{ end }} + + +{{ define "content" }} +
+ {{ block "hero" $ }}{{ end }} + {{ block "features" $ }}{{ end }} + {{ block "timeline/fragments/trending" $ }}{{ end }} + {{ block "timeline/fragments/timeline" $ }}{{ end }} +
+{{ end }} + + +{{ define "hero" }} +
+
+

tightly-knit
social coding.

+ +

+ tangled is new social-enabled git collaboration platform built on atproto. +

+

+ we envision a place where developers have complete ownership of their + code, open source communities can freely self-govern and most + importantly, coding can be social and fun again. +

+ + +
+ + +
+{{ end }} + +{{ define "feature" }} + {{ $info := index . 0 }} + {{ $bullets := index . 1 }} +
+
+

{{ $info.title }}

+
    + {{ range $bullets }} +
  • {{ escapeHtml . }}

  • + {{ end }} +
+
+
+ + {{ $info.alt }} + +
+
+{{ end }} + +{{ define "features" }} +
+ {{ template "feature" (list + (dict + "title" "lightweight git repo hosting" + "image" "https://assets.tangled.network/what-is-tangled-repo.png" + "alt" "A repository hosted on Tangled" + ) + (list + "Host your repositories on your own infrastructure using knots—tiny, headless servers that facilitate git operations." + "Fully federated using the AT Protocol." + "Use SSH to push/pull, guarded by role-based access control." + ) + ) }} + + {{ template "feature" (list + (dict + "title" "improved pull request model" + "image" "https://assets.tangled.network/pulls.png" + "alt" "Round-based pull requests." + ) + (list + "An intuitive and effective round-based pull request flow, with inter-diffing between rounds." + "Stacked pull requests using Jujutsu's change IDs." + "Paste a git diff or git format-patch for quick drive-by changes." + ) + ) }} + + {{ template "feature" (list + (dict + "title" "run pipelines using spindles" + "image" "https://assets.tangled.network/pipelines.png" + "alt" "CI pipeline running on spindle" + ) + (list + "Run pipelines on your own infrastructure using spindles—lightweight CI runners." + "Natively supports Nix for package management." + "Easily extended to support different execution backends." + ) + ) }} +
+{{ end }} + diff --git a/appview/pages/templates/timeline/timeline.html b/appview/pages/templates/timeline/timeline.html index 7f313f03..530054db 100644 --- a/appview/pages/templates/timeline/timeline.html +++ b/appview/pages/templates/timeline/timeline.html @@ -8,176 +8,6 @@ {{ end }} {{ define "content" }} - {{ if .LoggedInUser }} - {{ else }} - {{ block "hero" $ }}{{ end }} - {{ end }} - - {{ block "trending" $ }}{{ end }} - {{ block "timeline" $ }}{{ end }} -{{ end }} - -{{ define "hero" }} -
-
tightly-knit
social coding.
- -

- tangled is new social-enabled git collaboration platform built on atproto. -

-

- we envision a place where developers have complete ownership of their - code, open source communities can freely self-govern and most - importantly, coding can be social and fun again. -

- - -
-{{ end }} - -{{ define "trending" }} -
-
-

- Trending - {{ i "trending-up" "size-4 flex-shrink-0" }} -

-
-
- {{ range $index, $repo := .Repos }} -
- {{ template "user/fragments/repoCard" (list $ $repo true) }} -
- {{ else }} -
-
- No trending repositories this week -
-
- {{ end }} -
-
-{{ end }} - -{{ define "timeline" }} -
-
-

Timeline

-
- -
- {{ range $i, $e := .Timeline }} -
- {{ if ne $i 0 }} -
- {{ end }} - {{ with $e }} -
- {{ if .Repo }} - {{ block "repoEvent" (list $ .Repo .Source) }} {{ end }} - {{ else if .Star }} - {{ block "starEvent" (list $ .Star) }} {{ end }} - {{ else if .Follow }} - {{ block "followEvent" (list $ .Follow .Profile .FollowStats) }} {{ end }} - {{ end }} -
- {{ end }} -
- {{ end }} -
-
-{{ end }} - -{{ define "repoEvent" }} - {{ $root := index . 0 }} - {{ $repo := index . 1 }} - {{ $source := index . 2 }} - {{ $userHandle := resolve $repo.Did }} -
- {{ template "user/fragments/picHandleLink" $repo.Did }} - {{ with $source }} - {{ $sourceDid := resolve .Did }} - forked - - {{ $sourceDid }}/{{ .Name }} - - to - {{ $repo.Name }} - {{ else }} - created - - {{ $repo.Name }} - - {{ end }} - {{ template "repo/fragments/time" $repo.Created }} -
- {{ with $repo }} - {{ template "user/fragments/repoCard" (list $root . true) }} - {{ end }} -{{ end }} - -{{ define "starEvent" }} - {{ $root := index . 0 }} - {{ $star := index . 1 }} - {{ with $star }} - {{ $starrerHandle := resolve .StarredByDid }} - {{ $repoOwnerHandle := resolve .Repo.Did }} -
- {{ template "user/fragments/picHandleLink" $starrerHandle }} - starred - - {{ $repoOwnerHandle | truncateAt30 }}/{{ .Repo.Name }} - - {{ template "repo/fragments/time" .Created }} -
- {{ with .Repo }} - {{ template "user/fragments/repoCard" (list $root . true) }} - {{ end }} - {{ end }} -{{ end }} - - -{{ define "followEvent" }} - {{ $root := index . 0 }} - {{ $follow := index . 1 }} - {{ $profile := index . 2 }} - {{ $stat := index . 3 }} - - {{ $userHandle := resolve $follow.UserDid }} - {{ $subjectHandle := resolve $follow.SubjectDid }} -
- {{ template "user/fragments/picHandleLink" $userHandle }} - followed - {{ template "user/fragments/picHandleLink" $subjectHandle }} - {{ template "repo/fragments/time" $follow.FollowedAt }} -
-
-
- -
- -
- - {{ $subjectHandle | truncateAt30 }} - - {{ with $profile }} - {{ with .Description }} -

{{.}}

- {{ end }} - {{ end }} - {{ with $stat }} -
- {{ i "users" "size-4" }} - {{ .Followers }} followers - - {{ .Following }} following -
- {{ end }} -
-
+ {{ template "timeline/fragments/trending" . }} + {{ template "timeline/fragments/timeline" . }} {{ end }} diff --git a/appview/state/router.go b/appview/state/router.go index a41ee4b4..9b48ea96 100644 --- a/appview/state/router.go +++ b/appview/state/router.go @@ -111,7 +111,8 @@ func (s *State) StandardRouter(mw *middleware.Middleware) http.Handler { r.Handle("/static/*", s.pages.Static()) - r.Get("/", s.Timeline) + r.Get("/", s.HomeOrTimeline) + r.Get("/timeline", s.Timeline) r.Route("/repo", func(r chi.Router) { r.Route("/new", func(r chi.Router) { diff --git a/appview/state/state.go b/appview/state/state.go index 2803e9f8..26339562 100644 --- a/appview/state/state.go +++ b/appview/state/state.go @@ -190,10 +190,18 @@ func (s *State) PrivacyPolicy(w http.ResponseWriter, r *http.Request) { }) } +func (s *State) HomeOrTimeline(w http.ResponseWriter, r *http.Request) { + if s.oauth.GetUser(r) != nil { + s.Timeline(w, r) + return + } + s.Home(w, r) +} + func (s *State) Timeline(w http.ResponseWriter, r *http.Request) { user := s.oauth.GetUser(r) - timeline, err := db.MakeTimeline(s.db) + timeline, err := db.MakeTimeline(s.db, 50) if err != nil { log.Println(err) s.pages.Notice(w, "timeline", "Uh oh! Failed to load timeline.") @@ -213,6 +221,30 @@ func (s *State) Timeline(w http.ResponseWriter, r *http.Request) { }) } +func (s *State) Home(w http.ResponseWriter, r *http.Request) { + timeline, err := db.MakeTimeline(s.db, 15) + if err != nil { + log.Println(err) + s.pages.Notice(w, "timeline", "Uh oh! Failed to load timeline.") + return + } + + repos, err := db.GetTopStarredReposLastWeek(s.db) + if err != nil { + log.Println(err) + s.pages.Notice(w, "topstarredrepos", "Unable to load.") + return + } + + timeline = timeline[:5] + + s.pages.Home(w, pages.TimelineParams{ + LoggedInUser: nil, + Timeline: timeline, + Repos: repos, + }) +} + func (s *State) Keys(w http.ResponseWriter, r *http.Request) { user := chi.URLParam(r, "user") user = strings.TrimPrefix(user, "@") -- 2.43.0