mirror of
https://github.com/aljazceru/njump.git
synced 2025-12-17 06:14:22 +01:00
moving some code around to show a "(reply)" on opengraph subscripts.
This commit is contained in:
21
data.go
21
data.go
@@ -8,15 +8,13 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/nbd-wtf/go-nostr"
|
|
||||||
"github.com/nbd-wtf/go-nostr/nip19"
|
"github.com/nbd-wtf/go-nostr/nip19"
|
||||||
sdk "github.com/nbd-wtf/nostr-sdk"
|
sdk "github.com/nbd-wtf/nostr-sdk"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Data struct {
|
type Data struct {
|
||||||
templateId TemplateID
|
templateId TemplateID
|
||||||
event *nostr.Event
|
event EnhancedEvent
|
||||||
relays []string
|
|
||||||
nprofile string
|
nprofile string
|
||||||
nevent string
|
nevent string
|
||||||
neventNaked string
|
neventNaked string
|
||||||
@@ -28,7 +26,6 @@ type Data struct {
|
|||||||
metadata Metadata
|
metadata Metadata
|
||||||
authorRelays []string
|
authorRelays []string
|
||||||
authorLong string
|
authorLong string
|
||||||
authorShort string
|
|
||||||
renderableLastNotes []EnhancedEvent
|
renderableLastNotes []EnhancedEvent
|
||||||
kindDescription string
|
kindDescription string
|
||||||
kindNIP string
|
kindNIP string
|
||||||
@@ -64,14 +61,15 @@ func grabData(ctx context.Context, code string, isProfileSitemap bool) (*Data, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
data := &Data{
|
data := &Data{
|
||||||
event: event,
|
event: EnhancedEvent{
|
||||||
relays: relays,
|
Event: event,
|
||||||
|
relays: relays,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
npub, _ := nip19.EncodePublicKey(event.PubKey)
|
npub, _ := nip19.EncodePublicKey(event.PubKey)
|
||||||
npubShort := npub[:8] + "…" + npub[len(npub)-4:]
|
npubShort := npub[:8] + "…" + npub[len(npub)-4:]
|
||||||
data.authorLong = npub // hopefully will be replaced later
|
data.authorLong = npub // hopefully will be replaced later
|
||||||
data.authorShort = 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 = ""
|
||||||
@@ -193,7 +191,7 @@ func grabData(ctx context.Context, code string, isProfileSitemap bool) (*Data, e
|
|||||||
pTags := event.Tags.GetAll([]string{"p", ""})
|
pTags := event.Tags.GetAll([]string{"p", ""})
|
||||||
for _, p := range pTags {
|
for _, p := range pTags {
|
||||||
if p[3] == "host" {
|
if p[3] == "host" {
|
||||||
data.kind30311Metadata.Host = sdk.FetchProfileMetadata(ctx, pool, p[1], data.relays...)
|
data.kind30311Metadata.Host = sdk.FetchProfileMetadata(ctx, pool, p[1], data.event.relays...)
|
||||||
data.kind30311Metadata.HostNpub = data.kind30311Metadata.Host.Npub()
|
data.kind30311Metadata.HostNpub = data.kind30311Metadata.Host.Npub()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,7 +206,7 @@ func grabData(ctx context.Context, code string, isProfileSitemap bool) (*Data, e
|
|||||||
if atag := event.Tags.GetFirst([]string{"a", ""}); atag != nil {
|
if atag := event.Tags.GetFirst([]string{"a", ""}); atag != nil {
|
||||||
parts := strings.Split((*atag)[1], ":")
|
parts := strings.Split((*atag)[1], ":")
|
||||||
kind, _ := strconv.Atoi(parts[0])
|
kind, _ := strconv.Atoi(parts[0])
|
||||||
parentNevent, _ := nip19.EncodeEntity(parts[1], kind, parts[2], data.relays)
|
parentNevent, _ := nip19.EncodeEntity(parts[1], kind, parts[2], data.event.relays)
|
||||||
data.parentLink = template.HTML(replaceNostrURLsWithTags(nostrEveryMatcher, "nostr:"+parentNevent))
|
data.parentLink = template.HTML(replaceNostrURLsWithTags(nostrEveryMatcher, "nostr:"+parentNevent))
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -229,8 +227,7 @@ func grabData(ctx context.Context, code string, isProfileSitemap bool) (*Data, e
|
|||||||
spm, _ := sdk.ParseMetadata(author)
|
spm, _ := sdk.ParseMetadata(author)
|
||||||
data.metadata = Metadata{spm}
|
data.metadata = Metadata{spm}
|
||||||
if data.metadata.Name != "" {
|
if data.metadata.Name != "" {
|
||||||
data.authorLong = fmt.Sprintf("%s (%s)", data.metadata.Name, npub)
|
data.authorLong = fmt.Sprintf("%s (%s)", data.metadata.Name, npubShort)
|
||||||
data.authorShort = fmt.Sprintf("%s (%s)", data.metadata.Name, npubShort)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data.nprofile, _ = nip19.EncodeProfile(event.PubKey, limitAt(relays, 2))
|
data.nprofile, _ = nip19.EncodeProfile(event.PubKey, limitAt(relays, 2))
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ func renderEvent(w http.ResponseWriter, r *http.Request) {
|
|||||||
useTextImage = false
|
useTextImage = false
|
||||||
}
|
}
|
||||||
|
|
||||||
title := ""
|
subscript := ""
|
||||||
if data.event.Kind >= 30000 && data.event.Kind < 40000 {
|
if data.event.Kind >= 30000 && data.event.Kind < 40000 {
|
||||||
tValue := "~"
|
tValue := "~"
|
||||||
for _, tag := range data.event.Tags {
|
for _, tag := range data.event.Tags {
|
||||||
@@ -150,20 +150,23 @@ func renderEvent(w http.ResponseWriter, r *http.Request) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
title = fmt.Sprintf("%s: %s", kindNames[data.event.Kind], tValue)
|
subscript = fmt.Sprintf("%s: %s", kindNames[data.event.Kind], tValue)
|
||||||
} else if kindName, ok := kindNames[data.event.Kind]; ok {
|
} else if kindName, ok := kindNames[data.event.Kind]; ok {
|
||||||
title = kindName
|
subscript = kindName
|
||||||
} else {
|
} else {
|
||||||
title = fmt.Sprintf("kind:%d event", data.event.Kind)
|
subscript = fmt.Sprintf("kind:%d event", data.event.Kind)
|
||||||
}
|
}
|
||||||
if subject != "" {
|
if subject != "" {
|
||||||
title += " (" + subject + ")"
|
subscript += " (" + subject + ")"
|
||||||
|
}
|
||||||
|
subscript += " by " + data.metadata.ShortName()
|
||||||
|
if data.event.IsReply() {
|
||||||
|
subscript += " (reply)"
|
||||||
}
|
}
|
||||||
title += " by " + data.authorShort
|
|
||||||
|
|
||||||
seenOnRelays := ""
|
seenOnRelays := ""
|
||||||
if len(data.relays) > 0 {
|
if len(data.event.relays) > 0 {
|
||||||
seenOnRelays = fmt.Sprintf("seen on %s", strings.Join(data.relays, ", "))
|
seenOnRelays = fmt.Sprintf("seen on %s", strings.Join(data.event.relays, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
textImageURL := ""
|
textImageURL := ""
|
||||||
@@ -212,7 +215,7 @@ func renderEvent(w http.ResponseWriter, r *http.Request) {
|
|||||||
titleizedContent = urlRegex.ReplaceAllString(titleizedContent, "")
|
titleizedContent = urlRegex.ReplaceAllString(titleizedContent, "")
|
||||||
|
|
||||||
if titleizedContent == "" {
|
if titleizedContent == "" {
|
||||||
titleizedContent = title
|
titleizedContent = subscript
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(titleizedContent) > 85 {
|
if len(titleizedContent) > 85 {
|
||||||
@@ -283,9 +286,9 @@ func renderEvent(w http.ResponseWriter, r *http.Request) {
|
|||||||
CreatedAt: data.createdAt,
|
CreatedAt: data.createdAt,
|
||||||
KindDescription: data.kindDescription,
|
KindDescription: data.kindDescription,
|
||||||
KindNIP: data.kindNIP,
|
KindNIP: data.kindNIP,
|
||||||
EventJSON: eventToHTML(data.event),
|
EventJSON: data.event.ToJSONHTML(),
|
||||||
Kind: data.event.Kind,
|
Kind: data.event.Kind,
|
||||||
SeenOn: data.relays,
|
SeenOn: data.event.relays,
|
||||||
Metadata: data.metadata,
|
Metadata: data.metadata,
|
||||||
|
|
||||||
// kind-specific stuff
|
// kind-specific stuff
|
||||||
@@ -301,7 +304,7 @@ func renderEvent(w http.ResponseWriter, r *http.Request) {
|
|||||||
ProxiedImage: "https://" + host + "/njump/proxy?src=" + data.image,
|
ProxiedImage: "https://" + host + "/njump/proxy?src=" + data.image,
|
||||||
|
|
||||||
Superscript: data.authorLong,
|
Superscript: data.authorLong,
|
||||||
Subscript: title,
|
Subscript: subscript,
|
||||||
Text: strings.TrimSpace(description),
|
Text: strings.TrimSpace(description),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ func renderProfile(w http.ResponseWriter, r *http.Request, code string) {
|
|||||||
CreatedAt: data.createdAt,
|
CreatedAt: data.createdAt,
|
||||||
KindDescription: data.kindDescription,
|
KindDescription: data.kindDescription,
|
||||||
KindNIP: data.kindNIP,
|
KindNIP: data.kindNIP,
|
||||||
EventJSON: eventToHTML(data.event),
|
EventJSON: data.event.ToJSONHTML(),
|
||||||
Kind: data.event.Kind,
|
Kind: data.event.Kind,
|
||||||
Metadata: data.metadata,
|
Metadata: data.metadata,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -32,12 +32,16 @@ templ telegramInstantViewTemplate(params TelegramInstantViewParams) {
|
|||||||
} else {
|
} else {
|
||||||
<a href={ templ.URL("/" + params.Metadata.Npub()) }>
|
<a href={ templ.URL("/" + params.Metadata.Npub()) }>
|
||||||
{ params.Metadata.ShortName() }
|
{ params.Metadata.ShortName() }
|
||||||
</a> on Nostr:
|
</a> on Nostr
|
||||||
|
if params.ParentLink != "" {
|
||||||
|
(reply)
|
||||||
|
}
|
||||||
|
:
|
||||||
}
|
}
|
||||||
</h1>
|
</h1>
|
||||||
if params.ParentLink != "" {
|
if params.ParentLink != "" {
|
||||||
<aside>
|
<aside>
|
||||||
in reply to
|
in reply to{ " " }
|
||||||
@templ.Raw(params.ParentLink)
|
@templ.Raw(params.ParentLink)
|
||||||
</aside>
|
</aside>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"html"
|
"html"
|
||||||
"html/template"
|
"html/template"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -30,20 +32,20 @@ func (m Metadata) NpubShort() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type EnhancedEvent struct {
|
type EnhancedEvent struct {
|
||||||
event *nostr.Event
|
*nostr.Event
|
||||||
relays []string
|
relays []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ee EnhancedEvent) IsReply() bool {
|
func (ee EnhancedEvent) IsReply() bool {
|
||||||
return nip10.GetImmediateReply(ee.event.Tags) != nil
|
return nip10.GetImmediateReply(ee.Event.Tags) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ee EnhancedEvent) Reply() *nostr.Tag {
|
func (ee EnhancedEvent) Reply() *nostr.Tag {
|
||||||
return nip10.GetImmediateReply(ee.event.Tags)
|
return nip10.GetImmediateReply(ee.Event.Tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ee EnhancedEvent) Preview() template.HTML {
|
func (ee EnhancedEvent) Preview() template.HTML {
|
||||||
lines := strings.Split(html.EscapeString(ee.event.Content), "\n")
|
lines := strings.Split(html.EscapeString(ee.Event.Content), "\n")
|
||||||
var processedLines []string
|
var processedLines []string
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
if strings.TrimSpace(line) == "" {
|
if strings.TrimSpace(line) == "" {
|
||||||
@@ -88,11 +90,11 @@ func (ee EnhancedEvent) RssTitle() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ee EnhancedEvent) RssContent() string {
|
func (ee EnhancedEvent) RssContent() string {
|
||||||
content := ee.event.Content
|
content := ee.Event.Content
|
||||||
content = basicFormatting(html.EscapeString(content), true, false, false)
|
content = basicFormatting(html.EscapeString(content), true, false, false)
|
||||||
content = renderQuotesAsHTML(context.Background(), content, false)
|
content = renderQuotesAsHTML(context.Background(), content, false)
|
||||||
if ee.IsReply() {
|
if ee.IsReply() {
|
||||||
nevent, _ := nip19.EncodeEvent(ee.Reply().Value(), ee.relays, ee.event.PubKey)
|
nevent, _ := nip19.EncodeEvent(ee.Reply().Value(), ee.relays, ee.Event.PubKey)
|
||||||
neventShort := nevent[:8] + "…" + nevent[len(nevent)-4:]
|
neventShort := nevent[:8] + "…" + nevent[len(nevent)-4:]
|
||||||
content = "In reply to <a href='/" + nevent + "'>" + neventShort + "</a><br/>_________________________<br/><br/>" + content
|
content = "In reply to <a href='/" + nevent + "'>" + neventShort + "</a><br/>_________________________<br/><br/>" + content
|
||||||
}
|
}
|
||||||
@@ -101,7 +103,7 @@ func (ee EnhancedEvent) RssContent() string {
|
|||||||
|
|
||||||
func (ee EnhancedEvent) Thumb() string {
|
func (ee EnhancedEvent) Thumb() string {
|
||||||
imgRegex := regexp.MustCompile(`(https?://[^\s]+\.(?:png|jpe?g|gif|bmp|svg)(?:/[^\s]*)?)`)
|
imgRegex := regexp.MustCompile(`(https?://[^\s]+\.(?:png|jpe?g|gif|bmp|svg)(?:/[^\s]*)?)`)
|
||||||
matches := imgRegex.FindAllStringSubmatch(ee.event.Content, -1)
|
matches := imgRegex.FindAllStringSubmatch(ee.Event.Content, -1)
|
||||||
if len(matches) > 0 {
|
if len(matches) > 0 {
|
||||||
// The first match group captures the image URL
|
// The first match group captures the image URL
|
||||||
return matches[0][1]
|
return matches[0][1]
|
||||||
@@ -110,7 +112,7 @@ func (ee EnhancedEvent) Thumb() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ee EnhancedEvent) Npub() string {
|
func (ee EnhancedEvent) Npub() string {
|
||||||
npub, _ := nip19.EncodePublicKey(ee.event.PubKey)
|
npub, _ := nip19.EncodePublicKey(ee.Event.PubKey)
|
||||||
return npub
|
return npub
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,16 +122,59 @@ func (ee EnhancedEvent) NpubShort() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ee EnhancedEvent) Nevent() string {
|
func (ee EnhancedEvent) Nevent() string {
|
||||||
nevent, _ := nip19.EncodeEvent(ee.event.ID, ee.relays, ee.event.PubKey)
|
nevent, _ := nip19.EncodeEvent(ee.Event.ID, ee.relays, ee.Event.PubKey)
|
||||||
return nevent
|
return nevent
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ee EnhancedEvent) CreatedAtStr() string {
|
func (ee EnhancedEvent) CreatedAtStr() string {
|
||||||
return time.Unix(int64(ee.event.CreatedAt), 0).Format("2006-01-02 15:04:05")
|
return time.Unix(int64(ee.Event.CreatedAt), 0).Format("2006-01-02 15:04:05")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ee EnhancedEvent) ModifiedAtStr() string {
|
func (ee EnhancedEvent) ModifiedAtStr() string {
|
||||||
return time.Unix(int64(ee.event.CreatedAt), 0).Format("2006-01-02T15:04:05Z07:00")
|
return time.Unix(int64(ee.Event.CreatedAt), 0).Format("2006-01-02T15:04:05Z07:00")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ee EnhancedEvent) ToJSONHTML() template.HTML {
|
||||||
|
tagsHTML := "["
|
||||||
|
for t, tag := range ee.Tags {
|
||||||
|
tagsHTML += "\n ["
|
||||||
|
for i, item := range tag {
|
||||||
|
cls := `"text-zinc-500 dark:text-zinc-50"`
|
||||||
|
if i == 0 {
|
||||||
|
cls = `"text-amber-500 dark:text-amber-200"`
|
||||||
|
}
|
||||||
|
itemJSON, _ := json.Marshal(item)
|
||||||
|
tagsHTML += "\n <span class=" + cls + ">" + html.EscapeString(string(itemJSON))
|
||||||
|
if i < len(tag)-1 {
|
||||||
|
tagsHTML += ","
|
||||||
|
} else {
|
||||||
|
tagsHTML += "\n "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tagsHTML += "]"
|
||||||
|
if t < len(ee.Tags)-1 {
|
||||||
|
tagsHTML += ","
|
||||||
|
} else {
|
||||||
|
tagsHTML += "\n "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tagsHTML += "]"
|
||||||
|
|
||||||
|
contentJSON, _ := json.Marshal(ee.Content)
|
||||||
|
|
||||||
|
keyCls := "text-purple-700 dark:text-purple-300"
|
||||||
|
|
||||||
|
return template.HTML(fmt.Sprintf(
|
||||||
|
`{
|
||||||
|
<span class="`+keyCls+`">"id":</span> <span class="text-zinc-500 dark:text-zinc-50">"%s"</span>,
|
||||||
|
<span class="`+keyCls+`">"pubkey":</span> <span class="text-zinc-500 dark:text-zinc-50">"%s"</span>,
|
||||||
|
<span class="`+keyCls+`">"created_at":</span> <span class="text-green-600">%d</span>,
|
||||||
|
<span class="`+keyCls+`">"kind":</span> <span class="text-amber-500 dark:text-amber-200">%d</span>,
|
||||||
|
<span class="`+keyCls+`">"tags":</span> %s,
|
||||||
|
<span class="`+keyCls+`">"content":</span> <span class="text-zinc-500 dark:text-zinc-50">%s</span>,
|
||||||
|
<span class="`+keyCls+`">"sig":</span> <span class="text-zinc-500 dark:text-zinc-50 content">"%s"</span>
|
||||||
|
}`, ee.ID, ee.PubKey, ee.CreatedAt, ee.Kind, tagsHTML, html.EscapeString(string(contentJSON)), ee.Sig),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Kind1063Metadata struct {
|
type Kind1063Metadata struct {
|
||||||
|
|||||||
46
utils.go
46
utils.go
@@ -3,10 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"html"
|
|
||||||
"html/template"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -431,49 +428,6 @@ func normalizeWebsiteURL(u string) string {
|
|||||||
return "https://" + u
|
return "https://" + u
|
||||||
}
|
}
|
||||||
|
|
||||||
func eventToHTML(evt *nostr.Event) template.HTML {
|
|
||||||
tagsHTML := "["
|
|
||||||
for t, tag := range evt.Tags {
|
|
||||||
tagsHTML += "\n ["
|
|
||||||
for i, item := range tag {
|
|
||||||
cls := `"text-zinc-500 dark:text-zinc-50"`
|
|
||||||
if i == 0 {
|
|
||||||
cls = `"text-amber-500 dark:text-amber-200"`
|
|
||||||
}
|
|
||||||
itemJSON, _ := json.Marshal(item)
|
|
||||||
tagsHTML += "\n <span class=" + cls + ">" + html.EscapeString(string(itemJSON))
|
|
||||||
if i < len(tag)-1 {
|
|
||||||
tagsHTML += ","
|
|
||||||
} else {
|
|
||||||
tagsHTML += "\n "
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tagsHTML += "]"
|
|
||||||
if t < len(evt.Tags)-1 {
|
|
||||||
tagsHTML += ","
|
|
||||||
} else {
|
|
||||||
tagsHTML += "\n "
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tagsHTML += "]"
|
|
||||||
|
|
||||||
contentJSON, _ := json.Marshal(evt.Content)
|
|
||||||
|
|
||||||
keyCls := "text-purple-700 dark:text-purple-300"
|
|
||||||
|
|
||||||
return template.HTML(fmt.Sprintf(
|
|
||||||
`{
|
|
||||||
<span class="`+keyCls+`">"id":</span> <span class="text-zinc-500 dark:text-zinc-50">"%s"</span>,
|
|
||||||
<span class="`+keyCls+`">"pubkey":</span> <span class="text-zinc-500 dark:text-zinc-50">"%s"</span>,
|
|
||||||
<span class="`+keyCls+`">"created_at":</span> <span class="text-green-600">%d</span>,
|
|
||||||
<span class="`+keyCls+`">"kind":</span> <span class="text-amber-500 dark:text-amber-200">%d</span>,
|
|
||||||
<span class="`+keyCls+`">"tags":</span> %s,
|
|
||||||
<span class="`+keyCls+`">"content":</span> <span class="text-zinc-500 dark:text-zinc-50">%s</span>,
|
|
||||||
<span class="`+keyCls+`">"sig":</span> <span class="text-zinc-500 dark:text-zinc-50 content">"%s"</span>
|
|
||||||
}`, evt.ID, evt.PubKey, evt.CreatedAt, evt.Kind, tagsHTML, html.EscapeString(string(contentJSON)), evt.Sig),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func limitAt[V any](list []V, n int) []V {
|
func limitAt[V any](list []V, n int) []V {
|
||||||
if len(list) < n {
|
if len(list) < n {
|
||||||
return list
|
return list
|
||||||
|
|||||||
Reference in New Issue
Block a user