modularizing opengraph headers and fixing a bunch of small things on there.

This commit is contained in:
fiatjaf
2023-11-06 11:07:20 -03:00
parent f349bd2d1d
commit 604be14407
7 changed files with 103 additions and 90 deletions

15
data.go
View File

@@ -2,7 +2,6 @@ package main
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"html" "html"
"html/template" "html/template"
@@ -75,7 +74,7 @@ type Data struct {
createdAt string createdAt string
modifiedAt string modifiedAt string
parentLink template.HTML parentLink template.HTML
metadata sdk.ProfileMetadata metadata *sdk.ProfileMetadata
authorRelays []string authorRelays []string
authorLong string authorLong string
authorShort string authorShort string
@@ -139,6 +138,9 @@ func grabData(ctx context.Context, code string, isProfileSitemap bool) (*Data, e
} }
data.npub, _ = nip19.EncodePublicKey(event.PubKey) data.npub, _ = nip19.EncodePublicKey(event.PubKey)
data.npubShort = data.npub[:8] + "…" + data.npub[len(data.npub)-4:]
data.authorLong = data.npub // hopefully will be replaced later
data.authorShort = data.npubShort // hopefully will be replaced later
data.nevent, _ = nip19.EncodeEvent(event.ID, relaysForNip19, event.PubKey) data.nevent, _ = nip19.EncodeEvent(event.ID, relaysForNip19, event.PubKey)
data.neventNaked, _ = nip19.EncodeEvent(event.ID, nil, event.PubKey) data.neventNaked, _ = nip19.EncodeEvent(event.ID, nil, event.PubKey)
data.naddr = "" data.naddr = ""
@@ -256,13 +258,14 @@ func grabData(ctx context.Context, code string, isProfileSitemap bool) (*Data, e
if event.Kind == 0 { if event.Kind == 0 {
data.nprofile, _ = nip19.EncodeProfile(event.PubKey, limitAt(relays, 2)) data.nprofile, _ = nip19.EncodeProfile(event.PubKey, limitAt(relays, 2))
json.Unmarshal([]byte(event.Content), &data.metadata) data.metadata, _ = sdk.ParseMetadata(event)
} else { } else {
ctx, cancel := context.WithTimeout(ctx, time.Second*3) ctx, cancel := context.WithTimeout(ctx, time.Second*3)
defer cancel() defer cancel()
author, relays, _ := getEvent(ctx, data.npub, relaysForNip19) author, relays, _ := getEvent(ctx, data.npub, relaysForNip19)
if author != nil { if author != nil {
if err := json.Unmarshal([]byte(author.Content), &data.metadata); err == nil { data.metadata, _ = sdk.ParseMetadata(author)
if data.metadata != nil {
data.authorLong = fmt.Sprintf("%s (%s)", data.metadata.Name, data.npub) data.authorLong = fmt.Sprintf("%s (%s)", data.metadata.Name, data.npub)
data.authorShort = fmt.Sprintf("%s (%s)", data.metadata.Name, data.npubShort) data.authorShort = fmt.Sprintf("%s (%s)", data.metadata.Name, data.npubShort)
} }
@@ -308,9 +311,5 @@ func grabData(ctx context.Context, code string, isProfileSitemap bool) (*Data, e
} }
} }
data.npubShort = data.npub[:8] + "…" + data.npub[len(data.npub)-4:]
data.authorLong = data.npub
data.authorShort = data.npubShort
return data, nil return data, nil
} }

View File

@@ -29,17 +29,20 @@ var (
//tmpl:bind head_common.html //tmpl:bind head_common.html
type OpenGraphPartial struct { type OpenGraphPartial struct {
IsTwitter bool SingleTitle string
TitleizedContent string // x (we will always render just the singletitle if we have that)
Title string Superscript string
TwitterTitle string Subscript string
Proxy string
AuthorLong string BigImage string
TextImageURL string // x (we will always render just the bigimage if we have that)
Video string Video string
VideoType string VideoType string
Image string Image string
Description string ProxiedImage string
// this is the main text we should always have
Text string
} }
func (*OpenGraphPartial) TemplateText() string { return tmplOpenGraph } func (*OpenGraphPartial) TemplateText() string { return tmplOpenGraph }
@@ -135,7 +138,7 @@ type TelegramInstantViewPage struct {
Content template.HTML Content template.HTML
Description string Description string
Subject string Subject string
Metadata sdk.ProfileMetadata Metadata *sdk.ProfileMetadata
AuthorLong string AuthorLong string
CreatedAt string CreatedAt string
} }
@@ -217,7 +220,7 @@ type NotePage struct {
Content template.HTML Content template.HTML
CreatedAt string CreatedAt string
Metadata sdk.ProfileMetadata Metadata *sdk.ProfileMetadata
Npub string Npub string
NpubShort string NpubShort string
ParentLink template.HTML ParentLink template.HTML
@@ -246,7 +249,7 @@ type ProfilePage struct {
CreatedAt string CreatedAt string
Domain string Domain string
LastNotes []EnhancedEvent LastNotes []EnhancedEvent
Metadata sdk.ProfileMetadata Metadata *sdk.ProfileMetadata
NormalizedAuthorWebsiteURL string NormalizedAuthorWebsiteURL string
RenderedAuthorAboutText template.HTML RenderedAuthorAboutText template.HTML
Nevent string Nevent string
@@ -275,7 +278,7 @@ type FileMetadataPage struct {
Content template.HTML Content template.HTML
CreatedAt string CreatedAt string
Metadata sdk.ProfileMetadata Metadata *sdk.ProfileMetadata
Npub string Npub string
NpubShort string NpubShort string
ParentLink template.HTML ParentLink template.HTML

View File

@@ -96,32 +96,24 @@ func renderEvent(w http.ResponseWriter, r *http.Request) {
} }
title := "" title := ""
twitterTitle := title if data.event.Kind >= 30000 && data.event.Kind < 40000 {
if data.event.Kind == 0 && data.metadata.Name != "" { tValue := "~"
title = data.metadata.Name for _, tag := range data.event.Tags {
} else { if tag[0] == "t" {
if data.event.Kind >= 30000 && data.event.Kind < 40000 { tValue = tag[1]
tValue := "~" break
for _, tag := range data.event.Tags {
if tag[0] == "t" {
tValue = tag[1]
break
}
} }
title = fmt.Sprintf("%s: %s", kindNames[data.event.Kind], tValue)
} else if kindName, ok := kindNames[data.event.Kind]; ok {
title = kindName
} else {
title = fmt.Sprintf("kind:%d event", data.event.Kind)
} }
if subject != "" { title = fmt.Sprintf("%s: %s", kindNames[data.event.Kind], tValue)
title += " (" + subject + ")" } else if kindName, ok := kindNames[data.event.Kind]; ok {
} title = kindName
twitterTitle += " by " + data.authorShort } else {
date := data.event.CreatedAt.Time().UTC().Format("2006-01-02 15:04") title = fmt.Sprintf("kind:%d event", data.event.Kind)
title += " at " + date
twitterTitle += " at " + date
} }
if subject != "" {
title += " (" + subject + ")"
}
title += " by " + data.authorShort
seenOnRelays := "" seenOnRelays := ""
if len(data.relays) > 0 { if len(data.relays) > 0 {
@@ -255,6 +247,18 @@ func renderEvent(w http.ResponseWriter, r *http.Request) {
FileMetadata: data.kind1063Metadata, FileMetadata: data.kind1063Metadata,
} }
opengraph := OpenGraphPartial{
BigImage: textImageURL,
Image: data.image,
Video: data.video,
VideoType: data.videoType,
ProxiedImage: "https://" + host + "/njump/proxy?src=" + data.image,
Superscript: data.authorLong,
Subscript: title,
Text: description,
}
switch data.templateId { switch data.templateId {
case TelegramInstantView: case TelegramInstantView:
err = TelegramInstantViewTemplate.Render(w, &TelegramInstantViewPage{ err = TelegramInstantViewTemplate.Render(w, &TelegramInstantViewPage{
@@ -270,20 +274,12 @@ func renderEvent(w http.ResponseWriter, r *http.Request) {
CreatedAt: data.createdAt, CreatedAt: data.createdAt,
}) })
case Note: case Note:
if style == StyleTwitter {
opengraph.SingleTitle = "by " + data.authorShort + " at " + humanDate(data.event.CreatedAt)
}
err = NoteTemplate.Render(w, &NotePage{ err = NoteTemplate.Render(w, &NotePage{
OpenGraphPartial: OpenGraphPartial{ OpenGraphPartial: opengraph,
IsTwitter: style == StyleTwitter,
Proxy: "https://" + host + "/njump/proxy?src=",
Title: title,
TwitterTitle: twitterTitle,
TitleizedContent: titleizedContent,
TextImageURL: textImageURL,
Image: data.image,
Video: data.video,
VideoType: data.videoType,
Description: description,
AuthorLong: data.authorLong,
},
HeadCommonPartial: HeadCommonPartial{ HeadCommonPartial: HeadCommonPartial{
IsProfile: false, IsProfile: false,
Oembed: oembed, Oembed: oembed,
@@ -306,20 +302,10 @@ func renderEvent(w http.ResponseWriter, r *http.Request) {
TitleizedContent: titleizedContent, TitleizedContent: titleizedContent,
}) })
case FileMetadata: case FileMetadata:
opengraph.Image = data.kind1063Metadata.DisplayImage()
err = FileMetadataTemplate.Render(w, &FileMetadataPage{ err = FileMetadataTemplate.Render(w, &FileMetadataPage{
OpenGraphPartial: OpenGraphPartial{ OpenGraphPartial: opengraph,
IsTwitter: style == StyleTwitter,
Proxy: "https://" + host + "/njump/proxy?src=",
TitleizedContent: titleizedContent,
TwitterTitle: twitterTitle,
Title: title,
TextImageURL: textImageURL,
Video: data.video,
VideoType: data.videoType,
Image: data.kind1063Metadata.DisplayImage(),
Description: description,
AuthorLong: data.authorLong,
},
HeadCommonPartial: HeadCommonPartial{ HeadCommonPartial: HeadCommonPartial{
IsProfile: false, IsProfile: false,
TailwindDebugStuff: tailwindDebugStuff, TailwindDebugStuff: tailwindDebugStuff,

View File

@@ -2,6 +2,7 @@
<html class="theme--default text-lg font-light print:text-base sm:text-xl"> <html class="theme--default text-lg font-light print:text-base sm:text-xl">
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<head> <head>
<title>File Metadata</title>
{{template "opengraph" .OpenGraphPartial}} {{template "opengraph" .OpenGraphPartial}}
<!----> <!---->
{{template "head_common" .HeadCommonPartial}} {{template "head_common" .HeadCommonPartial}}

View File

@@ -2,6 +2,8 @@
<html class="theme--default text-lg font-light print:text-base sm:text-xl"> <html class="theme--default text-lg font-light print:text-base sm:text-xl">
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<head> <head>
<title>{{.TitleizedContent}}</title>
{{template "opengraph" .OpenGraphPartial}} {{template "opengraph" .OpenGraphPartial}}
<!----> <!---->
{{template "head_common" .HeadCommonPartial}} {{template "head_common" .HeadCommonPartial}}

View File

@@ -1,29 +1,39 @@
<title>{{.TitleizedContent}}</title> {{ if not (eq "" .SingleTitle)}}
<!-- we only display this on twitter as a single title -->
<meta property="og:title" content="{{.Title}}" /> <meta name="twitter:title" content="{{.SingleTitle}}" />
{{ if .IsTwitter }} {{ else }}
<meta name="twitter:title" content="{{.TwitterTitle}}" /> <!-- these are not shown by twitter at all, so let's not even give them -->
<meta property="og:site_name" content="{{.Superscript}}" />
<meta property="og:title" content="{{.Subscript}}" />
{{ end }} {{ end }}
<meta property="og:site_name" content="{{.AuthorLong}}" /> <!-- this is used for when we want to take over the entire screen on twitter,
{{ if not (eq "" .TextImageURL) }} mostly for the big "text-to-image" images -->
{{ if not (eq "" .BigImage) }}
<meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@nostrprotocol" /> <meta name="twitter:site" content="@nostrprotocol" />
<meta property="og:image" content="{{.TextImageURL}}" /> <meta property="og:image" content="{{.BigImage}}" />
<meta name="twitter:image" content="{{.TextImageURL}}" /> <meta name="twitter:image" content="{{.BigImage}}" />
{{ else }} {{ else }}
<!----> <!-- otherwise we tell twitter to display it as a normal text-based embed.
these distinctions don't seem to make any difference in other platforms,
maybe telegram -->
<meta property="twitter:card" content="summary" /> <meta property="twitter:card" content="summary" />
{{ if not (eq "" .Image) }} {{ if not (eq "" .Image) }}
<meta property="og:image" content="{{.Image}}" /> <meta property="og:image" content="{{.Image}}" />
<meta name="twitter:image" content="{{.Proxy}}{{.Image}}" /> <meta name="twitter:image" content="{{.ProxiedImage}}" />
{{ end }} {{ if not (eq "" .Video) }} {{ end }}
<!---->
{{ if not (eq "" .Video) }}
<meta property="og:video" content="{{.Video}}" /> <meta property="og:video" content="{{.Video}}" />
<meta property="og:video:secure_url" content="{{.Video}}" /> <meta property="og:video:secure_url" content="{{.Video}}" />
<meta property="og:video:type" content="video/{{.VideoType}}" /> <meta property="og:video:type" content="video/{{.VideoType}}" />
{{ end }} {{ end }} {{ end }}
<!----> <!-- end the BigImage /if above -->
{{ if not (eq "" .Description) }} {{ end }}
<meta property="og:description" content="{{.Description}}" />
<meta name="twitter:description" content="{{.Description}}" /> <!-- now just display the short text if we have any (which we always should) -->
{{ if not (eq "" .Text) }}
<meta property="og:description" content="{{.Text}}" />
<meta name="twitter:description" content="{{.Text}}" />
{{ end }} {{ end }}

View File

@@ -4,7 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
html "html" "html"
"html/template" "html/template"
"io" "io"
"net/http" "net/http"
@@ -549,3 +549,15 @@ func limitAt[V any](list []V, n int) []V {
} }
return list[0:n] return list[0:n]
} }
func humanDate(createdAt nostr.Timestamp) string {
ts := createdAt.Time()
now := time.Now()
if ts.Before(now.AddDate(0, -9, 0)) {
return ts.UTC().Format("02 Jan 2006")
} else if ts.Before(now.AddDate(0, 0, -6)) {
return ts.UTC().Format("Jan _2")
} else {
return ts.UTC().Format("Mon, Jan _2 15:04 UTC")
}
}