mirror of
https://github.com/aljazceru/njump.git
synced 2025-12-18 23:04:21 +01:00
modularizing opengraph headers and fixing a bunch of small things on there.
This commit is contained in:
15
data.go
15
data.go
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
33
pages.go
33
pages.go
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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}}
|
||||||
|
|||||||
@@ -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}}
|
||||||
|
|||||||
@@ -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 }}
|
||||||
|
|||||||
14
utils.go
14
utils.go
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user