tinyrelayfile
tinyrelay/tinyclient/social.html/

tinyclient/social.html raw
1{{define "social"}}<section id="social">
2<header id="social-head"><h1>Social</h1><p>Notes, stories and conversations, newest first.</p></header>
3{{if .Error}}{{template "alert" .Error}}{{else}}
4{{if .Actor}}{{template "socialComposer" .}}{{else}}<p data-social-welcome><a href="{{signinURL .Base .Path .Query}}">Sign in</a> to join the conversation.</p>{{end}}
5<nav id="social-views" aria-label="Social feed"><a href="{{socialQuery .Query "kind" ""}}"{{if eq (.Query.Get "kind") ""}} aria-current="page"{{end}}>All posts</a><a href="{{socialQuery .Query "kind" "notes"}}"{{if eq (.Query.Get "kind") "notes"}} aria-current="page"{{end}}>Notes</a><a href="{{socialQuery .Query "kind" "articles"}}"{{if eq (.Query.Get "kind") "articles"}} aria-current="page"{{end}}>Articles</a></nav>
6{{if .Feed}}<ol id="social-feed">{{range .Feed}}{{template "socialPost" (socialContext . $)}}{{end}}</ol>
7{{else}}<div id="social-empty"><h2>{{if .Query.Get "q"}}No matching posts{{else}}A place to share what’s on your mind{{end}}</h2><p>{{if .Query.Get "q"}}Try another search or clear the filters.{{else}}Post a quick note or take your time with an article. Conversations grow from here.{{end}}</p></div>{{end}}
8{{with index (asMap .Event) "next_cursor"}}<p id="social-pages"><a href="{{socialQuery $.Query "cursor" (str .)}}">Older posts →</a></p>{{end}}
9{{end}}<noscript><p>Reading works without JavaScript. Publishing needs JavaScript and a connected signer.</p></noscript>
10</section>{{end}}
11
12{{define "social-thread"}}{{$post := socialContext (index (asMap .Event) "post") .}}<section id="social-thread">
13<p data-social-back><a href="/social">← Social</a></p>
14{{if .Error}}{{template "alert" .Error}}{{else}}
15{{if index $post "parent_url"}}<p data-thread-context><a href="{{index $post "parent_url"}}">↑ Parent post</a>{{if ne (index $post "parent_url") (index $post "root_url")}}{{with index $post "root_url"}} <a href="{{.}}">View full conversation</a>{{end}}{{end}}</p>{{end}}
16<ol id="social-thread-post">{{template "socialPost" $post}}</ol>
17<div id="social-comments"><h2>Conversation{{with index $post "reply_count"}} <small>{{.}}</small>{{end}}</h2>
18{{if .Actor}}{{template "socialCommentComposer" $post}}{{else}}<p><a href="{{signinURL .Base .Path .Query}}">Sign in</a> to reply.</p>{{end}}
19{{with index (asMap .Event) "comments"}}<ol>{{range .}}{{template "socialComment" (socialContext . $)}}{{end}}</ol>{{else}}<p data-social-empty-replies>No replies yet. Start the conversation.</p>{{end}}
20{{with index (asMap .Event) "next_cursor"}}<p><a href="{{socialThreadPage $.Path $.Query (str .)}}">Older replies →</a></p>{{end}}
21</div>{{end}}</section>{{end}}
22
23{{define "socialPost"}}{{$p := asMap .}}<li data-social-post data-kind="{{str (index $p "kind")}}">
24{{template "socialByline" $p}}
25{{if eq (str (index $p "kind")) "30023"}}
26<article data-post-body="article">
27{{if index $p "detail"}}<h1>{{or (str (index $p "title")) "Untitled article"}}</h1>{{else}}<h2><a href="{{socialURL $p}}">{{or (str (index $p "title")) "Untitled article"}}</a></h2>{{end}}
28{{with index $p "summary"}}<p data-post-summary>{{str .}}</p>{{else}}{{if not (index $p "detail")}}<p data-post-summary>{{socialPreview $p}}</p>{{end}}{{end}}
29{{with socialMediaURL (str (index $p "image"))}}<a href="{{socialURL $p}}" data-post-media><img src="{{.}}" alt="{{or (str (index $p "title")) "Article cover"}}" loading="lazy" referrerpolicy="no-referrer"></a>{{end}}
30{{if index $p "detail"}}<div data-article-content>{{socialBody $p}}</div>{{end}}
31{{with index $p "read_minutes"}}<small data-read-time>{{.}} min read{{if not (index $p "detail")}} <a href="{{socialURL $p}}">Read article →</a>{{end}}</small>{{end}}
32</article>
33{{else}}<article data-post-body="note">{{socialBody $p}}</article>{{end}}
34{{template "socialActions" $p}}
35</li>{{end}}
36
37{{define "socialByline"}}{{$p := asMap .}}<header data-social-byline><span data-social-avatar><nostr-avatar pubkey="{{index $p "pubkey"}}" aria-hidden="true">{{with socialMediaURL (str (index $p "author_picture"))}}<img src="{{.}}" alt="" loading="lazy" width="36" height="36" referrerpolicy="no-referrer">{{else}}<img src="{{avatarURL (str (index $p "pubkey"))}}" alt="" width="36" height="36" loading="lazy">{{end}}</nostr-avatar></span><span><a href="/social?author={{str (index $p "pubkey")}}"><b>{{if index $p "author_name"}}{{str (index $p "author_name")}}{{else}}{{template "nostrName" (str (index $p "pubkey"))}}{{end}}</b></a> <a href="{{socialURL $p}}"><time datetime="{{datetime (index $p "created_at")}}">{{when (index $p "created_at")}}</time></a>{{if eq (str (index $p "kind")) "30023"}} <small>Article</small>{{end}}{{with index $p "parent_url"}}<small data-social-parent>↳ <a href="{{.}}">Replying{{if index $p "parent_author"}} to {{template "nostrName" (str (index $p "parent_author"))}}{{else}} to a post{{end}}</a></small>{{end}}</span></header>{{end}}
38
39{{define "socialActions"}}{{$p := asMap .}}<footer data-social-actions>
40<a href="{{socialURL $p}}#reply">{{if index $p "reply_count"}}{{index $p "reply_count"}} {{if eq (str (index $p "reply_count")) "1"}}reply{{else}}replies{{end}}{{else}}Reply{{end}}</a>
41{{if index $p "viewer"}}<social-reaction actor="{{index $p "viewer"}}" event="{{index $p "id"}}" pubkey="{{index $p "pubkey"}}" kind="{{index $p "kind"}}" address="{{index $p "address"}}" relay="{{index $p "relay"}}" content="+"><button type="button" aria-label="Like this post" aria-pressed="{{socialReacted $p "+"}}"{{if socialReacted $p "+"}} disabled{{end}}>♥ {{socialReactionCount $p "+"}}</button></social-reaction>{{else}}<span aria-label="Likes">♥ {{socialReactionCount $p "+"}}</span>{{end}}
42{{range index $p "reactions"}}{{$r := asMap .}}{{if ne (str (index $r "content")) "+"}}<span title="Reaction">{{with socialMediaURL (str (index $r "image"))}}<img data-reaction-emoji src="{{.}}" alt="{{index $r "content"}}" width="20" height="20" loading="lazy" referrerpolicy="no-referrer">{{else}}{{str (index $r "content")}}{{end}} {{index $r "count"}}</span>{{end}}{{end}}
43{{if index $p "viewer"}}<details data-social-react><summary>React</summary>{{range strs "👍" "❤️" "😂" "🎉" "🤔"}}<social-reaction actor="{{index $p "viewer"}}" event="{{index $p "id"}}" pubkey="{{index $p "pubkey"}}" kind="{{index $p "kind"}}" address="{{index $p "address"}}" relay="{{index $p "relay"}}" content="{{.}}"><button type="button" aria-label="React {{.}}" aria-pressed="{{socialReacted $p .}}"{{if socialReacted $p .}} disabled{{end}}>{{.}}</button></social-reaction>{{end}}</details>{{end}}
44{{if index $p "detail"}}<share-link url="{{socialURL $p}}"></share-link>{{else}}<a href="{{socialURL $p}}">Open</a>{{end}}
45</footer>{{end}}
46
47{{define "socialComposer"}}{{$article := eq (.Query.Get "compose") "article"}}
48<nav id="social-write" aria-label="Write"><a href="{{socialQuery .Query "compose" ""}}"{{if not $article}} aria-current="page"{{end}}>Write a note</a><a href="{{socialQuery .Query "compose" "article"}}"{{if $article}} aria-current="page"{{end}}>Write an article</a></nav>
49<social-compose mode="{{if $article}}article{{else}}note{{end}}" actor="{{.Actor}}" relay="{{wsURL .URL}}" refresh="/social">
50{{if $article}}<label>Title <input name="title" required maxlength="200" placeholder="Give your story a title"></label><label>Subtitle <input name="summary" maxlength="500" placeholder="A short introduction"></label>{{end}}
51<label>{{if $article}}Article{{else}}Note{{end}} <textarea name="content" rows="{{if $article}}14{{else}}3{{end}}" maxlength="65000" placeholder="{{if $article}}Tell your story. Markdown is supported.{{else}}What’s on your mind?{{end}}"></textarea></label>
52{{if $article}}<label>Cover image URL <input name="cover" type="url" placeholder="https://"></label>{{end}}
53{{template "socialMediaFields" .}}
54<details data-post-options><summary>Topics</summary><label>Tags <input name="tags" placeholder="nature, design, notes"></label></details>
55<p data-compose-actions><button type="submit">{{if $article}}Publish article{{else}}Post note{{end}}</button> <button type="button" data-preview>Preview</button> <small>Draft saved in this browser.</small></p><div data-preview-output hidden></div>
56</social-compose>{{end}}
57
58{{define "socialMediaFields"}}<details data-post-options><summary>Attach media</summary><label>Images, video or audio <input type="file" name="media-file" accept="image/*,video/*,audio/*" multiple></label><label>Or paste a media URL <input name="media" type="url" placeholder="https://"></label><label>Description <input name="alt" maxlength="1000" placeholder="Describe the media for readers"></label><small>Attachments are published with your post.</small></details>{{end}}
59
60{{define "socialCommentComposer"}}{{$p := asMap .}}<social-compose id="reply" mode="comment" actor="{{index $p "viewer"}}" relay="{{index $p "relay"}}" target="{{index $p "id"}}" target-kind="{{index $p "kind"}}" target-pubkey="{{index $p "pubkey"}}" target-tags="{{json (index $p "tags")}}" address="{{or (str (index $p "root_address")) (str (index $p "address"))}}" root="{{or (str (index $p "root_id")) (str (index $p "id"))}}" root-pubkey="{{str (index $p "root_author")}}" root-kind="{{or (str (index $p "root_kind")) (str (index $p "kind"))}}" refresh="{{socialURL $p}}">
61<label>Reply <textarea name="content" rows="3" maxlength="20000" placeholder="Add to the conversation"></textarea></label>{{template "socialMediaFields" .}}<p data-compose-actions><button type="submit">Post reply</button> <button type="button" data-preview>Preview</button></p><div data-preview-output hidden></div>
62</social-compose>{{end}}
63
64{{define "socialComment"}}{{$c := asMap .}}<li data-social-comment{{if index $c "nested"}} data-nested-reply{{end}}>{{template "socialByline" $c}}<div data-comment-body>{{socialBody $c}}</div>{{template "socialActions" $c}}</li>{{end}}
65
66{{define "panel-social"}}{{$stats := asMap (index (asMap .Event) "stats")}}<section><h4>{{if eq .Tab "social"}}In this feed{{else}}Conversation{{end}}</h4>{{if eq .Tab "social"}}<table><tr><th>notes</th><td>{{or (index $stats "notes") 0}}</td></tr><tr><th>articles</th><td>{{or (index $stats "articles") 0}}</td></tr><tr><th>replies</th><td>{{or (index $stats "comments") 0}}</td></tr></table><p><small>Newest first. Replies and reactions stay with their posts.</small></p>{{else}}<p>Follow the discussion or add a reply of your own.</p><p><a href="/social">All posts</a></p>{{end}}</section>
67{{if eq .Tab "social"}}<section><h4>Find a post</h4><form action="/social" method="get"><input type="hidden" name="kind" value="{{.Query.Get "kind"}}"><label>Search <input name="q" type="search" value="{{.Query.Get "q"}}" placeholder="Words or a title"></label><label>Author <input name="author" value="{{.Query.Get "author"}}" placeholder="Public key"></label><button>Search</button> <a href="/social">Clear</a></form></section>{{end}}
68<section><h4>Follow the feed</h4><p><a href="/social.xml">Atom</a> | <a href="/social.json">JSON Feed</a></p></section>
69{{if not .Actor}}<section><h4>Join in</h4><p><a href="{{signinURL .Base .Path .Query}}">Sign in</a> to post, reply and react.</p></section>{{end}}{{end}}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs