mirror of
https://github.com/aljazceru/njump.git
synced 2025-12-17 22:34:25 +01:00
Setup up a first desktop version
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1,3 @@
|
|||||||
njump
|
njump
|
||||||
|
.air.toml
|
||||||
|
tmp/*
|
||||||
102
event.html
102
event.html
@@ -47,32 +47,89 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
<!----------->
|
<!----------->
|
||||||
|
|
||||||
<style>
|
<link rel="stylesheet" href="/static/styles.css" />
|
||||||
body {
|
|
||||||
padding: 3%;
|
|
||||||
}
|
|
||||||
#event {
|
|
||||||
margin: 5% 0;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-wrap: break-word;
|
|
||||||
word-break: break-all;
|
|
||||||
font-family: monospace;
|
|
||||||
font-size: 130%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div>
|
|
||||||
open {{.type}} in {{range .clients}}<br />
|
<div class="top">
|
||||||
<span style="color: #bbb"></span>
|
<a href="https://nostr.com" class="nostr_link">What is nostr?</a>
|
||||||
<a class="client" href="{{.url}}">{{.name}}</a> {{end}}
|
|
||||||
</div>
|
</div>
|
||||||
<div id="event">{{.eventJSON}}</div>
|
|
||||||
<div style="display: flex; justify-content: flex-end">
|
<div class="container_wrapper">
|
||||||
|
<div class="container profile">
|
||||||
|
<div class="column column1">
|
||||||
|
<div class="pic-wrapper">
|
||||||
|
<img class="pic" src="{{ .metadata.Picture }}" />
|
||||||
|
<svg width="0" height="0" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<clipPath id="svg-shape" clipPathUnits="objectBoundingBox">
|
||||||
|
<path
|
||||||
|
transform="scale(0.005, 0.005)"
|
||||||
|
d="M100,200c43.8,0,68.2,0,84.1-15.9C200,168.2,200,143.8,200,100s0-68.2-15.9-84.1C168.2,0,143.8,0,100,0S31.8,0,15.9,15.9C0,31.8,0,56.2,0,100s0,68.2,15.9,84.1C31.8,200,56.2,200,100,200z"
|
||||||
|
/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="last_update">
|
||||||
|
Last update:<br/>
|
||||||
|
{{.createdAt}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column column2">
|
||||||
|
<div class="field name">
|
||||||
|
{{.content_json.name}} <span class="display">{{.content_json.display_name}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="field separator"></div>
|
||||||
|
<div class="field">
|
||||||
|
<a href="{{.content_json.website}}">{{.content_json.website}}</a>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
{{.content_json.about}}
|
||||||
|
</div>
|
||||||
|
<div class="field separator"></div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="label">Public key</div>
|
||||||
|
{{.npub}}
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="label">NIP-05</div>
|
||||||
|
{{.content_json.nip05}}
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="label">LN Address</div>
|
||||||
|
{{.content_json.lud16}}
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="label">Metadata Event</div>
|
||||||
|
{{.nevent}}
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="label">JSON</div>
|
||||||
|
{{.eventJSON}}
|
||||||
|
</div>
|
||||||
|
<div class="field separator"></div>
|
||||||
|
</div>
|
||||||
|
<div class="column column3">
|
||||||
|
<div class="title">Open {{.type}} in</div>
|
||||||
|
<div class="clients">
|
||||||
|
{{range .clients}}
|
||||||
|
<div class="btn">
|
||||||
|
<a class="client" href="{{.url}}">{{.name}}</a>
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <div style="display: flex; justify-content: flex-end">
|
||||||
<span>
|
<span>
|
||||||
powered by <a href="https://git.fiatjaf.com/njump">njump</a>
|
powered by <a href="https://git.fiatjaf.com/njump">njump</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const type = '{{.type}}'
|
const type = '{{.type}}'
|
||||||
let counts = []
|
let counts = []
|
||||||
@@ -82,7 +139,7 @@
|
|||||||
let url = clients[i].href
|
let url = clients[i].href
|
||||||
let key = 'nj:' + type + ':' + name
|
let key = 'nj:' + type + ':' + name
|
||||||
let count = parseInt(localStorage.getItem(key) || 0)
|
let count = parseInt(localStorage.getItem(key) || 0)
|
||||||
clients[i].previousElementSibling.innerText = count
|
clients[i].nextElementSibling.innerText = count
|
||||||
clients[i].addEventListener('click', () => {
|
clients[i].addEventListener('click', () => {
|
||||||
localStorage.setItem(key, count + 1)
|
localStorage.setItem(key, count + 1)
|
||||||
})
|
})
|
||||||
@@ -97,5 +154,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<div class="background"></div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
3
main.go
3
main.go
@@ -7,6 +7,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
|
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
|
||||||
|
|
||||||
http.HandleFunc("/image/", generate)
|
http.HandleFunc("/image/", generate)
|
||||||
http.HandleFunc("/proxy/", proxy)
|
http.HandleFunc("/proxy/", proxy)
|
||||||
http.HandleFunc("/", render)
|
http.HandleFunc("/", render)
|
||||||
|
|||||||
16
render.go
16
render.go
@@ -8,7 +8,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
"time"
|
||||||
"github.com/nbd-wtf/go-nostr"
|
"github.com/nbd-wtf/go-nostr"
|
||||||
"github.com/nbd-wtf/go-nostr/nip19"
|
"github.com/nbd-wtf/go-nostr/nip19"
|
||||||
)
|
)
|
||||||
@@ -48,6 +48,7 @@ func render(w http.ResponseWriter, r *http.Request) {
|
|||||||
npub, _ := nip19.EncodePublicKey(event.PubKey)
|
npub, _ := nip19.EncodePublicKey(event.PubKey)
|
||||||
nevent, _ := nip19.EncodeEvent(event.ID, []string{}, event.PubKey)
|
nevent, _ := nip19.EncodeEvent(event.ID, []string{}, event.PubKey)
|
||||||
naddr := ""
|
naddr := ""
|
||||||
|
createdAt := time.Unix(int64(event.CreatedAt), 0).Format("2006-01-02 15:04:05")
|
||||||
|
|
||||||
author := event
|
author := event
|
||||||
if event.Kind != 0 {
|
if event.Kind != 0 {
|
||||||
@@ -142,21 +143,23 @@ func render(w http.ResponseWriter, r *http.Request) {
|
|||||||
// : ''
|
// : ''
|
||||||
|
|
||||||
textImageURL := ""
|
textImageURL := ""
|
||||||
description := ""
|
content := ""
|
||||||
|
content_json := make(map[string]interface{})
|
||||||
if useTextImage {
|
if useTextImage {
|
||||||
textImageURL = fmt.Sprintf("https://%s/image/%s", hostname, code)
|
textImageURL = fmt.Sprintf("https://%s/image/%s", hostname, code)
|
||||||
if subject != "" {
|
if subject != "" {
|
||||||
description = fmt.Sprintf("%s -- %s", subject, seenOnRelays)
|
content = fmt.Sprintf("%s -- %s", subject, seenOnRelays)
|
||||||
} else {
|
} else {
|
||||||
description = seenOnRelays
|
content = seenOnRelays
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
description = prettyJsonOrRaw(event.Content)
|
content_json, content = prettyJsonOrRaw(event.Content)
|
||||||
}
|
}
|
||||||
|
|
||||||
eventJSON, _ := json.MarshalIndent(event, "", " ")
|
eventJSON, _ := json.MarshalIndent(event, "", " ")
|
||||||
|
|
||||||
params := map[string]any{
|
params := map[string]any{
|
||||||
|
"createdAt": createdAt,
|
||||||
"clients": generateClientList(code, event),
|
"clients": generateClientList(code, event),
|
||||||
"type": typ,
|
"type": typ,
|
||||||
"title": title,
|
"title": title,
|
||||||
@@ -166,7 +169,8 @@ func render(w http.ResponseWriter, r *http.Request) {
|
|||||||
"naddr": naddr,
|
"naddr": naddr,
|
||||||
"metadata": metadata,
|
"metadata": metadata,
|
||||||
"authorLong": authorLong,
|
"authorLong": authorLong,
|
||||||
"description": description,
|
"content": content,
|
||||||
|
"content_json": content_json,
|
||||||
"textImageURL": textImageURL,
|
"textImageURL": textImageURL,
|
||||||
"videoType": videoType,
|
"videoType": videoType,
|
||||||
"image": image,
|
"image": image,
|
||||||
|
|||||||
167
static/styles.css
Normal file
167
static/styles.css
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
html {
|
||||||
|
font-family: Helvetica, sans-serif;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: #373737;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #373737;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: white;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
background-color: #FAFAFA;
|
||||||
|
width: 140%;
|
||||||
|
height: 100%;
|
||||||
|
transform: rotate(-20deg);
|
||||||
|
transform-origin: bottom;
|
||||||
|
bottom: -40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top, .container_wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top {
|
||||||
|
margin: 1rem 0;
|
||||||
|
justify-content: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nostr_link {
|
||||||
|
flex-basis: 15%;
|
||||||
|
text-align: right;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
width: 70%;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 4.8vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column {
|
||||||
|
}
|
||||||
|
|
||||||
|
.column1 {
|
||||||
|
flex-basis: 25%;
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.profile .column1 .pic-wrapper {
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.profile .column1 img.pic {
|
||||||
|
max-width: 100%;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
-webkit-clip-path: url(#svg-shape);
|
||||||
|
-moz-clip-path: url(#svg-shape);
|
||||||
|
-o-clip-path: url(#svg-shape);
|
||||||
|
-ms-clip-path: url(#svg-shape);
|
||||||
|
clip-path: url(#svg-shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
.last_update {
|
||||||
|
font-size: 0.8em;
|
||||||
|
text-align: center;
|
||||||
|
color: #C9C9C9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column2 {
|
||||||
|
flex-basis: 50%;
|
||||||
|
max-width: 50%;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
word-wrap: break-word;
|
||||||
|
margin-right: 1vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column2 .name {
|
||||||
|
font-size: 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column2 .name .display {
|
||||||
|
color: #C9C9C9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column2 .separator {
|
||||||
|
height: 6px;
|
||||||
|
width: 30%;
|
||||||
|
margin-left: -0.6rem;
|
||||||
|
background-color: #F3F3F3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column2 .field {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column2 .field .label {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #E32A6D;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column3 {
|
||||||
|
flex-basis: 25%;
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column3 .title {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column3 .btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column3 .btn a {
|
||||||
|
flex-basis: 80%;
|
||||||
|
padding: 0.4rem;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #FFFFFF;
|
||||||
|
background-color: #5A5A5A;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column3 .btn a:hover {
|
||||||
|
background-color: #373737;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column3 .btn:first-child a {
|
||||||
|
background-color: #E32A6D;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column3 .btn:first-child a:hover {
|
||||||
|
background-color: #bc1150;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column3 .btn span {
|
||||||
|
flex-basis: 20%;
|
||||||
|
margin-left: 0.4rem;
|
||||||
|
color: #9a9a9a;
|
||||||
|
}
|
||||||
8
utils.go
8
utils.go
@@ -81,14 +81,14 @@ func mergeMaps[K comparable, V any](m1 map[K]V, m2 map[K]V) map[K]V {
|
|||||||
return m1
|
return m1
|
||||||
}
|
}
|
||||||
|
|
||||||
func prettyJsonOrRaw(j string) string {
|
func prettyJsonOrRaw(j string) (map[string]interface{}, string) {
|
||||||
var parsedContent any
|
var parsedContent map[string]interface{}
|
||||||
if err := json.Unmarshal([]byte(j), &parsedContent); err == nil {
|
if err := json.Unmarshal([]byte(j), &parsedContent); err == nil {
|
||||||
if t, err := toml.Marshal(parsedContent); err == nil && len(t) > 0 {
|
if t, err := toml.Marshal(parsedContent); err == nil && len(t) > 0 {
|
||||||
return string(t)
|
return parsedContent, string(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return j
|
return nil, j
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPreviewStyle(r *http.Request) string {
|
func getPreviewStyle(r *http.Request) string {
|
||||||
|
|||||||
Reference in New Issue
Block a user