| 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}} |