tmpl migration: profile.html

This commit is contained in:
fiatjaf
2023-10-21 11:46:36 -03:00
parent 8cd313572f
commit 901b2b5d62
6 changed files with 149 additions and 104 deletions

57
data.go
View File

@@ -4,21 +4,38 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"html"
"html/template"
"strings" "strings"
"time" "time"
"github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip10"
"github.com/nbd-wtf/go-nostr/nip19" "github.com/nbd-wtf/go-nostr/nip19"
) )
type Event struct { type LastNotesItem struct {
Npub string Npub string
NpubShort string NpubShort string
Nevent string Nevent string
Content string Content string
CreatedAt string CreatedAt string
ModifiedAt string ModifiedAt string
ParentNevent string IsReply bool
}
func (i LastNotesItem) Preview() template.HTML {
lines := strings.Split(html.EscapeString(i.Content), "\n")
var processedLines []string
for _, line := range lines {
if strings.TrimSpace(line) == "" {
continue
}
processedLine := shortenNostrURLs(line)
processedLines = append(processedLines, processedLine)
}
return template.HTML(strings.Join(processedLines, "<br/>"))
} }
type Data struct { type Data struct {
@@ -37,7 +54,7 @@ type Data struct {
authorRelays []string authorRelays []string
authorLong string authorLong string
authorShort string authorShort string
renderableLastNotes []*Event renderableLastNotes []LastNotesItem
kindDescription string kindDescription string
kindNIP string kindNIP string
video string video string
@@ -69,7 +86,7 @@ func grabData(ctx context.Context, code string, isProfileSitemap bool) (*Data, e
modifiedAt := time.Unix(int64(event.CreatedAt), 0).Format("2006-01-02T15:04:05Z07:00") modifiedAt := time.Unix(int64(event.CreatedAt), 0).Format("2006-01-02T15:04:05Z07:00")
author := event author := event
var renderableLastNotes []*Event var renderableLastNotes []LastNotesItem
parentNevent := "" parentNevent := ""
authorRelays := []string{} authorRelays := []string{}
var content string var content string
@@ -98,7 +115,7 @@ func grabData(ctx context.Context, code string, isProfileSitemap bool) (*Data, e
key = "lns:" + event.PubKey key = "lns:" + event.PubKey
eventsToFetch = 50000 eventsToFetch = 50000
} else { } else {
typ = "profile" templateId = Profile
key = "ln:" + event.PubKey key = "ln:" + event.PubKey
} }
@@ -131,15 +148,15 @@ func grabData(ctx context.Context, code string, isProfileSitemap bool) (*Data, e
} }
} }
renderableLastNotes = make([]*Event, len(lastNotes)) renderableLastNotes = make([]LastNotesItem, len(lastNotes))
for i, n := range lastNotes { for i, levt := range lastNotes {
nevent, _ := nip19.EncodeEvent(n.ID, []string{}, n.PubKey) nevent, _ := nip19.EncodeEvent(levt.ID, []string{}, levt.PubKey)
renderableLastNotes[i] = &Event{ renderableLastNotes[i] = LastNotesItem{
Nevent: nevent, Nevent: nevent,
Content: n.Content, Content: levt.Content,
CreatedAt: time.Unix(int64(n.CreatedAt), 0).Format("2006-01-02 15:04:05"), CreatedAt: time.Unix(int64(levt.CreatedAt), 0).Format("2006-01-02 15:04:05"),
ModifiedAt: time.Unix(int64(n.CreatedAt), 0).Format("2006-01-02T15:04:05Z07:00"), ModifiedAt: time.Unix(int64(levt.CreatedAt), 0).Format("2006-01-02T15:04:05Z07:00"),
ParentNevent: getParentNevent(n), IsReply: nip10.GetImmediateReply(levt.Tags) != nil,
} }
} }
if err != nil { if err != nil {

View File

@@ -70,7 +70,6 @@ func main() {
// initialize templates // initialize templates
// use a mapping to expressly link the templates and share them between more kinds/types // use a mapping to expressly link the templates and share them between more kinds/types
templateMapping = map[string]string{ templateMapping = map[string]string{
"profile": "profile.html",
"profile_sitemap": "sitemap.xml", "profile_sitemap": "sitemap.xml",
"relay": "relay.html", "relay": "relay.html",
"relay_sitemap": "sitemap.xml", "relay_sitemap": "sitemap.xml",

View File

@@ -226,3 +226,35 @@ type NotePage struct {
func (*NotePage) TemplateText() string { func (*NotePage) TemplateText() string {
return tmplNote return tmplNote
} }
var (
//go:embed templates/profile.html
tmplProfile string
ProfileTemplate = tmpl.MustCompile(&ProfilePage{})
)
type ProfilePage struct {
HeadCommonPartial `tmpl:"head_common"`
TopPartial `tmpl:"top"`
DetailsPartial `tmpl:"details"`
ClientsPartial `tmpl:"clients"`
FooterPartial `tmpl:"footer"`
AuthorRelays []string
Content string
CreatedAt string
Domain string
LastNotes []LastNotesItem
Metadata nostr.ProfileMetadata
NormalizedAuthorWebsiteURL string
RenderedAuthorAboutText template.HTML
Nevent string
Npub string
IsReply string
Proxy string
Title string
}
func (*ProfilePage) TemplateText() string {
return tmplProfile
}

View File

@@ -328,6 +328,28 @@ func render(w http.ResponseWriter, r *http.Request) {
Video: data.video, Video: data.video,
VideoType: data.videoType, VideoType: data.videoType,
}) })
case Profile:
err = ProfileTemplate.Render(w, &ProfilePage{
HeadCommonPartial: HeadCommonPartial{IsProfile: true},
DetailsPartial: DetailsPartial{
HideDetails: true,
CreatedAt: data.createdAt,
KindDescription: data.kindDescription,
KindNIP: data.kindNIP,
EventJSON: string(eventJSON),
Kind: data.event.Kind,
},
ClientsPartial: ClientsPartial{
Clients: generateClientList(code, data.event),
},
Metadata: data.metadata,
NormalizedAuthorWebsiteURL: normalizeWebsiteURL(data.metadata.Website),
RenderedAuthorAboutText: template.HTML(basicFormatting(html.EscapeString(data.metadata.About), false, false)),
Npub: data.npub,
AuthorRelays: data.authorRelays,
LastNotes: data.renderableLastNotes,
})
case Other: case Other:
err = OtherTemplate.Render(w, &OtherPage{ err = OtherTemplate.Render(w, &OtherPage{
HeadCommonPartial: HeadCommonPartial{IsProfile: false}, HeadCommonPartial: HeadCommonPartial{IsProfile: false},

View File

@@ -7,6 +7,7 @@ import (
"time" "time"
"github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip10"
"github.com/nbd-wtf/go-nostr/nip11" "github.com/nbd-wtf/go-nostr/nip11"
"github.com/nbd-wtf/go-nostr/nip19" "github.com/nbd-wtf/go-nostr/nip19"
) )
@@ -43,24 +44,24 @@ func renderRelayPage(w http.ResponseWriter, r *http.Request) {
Limit: events_num, Limit: events_num,
}) })
} }
renderableLastNotes := make([]*Event, len(lastNotes)) renderableLastNotes := make([]LastNotesItem, len(lastNotes))
lastEventAt := time.Now() lastEventAt := time.Now()
relay := []string{"wss://" + hostname} relay := []string{"wss://" + hostname}
for i, n := range lastNotes { for i, levt := range lastNotes {
nevent, _ := nip19.EncodeEvent(n.ID, relay, n.PubKey) nevent, _ := nip19.EncodeEvent(levt.ID, relay, levt.PubKey)
npub, _ := nip19.EncodePublicKey(n.PubKey) npub, _ := nip19.EncodePublicKey(levt.PubKey)
npubShort := npub[:8] + "…" + npub[len(npub)-4:] npubShort := npub[:8] + "…" + npub[len(npub)-4:]
renderableLastNotes[i] = &Event{ renderableLastNotes[i] = LastNotesItem{
Npub: npub, Npub: npub,
NpubShort: npubShort, NpubShort: npubShort,
Nevent: nevent, Nevent: nevent,
Content: n.Content, Content: levt.Content,
CreatedAt: time.Unix(int64(n.CreatedAt), 0).Format("2006-01-02 15:04:05"), CreatedAt: time.Unix(int64(levt.CreatedAt), 0).Format("2006-01-02 15:04:05"),
ModifiedAt: time.Unix(int64(n.CreatedAt), 0).Format("2006-01-02T15:04:05Z07:00"), ModifiedAt: time.Unix(int64(levt.CreatedAt), 0).Format("2006-01-02T15:04:05Z07:00"),
ParentNevent: getParentNevent(n), IsReply: nip10.GetImmediateReply(levt.Tags) != nil,
} }
if i == 0 { if i == 0 {
lastEventAt = time.Unix(int64(n.CreatedAt), 0) lastEventAt = time.Unix(int64(levt.CreatedAt), 0)
} }
} }

View File

@@ -1,113 +1,92 @@
<!doctype html> <!DOCTYPE html>
<html class="theme--default"> <html class="theme--default">
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<head> <head>
<title> <title>{{.Metadata.Name}} / {{.Metadata.DisplayName}} is on nostr</title>
{{.metadata.Name | escapeString}} / {{.metadata.DisplayName |
escapeString}} is on nostr
</title>
<meta <meta
name="description" name="description"
content="{{.npub | escapeString}} is {{.metadata.Name | escapeString}} / {{.metadata.DisplayName | escapeString}} public key on nostr" content="{{.Npub}} is {{.Metadata.Name}} / {{.Metadata.DisplayName}} public key on nostr"
/>
<meta property="og:title" content="{{.title | escapeString}}" />
<meta property="og:site_name" content="{{.npub | escapeString}}" />
{{ if .metadata.Picture }}
<meta property="og:image" content="{{.metadata.Picture | escapeString}}" />
<meta
property="twitter:image"
content="{{.proxy}}{{.metadata.Picture | escapeString}}"
/>
{{end}} {{ if .metadata.About }}
<meta
property="og:description"
content="{{.metadata.About | escapeString}}"
/> />
<meta property="og:title" content="{{.Title}}" />
<meta property="og:site_name" content="{{.Npub}}" />
{{ if not (eq "" .Metadata.Picture) }}
<meta property="og:image" content="{{.Metadata.Picture}}" />
<meta property="twitter:image" content="{{.Proxy}}{{.Metadata.Picture}}" />
{{end}} {{ if not (eq "" .Metadata.About) }}
<meta property="og:description" content="{{.Metadata.About}}" />
{{end}} {{end}}
<meta property="twitter:card" content="summary" /> <meta property="twitter:card" content="summary" />
<link <link rel="canonical" href="https://njump.me/{{.Npub}}" />
rel="canonical"
href="https://{{s.Domain}}/{{.npub | escapeString }}"
/>
<link <link
rel="sitemap" rel="sitemap"
type="application/xml" type="application/xml"
title="Sitemap for {{.npub | escapeString}}" title="Sitemap for {{.Npub}}"
href="/{{.npub | escapeString}}.xml" href="/{{.Npub}}.Xml"
/> />
{{template "head_common.html" .}} {{template "head_common" .}}
</head> </head>
<body class="profile"> <body class="profile">
{{template "top.html" .}} {{template "top" .}}
<div class="container_wrapper"> <div class="container_wrapper">
<div class="container"> <div class="container">
<header class="column columnA"> <header class="column columnA">
<div class="info-wrapper"> <div class="info-wrapper">
<div class="name"> <div class="name">
{{.metadata.Name | escapeString}} {{if not (eq .metadata.Name {{.Metadata.Name}} {{if not (eq .Metadata.Name
.metadata.DisplayName)}} .Metadata.DisplayName)}}
<span class="display" <span class="display">{{.Metadata.DisplayName}}</span>
>{{.metadata.DisplayName | escapeString}}</span
>
{{end}} {{end}}
</div> </div>
</div> </div>
<div class="pic-wrapper"> <div class="pic-wrapper">
<img class="pic" src="{{.metadata.Picture | escapeString}}" /> <img class="pic" src="{{.Metadata.Picture}}" />
</div> </div>
<div class="last_update"> <div class="last_update">
Last update:<br /> Last update:<br />
{{.createdAt | escapeString}} {{.CreatedAt}}
</div> </div>
</header> </header>
<div class="column column_content"> <div class="column column_content">
<header class="field info-wrapper"> <header class="field info-wrapper">
<h1 id="profile_name" class="name"> <h1 id="profile_name" class="name">
{{.metadata.Name | escapeString}} {{if not (eq .metadata.Name {{.Metadata.Name}} {{if not (eq .Metadata.Name
.metadata.DisplayName)}} .Metadata.DisplayName)}}
<span class="display" <span class="display">{{.Metadata.DisplayName}}</span>
>{{.metadata.DisplayName | escapeString}}</span
>
{{end}} {{end}}
</h1> </h1>
</header> </header>
<div class="field separator long"></div> <div class="field separator long"></div>
<div class="field"> <div class="field">
<a href="{{.metadata.Website | normalizeWebsiteURL | escapeString}}" <a href="{{.NormalizedAuthorWebsiteURL}}">{{.Metadata.Website}}</a>
>{{.metadata.Website | escapeString}}</a
>
</div>
<div class="field about">
{{.metadata.About | escapeString | basicFormatting}}
</div> </div>
<div class="field about">{{.RenderedAuthorAboutText}}</div>
<div class="field separator"></div> <div class="field separator"></div>
<div class="field"> <div class="field">
<div class="label">Public key</div> <div class="label">Public key</div>
{{.npub | escapeString}} {{.Npub}}
</div> </div>
<div class="field"> <div class="field">
<div class="label">NIP-05</div> <div class="label">NIP-05</div>
{{.metadata.NIP05 | escapeString}} {{.Metadata.NIP05}}
</div> </div>
<div class="field"> <div class="field">
<div class="label">LN Address</div> <div class="label">LN Address</div>
{{.metadata.LUD16 | escapeString}} {{.Metadata.LUD16}}
</div> </div>
{{ if not (eq 0 (len .AuthorRelays)) }}
<div class="field"> <div class="field">
<div class="label">Posting on these relays</div> <div class="label">Posting on these relays</div>
{{if .authorRelays}} {{range $index, $element := .authorRelays}} {{range $index, $element := .AuthorRelays}}
<a href="/r/{{$element | escapeString}}" class="button" <a href="/r/{{$element}}" class="button">{{$element}}</a>
>{{$element}}</a {{end}}
>
{{end}} {{else}} No relays found, sorry! Try to reload to search
again. {{end}}
</div> </div>
{{ end }}
<div class="field advanced-switch-wrapper"> <div class="field advanced-switch-wrapper">
<input <input
@@ -119,29 +98,26 @@
<label for="advanced-switch">Show more details</label> <label for="advanced-switch">Show more details</label>
</div> </div>
{{template "details.html" .}} {{template "details" .}}
<div class="field last_update"> <div class="field last_update">
Last update:<br /> Last update:<br />
{{.createdAt | escapeString}} {{.CreatedAt}}
</div> </div>
{{if not (eq 0 (len .LastNotes))}}
<div class="field separator"></div> <div class="field separator"></div>
{{if .lastNotes}}
<nav class="field last_notes"> <nav class="field last_notes">
<h2>Last Notes</h2> <h2>Last Notes</h2>
{{range .lastNotes}} {{range $i, $element := .LastNotes}}
<a href="/{{.Nevent | escapeString}}" class="note"> <a href="/{{.Nevent}}" class="note">
<div class="header"> <div class="header">
<div class="published_at">{{.CreatedAt | escapeString}}</div> <div class="published_at">{{$element.CreatedAt}}</div>
{{if not (eq .ParentNevent "")}} {{if $element.IsReply}}
<div class="is_reply">- reply</div> <div class="is_reply">- reply</div>
{{end}} {{end}}
</div> </div>
<div class="content"> <div class="content">{{$element.Preview}}</div>
{{.Content | escapeString | previewNotesFormatting}}
</div>
</a> </a>
{{end}} {{end}}
</nav> </nav>
@@ -150,14 +126,12 @@
<div class="field separator"></div> <div class="field separator"></div>
</div> </div>
{{template "column_clients.html" .}} {{template "clients" .ClientsPartial}}
</div> </div>
</div> </div>
{{template "footer.html"}} {{template "footer" .}}
<script> <script src="/njump/static/scripts.js"></script>
{{template "scripts.js"}}
</script>
</body> </body>
</html> </html>