about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--bskyweb/cmd/bskyweb/server.go53
-rw-r--r--bskyweb/templates/feed.html54
2 files changed, 106 insertions, 1 deletions
diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go
index f305f0d3c..8d75bb6ef 100644
--- a/bskyweb/cmd/bskyweb/server.go
+++ b/bskyweb/cmd/bskyweb/server.go
@@ -313,7 +313,7 @@ func serve(cctx *cli.Context) error {
 	e.GET("/profile/:handleOrDID/known-followers", server.WebGeneric)
 	e.GET("/profile/:handleOrDID/search", server.WebGeneric)
 	e.GET("/profile/:handleOrDID/lists/:rkey", server.WebGeneric)
-	e.GET("/profile/:handleOrDID/feed/:rkey", server.WebGeneric)
+	e.GET("/profile/:handleOrDID/feed/:rkey", server.WebFeed)
 	e.GET("/profile/:handleOrDID/feed/:rkey/liked-by", server.WebGeneric)
 	e.GET("/profile/:handleOrDID/labeler/liked-by", server.WebGeneric)
 
@@ -603,6 +603,57 @@ func (srv *Server) WebProfile(c echo.Context) error {
 	return c.Render(http.StatusOK, "profile.html", data)
 }
 
+func (srv *Server) WebFeed(c echo.Context) error {
+	ctx := c.Request().Context()
+	data := srv.NewTemplateContext()
+
+	// sanity check arguments. don't 4xx, just let app handle if not expected format
+	rkeyParam := c.Param("rkey")
+	rkey, err := syntax.ParseRecordKey(rkeyParam)
+	if err != nil {
+		return c.Render(http.StatusOK, "feed.html", data)
+	}
+	handleOrDIDParam := c.Param("handleOrDID")
+	handleOrDID, err := syntax.ParseAtIdentifier(handleOrDIDParam)
+	if err != nil {
+		return c.Render(http.StatusOK, "feed.html", data)
+	}
+
+	identifier := handleOrDID.Normalize().String()
+
+	// requires two fetches: first fetch profile to get DID
+	pv, err := appbsky.ActorGetProfile(ctx, srv.xrpcc, identifier)
+	if err != nil {
+		log.Warnf("failed to fetch profile for: %s\t%v", identifier, err)
+		return c.Render(http.StatusOK, "feed.html", data)
+	}
+	unauthedViewingOkay := true
+	for _, label := range pv.Labels {
+		if label.Src == pv.Did && label.Val == "!no-unauthenticated" {
+			unauthedViewingOkay = false
+		}
+	}
+
+	if !unauthedViewingOkay {
+		return c.Render(http.StatusOK, "feed.html", data)
+	}
+	did := pv.Did
+	data["did"] = did
+
+	// then fetch the feed generator
+	feedURI := fmt.Sprintf("at://%s/app.bsky.feed.generator/%s", did, rkey)
+	fgv, err := appbsky.FeedGetFeedGenerator(ctx, srv.xrpcc, feedURI)
+	if err != nil {
+		log.Warnf("failed to fetch feed generator: %s\t%v", feedURI, err)
+		return c.Render(http.StatusOK, "feed.html", data)
+	}
+	req := c.Request()
+	data["feedView"] = fgv.View
+	data["requestURI"] = fmt.Sprintf("https://%s%s", req.Host, req.URL.Path)
+
+	return c.Render(http.StatusOK, "feed.html", data)
+}
+
 type IPCCRequest struct {
 	IP string `json:"ip"`
 }
diff --git a/bskyweb/templates/feed.html b/bskyweb/templates/feed.html
new file mode 100644
index 000000000..b20139582
--- /dev/null
+++ b/bskyweb/templates/feed.html
@@ -0,0 +1,54 @@
+{% extends "base.html" %}
+
+{% block head_title %}
+{%- if feedView -%}
+  {{ feedView.DisplayName }} by @{{ feedView.Creator.Handle }} | Bluesky Feed
+{%- else -%}
+  Bluesky
+{%- endif -%}
+{% endblock %}
+
+{% block html_head_extra -%}
+{%- if feedView -%}
+  <meta property="og:site_name" content="Bluesky Social">
+  <meta property="og:type" content="website">
+  {%- if requestURI %}
+  <meta property="og:url" content="{{ requestURI }}">
+  <link rel="canonical" href="{{ requestURI|canonicalize_url }}" />
+  {% endif -%}
+
+  {%- if feedView.DisplayName %}
+  <meta property="og:title" content="{{ feedView.DisplayName }} by @{{ feedView.Creator.Handle }}">
+  {% else %}
+  <meta property="og:title" content="Feed by @{{ feedView.Creator.Handle }}">
+  {% endif -%}
+
+  {%- if feedView.Description %}
+  <meta name="description" content="{{ feedView.Description }}">
+  <meta property="og:description" content="{{ feedView.Description }}">
+  <meta property="twitter:description" content="{{ feedView.Description }}">
+  {% endif -%}
+
+  {%- if feedView.Avatar %}
+  <meta property="og:image" content="{{ feedView.Avatar }}">
+  <meta property="twitter:image" content="{{ feedView.Avatar }}">
+  <meta name="twitter:card" content="summary">
+  {% endif %}
+
+  <meta name="twitter:label1" content="Created by">
+  <meta name="twitter:value1" content="@{{ feedView.Creator.Handle }}">
+
+  <link rel="alternate" href="{{ feedView.Uri }}" />
+{% endif -%}
+{%- endblock %}
+
+{% block noscript_extra -%}
+{%- if feedView -%}
+<div id="bsky_feed_summary">
+  <h3>Feed</h3>
+  <p id="bsky_feed_name">{{ feedView.DisplayName }}</p>
+  <p id="bsky_feed_creator">{{ feedView.Creator.Handle }}</p>
+  <p id="bsky_feed_description">{{ feedView.Description }}</p>
+</div>
+{% endif -%}
+{%- endblock %}