mirror of
https://github.com/aljazceru/njump.git
synced 2026-01-31 11:44:34 +01:00
Show last notes on profiles and add canonical to notes
This commit is contained in:
26
nostr.go
26
nostr.go
@@ -98,3 +98,29 @@ func getEvent(ctx context.Context, code string) (*nostr.Event, error) {
|
||||
|
||||
return nil, fmt.Errorf("couldn't find this %s", prefix)
|
||||
}
|
||||
|
||||
func getLastNotes(ctx context.Context, npub string) ([]nostr.Event, error) {
|
||||
var filter nostr.Filters
|
||||
relays := make([]string, 0, 7)
|
||||
relays = append(relays, always...)
|
||||
lastNotes := make([]nostr.Event, 0)
|
||||
if _, v, err := nip19.Decode(npub); err == nil {
|
||||
pub := v.(string)
|
||||
filter = nostr.Filters{
|
||||
{
|
||||
Kinds: []int{nostr.KindTextNote},
|
||||
Authors: []string{pub},
|
||||
Limit: 20,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second*4)
|
||||
defer cancel()
|
||||
events := pool.SubManyEose(ctx, relays, filter)
|
||||
for event := range events {
|
||||
lastNotes = append(lastNotes, *event)
|
||||
}
|
||||
return lastNotes, nil
|
||||
}
|
||||
|
||||
27
render.go
27
render.go
@@ -22,6 +22,13 @@ var static embed.FS
|
||||
//go:embed templates/*
|
||||
var templates embed.FS
|
||||
|
||||
type Event struct {
|
||||
Nevent string
|
||||
Content string
|
||||
CreatedAt string
|
||||
// ...
|
||||
}
|
||||
|
||||
func render(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println(r.URL.Path, ":~", r.Header.Get("user-agent"))
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
@@ -49,17 +56,35 @@ func render(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
npub, _ := nip19.EncodePublicKey(event.PubKey)
|
||||
nevent, _ := nip19.EncodeEvent(event.ID, []string{}, event.PubKey)
|
||||
note := ""
|
||||
naddr := ""
|
||||
createdAt := time.Unix(int64(event.CreatedAt), 0).Format("2006-01-02 15:04:05")
|
||||
|
||||
typ := ""
|
||||
author := event
|
||||
lastNotes := make([]Event, 0)
|
||||
|
||||
if event.Kind == 0 {
|
||||
typ = "profile"
|
||||
thisLastNotes, err := getLastNotes(r.Context(), code)
|
||||
lastNotes = make([]Event, len(thisLastNotes))
|
||||
for i, n := range thisLastNotes {
|
||||
this_nevent, _ := nip19.EncodeEvent(n.ID, []string{}, n.PubKey)
|
||||
this_date := time.Unix(int64(n.CreatedAt), 0).Format("2006-01-02 15:04:05")
|
||||
lastNotes[i] = Event{
|
||||
Nevent: this_nevent,
|
||||
Content: n.Content,
|
||||
CreatedAt: this_date,
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
http.Error(w, "error fetching event: "+err.Error(), 404)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if event.Kind == 1 || event.Kind == 7 || event.Kind == 30023 {
|
||||
typ = "note"
|
||||
note, _ = nip19.EncodeNote(event.ID)
|
||||
} else if event.Kind >= 30000 && event.Kind < 40000 {
|
||||
typ = "address"
|
||||
if d := event.Tags.GetFirst([]string{"d", ""}); d != nil {
|
||||
@@ -179,6 +204,7 @@ func render(w http.ResponseWriter, r *http.Request) {
|
||||
"npub": npub,
|
||||
"npubShort": npubShort,
|
||||
"nevent": nevent,
|
||||
"note": note,
|
||||
"naddr": naddr,
|
||||
"metadata": metadata,
|
||||
"authorLong": authorLong,
|
||||
@@ -193,6 +219,7 @@ func render(w http.ResponseWriter, r *http.Request) {
|
||||
"kindID": event.Kind,
|
||||
"kindDescription": kindDescription,
|
||||
"kindNIP": kindNIP,
|
||||
"lastNotes": lastNotes,
|
||||
}
|
||||
|
||||
// Use a mapping to expressly link the templates and share them between more kinds/types
|
||||
|
||||
@@ -32,6 +32,10 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
.theme--default a {
|
||||
color: #373737;
|
||||
}
|
||||
@@ -323,6 +327,40 @@ body {
|
||||
.theme--dark .container .column_content .field.boxed .label {
|
||||
background: #191919;
|
||||
}
|
||||
.container .column_content .field.last_notes a {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
}
|
||||
.container .column_content .field.last_notes a.note {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.container .column_content .field.last_notes a.note .published_at {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.theme--default .container .column_content .field.last_notes a.note .published_at {
|
||||
color: #e32a6d;
|
||||
}
|
||||
.theme--dark .container .column_content .field.last_notes a.note .published_at {
|
||||
color: #e32a6d;
|
||||
}
|
||||
.container .column_content .field.last_notes a.note .content {
|
||||
max-height: 160px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.container .column_content .field.last_notes a.note .content.gradient {
|
||||
-webkit-mask-image: linear-gradient(to bottom, rgb(0, 0, 0) 50%, rgba(0, 0, 0, 0) 100%);
|
||||
mask-image: linear-gradient(to bottom, rgb(0, 0, 0) 50%, rgba(0, 0, 0, 0) 100%);
|
||||
}
|
||||
.container .column_content .field.last_notes a:hover {
|
||||
padding-left: 1rem;
|
||||
margin-left: -1.5rem;
|
||||
}
|
||||
.theme--default .container .column_content .field.last_notes a:hover {
|
||||
border-left: 0.5rem solid #f3f3f3;
|
||||
}
|
||||
.theme--dark .container .column_content .field.last_notes a:hover {
|
||||
border-left: 0.5rem solid #2d2d2d;
|
||||
}
|
||||
.container .column_content .field.advanced-switch-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -667,6 +705,7 @@ body.note .column_content .profile_intro .published_at, body.raw .column_content
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 2rem;
|
||||
color: #9a9a9a;
|
||||
font-size: 0.8rem;
|
||||
text-align: center;
|
||||
|
||||
@@ -24,7 +24,7 @@ $themes: (
|
||||
boxed-title: $color-base7,
|
||||
boxed-bg-title: $color-base4,
|
||||
boxed-bg: $color-base3,
|
||||
separator: $color-base3,
|
||||
over-bg: $color-base3,
|
||||
theme-toggle: $color-base3,
|
||||
),
|
||||
dark: (
|
||||
@@ -42,7 +42,7 @@ $themes: (
|
||||
boxed-title: $color-base5,
|
||||
boxed-bg-title: darken($color-base6, 14%),
|
||||
boxed-bg: darken($color-base7, 14%),
|
||||
separator: darken($color-base7, 4%),
|
||||
over-bg: darken($color-base7, 4%),
|
||||
theme-toggle: $color-base6,
|
||||
)
|
||||
);
|
||||
@@ -61,7 +61,7 @@ $bg-down: 'bg-down';
|
||||
$boxed-title: 'boxed-title';
|
||||
$boxed-bg-title: 'boxed-bg-title';
|
||||
$boxed-bg: 'boxed-bg';
|
||||
$separator: 'separator';
|
||||
$over-bg: 'over-bg';
|
||||
$theme-toggle: 'theme-toggle';
|
||||
|
||||
$theme-map: null;
|
||||
@@ -112,6 +112,9 @@ body {
|
||||
}
|
||||
}
|
||||
}
|
||||
h1, h2 {
|
||||
font-weight: 100;
|
||||
}
|
||||
a {
|
||||
@include themed() {
|
||||
color: t($base7);
|
||||
@@ -306,7 +309,7 @@ a {
|
||||
width: 30%;
|
||||
margin-left: -0.6rem;
|
||||
@include themed() {
|
||||
background: t($separator);
|
||||
background: t($over-bg);
|
||||
}
|
||||
&.long {
|
||||
width: 50%;
|
||||
@@ -347,6 +350,36 @@ a {
|
||||
}
|
||||
}
|
||||
}
|
||||
&.last_notes {
|
||||
a {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
&.note {
|
||||
margin-bottom: 1rem;
|
||||
.published_at {
|
||||
font-size: 0.8rem;
|
||||
@include themed() {
|
||||
color: t($accent1);
|
||||
}
|
||||
}
|
||||
.content {
|
||||
max-height: 160px;
|
||||
overflow: hidden;
|
||||
&.gradient {
|
||||
-webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 50%, rgba(0, 0, 0, 0) 100%);
|
||||
mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 50%, rgba(0, 0, 0, 0) 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
a:hover {
|
||||
padding-left: 1rem;
|
||||
margin-left: -1.5rem;
|
||||
@include themed() {
|
||||
border-left: 0.5rem solid t($over-bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
&.advanced-switch-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -647,6 +680,7 @@ body.note, body.raw {
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 2rem;
|
||||
color: $color-base5;
|
||||
font-size: 0.8rem;
|
||||
text-align: center;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
<meta property="og:site_name" content="{{.authorLong | SanitizeString}}" />
|
||||
<meta property="og:title" content="{{.title | SanitizeString}}" />
|
||||
<meta name="twitter:title" content="{{.twitterTitle | SanitizeString}}" />
|
||||
<link rel="canonical" href="/{{.note | SanitizeString }}" />
|
||||
<!---->
|
||||
{{ if .textImageURL }}
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
@@ -61,5 +62,5 @@
|
||||
{{end}}
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="/njump/static/styles.css?v=20230528b" />
|
||||
<link rel="stylesheet" href="/njump/static/styles.css?v=20230529" />
|
||||
</head>
|
||||
|
||||
@@ -72,6 +72,21 @@
|
||||
{{template "details.html" .}}
|
||||
|
||||
<div class="field separator"></div>
|
||||
|
||||
<div class="field last_notes">
|
||||
<h2>Last Notes</h2>
|
||||
{{range .lastNotes}}
|
||||
<a href="/{{.Nevent}}" class="note">
|
||||
<div class="published_at">
|
||||
{{.CreatedAt}}
|
||||
</div>
|
||||
<div class="content">
|
||||
{{.Content}}
|
||||
</div>
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{template "column_clients.html" .}}
|
||||
|
||||
@@ -37,7 +37,6 @@ if (clients_wrapper !== null) {
|
||||
|
||||
let jsons = document.querySelectorAll('.json')
|
||||
for (let i = 0; i < jsons.length; i++) {
|
||||
console.log(jsons[i].innerHTML)
|
||||
jsons[i].innerHTML = syntaxHighlight(jsons[i].innerHTML)
|
||||
}
|
||||
|
||||
@@ -97,3 +96,13 @@ function syntaxHighlight(json) {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var contentDivs = document.getElementsByClassName('content');
|
||||
for (var i = 0; i < contentDivs.length; i++) {
|
||||
var contentDiv = contentDivs[i];
|
||||
if (contentDiv.offsetHeight == 160) {
|
||||
contentDiv.classList.add('gradient');
|
||||
}
|
||||
}
|
||||
});
|
||||
4
utils.go
4
utils.go
@@ -187,7 +187,7 @@ func ReplaceURLsWithTags(line string) string {
|
||||
regex := regexp.MustCompile(regexPattern)
|
||||
matches := regex.FindAllString(line, -1)
|
||||
for _, match := range matches {
|
||||
imgTag := fmt.Sprintf(`<img src="%s" alt="">`, strings.ReplaceAll(match, "\n", ""))
|
||||
imgTag := fmt.Sprintf(`<img src="%s" alt=""> `, strings.ReplaceAll(match, "\n", ""))
|
||||
line = strings.ReplaceAll(line, match, imgTag)
|
||||
return line
|
||||
}
|
||||
@@ -207,7 +207,7 @@ func ReplaceURLsWithTags(line string) string {
|
||||
}
|
||||
|
||||
// Match and replace npup1, nprofile1, note1, nevent1, etc
|
||||
nostrRegexPattern := `\s*nostr:((npub|note|nevent|nprofile)1[a-z0-9]+)\s*`
|
||||
nostrRegexPattern := `\S*nostr:((npub|note|nevent|nprofile)1[a-z0-9]+)\S*`
|
||||
nostrRegex := regexp.MustCompile(nostrRegexPattern)
|
||||
line = nostrRegex.ReplaceAllStringFunc(line, func(match string) string {
|
||||
submatch := nostrRegex.FindStringSubmatch(match)
|
||||
|
||||
Reference in New Issue
Block a user