An ATproto social media client -- with an independent Appview.

Merge pull request #9023 from internet-development/caidanw/app-1040-feed-open-graph-tags

feat: add feed open graph tags

authored by

jim and committed by
GitHub
426f0259 20ff3e3b

+106 -1
+52 -1
bskyweb/cmd/bskyweb/server.go
··· 313 313 e.GET("/profile/:handleOrDID/known-followers", server.WebGeneric) 314 314 e.GET("/profile/:handleOrDID/search", server.WebGeneric) 315 315 e.GET("/profile/:handleOrDID/lists/:rkey", server.WebGeneric) 316 - e.GET("/profile/:handleOrDID/feed/:rkey", server.WebGeneric) 316 + e.GET("/profile/:handleOrDID/feed/:rkey", server.WebFeed) 317 317 e.GET("/profile/:handleOrDID/feed/:rkey/liked-by", server.WebGeneric) 318 318 e.GET("/profile/:handleOrDID/labeler/liked-by", server.WebGeneric) 319 319 ··· 601 601 data["requestURI"] = fmt.Sprintf("https://%s%s", req.Host, req.URL.Path) 602 602 data["requestHost"] = req.Host 603 603 return c.Render(http.StatusOK, "profile.html", data) 604 + } 605 + 606 + func (srv *Server) WebFeed(c echo.Context) error { 607 + ctx := c.Request().Context() 608 + data := srv.NewTemplateContext() 609 + 610 + // sanity check arguments. don't 4xx, just let app handle if not expected format 611 + rkeyParam := c.Param("rkey") 612 + rkey, err := syntax.ParseRecordKey(rkeyParam) 613 + if err != nil { 614 + return c.Render(http.StatusOK, "feed.html", data) 615 + } 616 + handleOrDIDParam := c.Param("handleOrDID") 617 + handleOrDID, err := syntax.ParseAtIdentifier(handleOrDIDParam) 618 + if err != nil { 619 + return c.Render(http.StatusOK, "feed.html", data) 620 + } 621 + 622 + identifier := handleOrDID.Normalize().String() 623 + 624 + // requires two fetches: first fetch profile to get DID 625 + pv, err := appbsky.ActorGetProfile(ctx, srv.xrpcc, identifier) 626 + if err != nil { 627 + log.Warnf("failed to fetch profile for: %s\t%v", identifier, err) 628 + return c.Render(http.StatusOK, "feed.html", data) 629 + } 630 + unauthedViewingOkay := true 631 + for _, label := range pv.Labels { 632 + if label.Src == pv.Did && label.Val == "!no-unauthenticated" { 633 + unauthedViewingOkay = false 634 + } 635 + } 636 + 637 + if !unauthedViewingOkay { 638 + return c.Render(http.StatusOK, "feed.html", data) 639 + } 640 + did := pv.Did 641 + data["did"] = did 642 + 643 + // then fetch the feed generator 644 + feedURI := fmt.Sprintf("at://%s/app.bsky.feed.generator/%s", did, rkey) 645 + fgv, err := appbsky.FeedGetFeedGenerator(ctx, srv.xrpcc, feedURI) 646 + if err != nil { 647 + log.Warnf("failed to fetch feed generator: %s\t%v", feedURI, err) 648 + return c.Render(http.StatusOK, "feed.html", data) 649 + } 650 + req := c.Request() 651 + data["feedView"] = fgv.View 652 + data["requestURI"] = fmt.Sprintf("https://%s%s", req.Host, req.URL.Path) 653 + 654 + return c.Render(http.StatusOK, "feed.html", data) 604 655 } 605 656 606 657 type IPCCRequest struct {
+54
bskyweb/templates/feed.html
··· 1 + {% extends "base.html" %} 2 + 3 + {% block head_title %} 4 + {%- if feedView -%} 5 + {{ feedView.DisplayName }} by @{{ feedView.Creator.Handle }} | Bluesky Feed 6 + {%- else -%} 7 + Bluesky 8 + {%- endif -%} 9 + {% endblock %} 10 + 11 + {% block html_head_extra -%} 12 + {%- if feedView -%} 13 + <meta property="og:site_name" content="Bluesky Social"> 14 + <meta property="og:type" content="website"> 15 + {%- if requestURI %} 16 + <meta property="og:url" content="{{ requestURI }}"> 17 + <link rel="canonical" href="{{ requestURI|canonicalize_url }}" /> 18 + {% endif -%} 19 + 20 + {%- if feedView.DisplayName %} 21 + <meta property="og:title" content="{{ feedView.DisplayName }} by @{{ feedView.Creator.Handle }}"> 22 + {% else %} 23 + <meta property="og:title" content="Feed by @{{ feedView.Creator.Handle }}"> 24 + {% endif -%} 25 + 26 + {%- if feedView.Description %} 27 + <meta name="description" content="{{ feedView.Description }}"> 28 + <meta property="og:description" content="{{ feedView.Description }}"> 29 + <meta property="twitter:description" content="{{ feedView.Description }}"> 30 + {% endif -%} 31 + 32 + {%- if feedView.Avatar %} 33 + <meta property="og:image" content="{{ feedView.Avatar }}"> 34 + <meta property="twitter:image" content="{{ feedView.Avatar }}"> 35 + <meta name="twitter:card" content="summary"> 36 + {% endif %} 37 + 38 + <meta name="twitter:label1" content="Created by"> 39 + <meta name="twitter:value1" content="@{{ feedView.Creator.Handle }}"> 40 + 41 + <link rel="alternate" href="{{ feedView.Uri }}" /> 42 + {% endif -%} 43 + {%- endblock %} 44 + 45 + {% block noscript_extra -%} 46 + {%- if feedView -%} 47 + <div id="bsky_feed_summary"> 48 + <h3>Feed</h3> 49 + <p id="bsky_feed_name">{{ feedView.DisplayName }}</p> 50 + <p id="bsky_feed_creator">{{ feedView.Creator.Handle }}</p> 51 + <p id="bsky_feed_description">{{ feedView.Description }}</p> 52 + </div> 53 + {% endif -%} 54 + {%- endblock %}