tinyrelayfile
tinyrelay/tinyclient/browse.html/

tinyclient/browse.html raw
1{{define "repos"}}<section>
2<h1>Repositories</h1>
3{{if .Error}}{{template "alert" .Error}}
4{{else if .Feed}}<table id="rows">
5<thead><tr><th>owner</th><th>repository</th><th>head</th></tr></thead>
6<tbody>
7{{range .Feed}}{{$r := asMap .}}<tr><td>{{template "nostrKey" (str (index $r "owner"))}}</td><td><a href="/repo?owner={{urlquery (str (index $r "owner"))}}&repo={{urlquery (str (index $r "identifier"))}}&view=home">{{str (index $r "identifier")}}</a>{{if index $r "private"}} <small>private</small>{{end}}</td><td>{{shortID (index $r "head")}}</td></tr>
8{{end}}</tbody>
9</table>
10{{else}}<p>No repositories were reported.</p>
11{{end}}{{with (asMap .Event).next_cursor}}<p id="pages"><a href="{{browsePageURL "/repos" $.Query "cursor" .}}">next page</a></p>
12{{end}}</section>
13{{end}}
14
15{{define "files"}}{{$data := asMap .Event}}{{$view := or (str (index $data "view")) (.Query.Get "view") "library"}}<section id="file-library" data-pubkey="{{.Actor}}">
16<h1>Files</h1>
17<nav id="file-views" aria-label="File collections"><a href="/files"{{if eq $view "library"}} aria-current="page"{{end}}>My files</a><a href="/files?view=sites"{{if eq $view "sites"}} aria-current="page"{{end}}>Sites</a><a href="/files?view=rooms"{{if eq $view "rooms"}} aria-current="page"{{end}}>Rooms</a>{{if index $data "can_manage"}}<a href="/files?view=storage"{{if eq $view "storage"}} aria-current="page"{{end}}>Storage</a>{{end}}</nav>
18<p id="file-context">{{if eq $view "sites"}}Published assets, organized by site.{{else if eq $view "rooms"}}Attachments from rooms you can access.{{else if eq $view "storage"}}All stored files, newest first.{{else}}Your uploads, folders and files shared with relay members.{{end}}</p>
19<form method="get" action="/files" id="file-search"><input type="hidden" name="view" value="{{$view}}">{{with .Query.Get "path"}}<input type="hidden" name="path" value="{{.}}">{{end}}<label>Search <input name="q" value="{{.Query.Get "q"}}" placeholder="Name, type or hash" autocomplete="off"></label> <button>Search</button>{{if .Query.Get "q"}} <a href="{{browsePageURL "/files" .Query "q" ""}}">Clear</a>{{end}}</form>
20{{with index $data "breadcrumbs"}}<nav id="file-breadcrumbs" aria-label="Folder path">{{range $i, $raw := .}}{{$crumb := asMap $raw}}{{if $i}} / {{end}}<a href="{{str (index $crumb "open")}}">{{str (index $crumb "name")}}</a>{{end}}</nav>{{end}}
21{{if .Error}}{{template "alert" .Error}}
22{{else if .Feed}}<table id="rows">
23<thead><tr><th scope="col">Name</th><th scope="col">Details</th><th scope="col">Added</th></tr></thead>
24<tbody>
25{{range .Feed}}{{$f := asMap .}}{{$kind := str (index $f "kind")}}{{$hash := str (index $f "sha256")}}{{$name := or (str (index $f "name")) (shortID $hash)}}{{$open := or (str (index $f "open")) (printf "/file?hash=%s" (urlquery $hash))}}<tr data-kind="{{$kind}}" data-hash="{{$hash}}"><td><a href="{{$open}}">{{if and $hash (hasPrefix (str (index $f "type")) "image/")}}<img loading="lazy" src="/files/raw?hash={{urlquery $hash}}" alt="">{{else}}<span data-file-icon aria-hidden="true">{{if or (eq $kind "folder") (eq $kind "site") (eq $kind "room")}}▱{{else}}▤{{end}}</span>{{end}}<span>{{$name}}</span></a>{{with index $f "context_url"}}<small><a href="{{.}}">{{str (index $f "context")}}</a></small>{{end}}</td><td>{{if eq $kind "folder"}}Folder{{else if eq $kind "site"}}Site{{else if eq $kind "room"}}Room{{else}}{{fileSize (index $f "size")}}<small>{{str (index $f "type")}}</small>{{if eq (str (index $f "access")) "members"}}<small>Relay members</small>{{else if eq (str (index $f "purpose")) "file"}}<small>Secret link</small>{{end}}{{end}}</td><td>{{with index $f "uploaded"}}<time datetime="{{datetime .}}">{{when .}}</time>{{end}}</td></tr>
26{{end}}</tbody>
27</table>
28{{else}}<p id="file-empty">{{if .Query.Get "q"}}No files match this search.{{else if eq $view "sites"}}No site files here yet.{{else if eq $view "rooms"}}No room attachments here yet.{{else if .Query.Get "path"}}This folder is empty.{{else}}No files here yet. Use Upload to add files or a folder.{{end}}</p>
29{{end}}{{with (asMap .Event).next_cursor}}<p id="pages"><a href="{{browsePageURL "/files" $.Query "cursor" .}}">next page</a></p>
30{{end}}</section>
31{{end}}
32
33{{define "file"}}{{if .Error}}<section><h1>File</h1>{{template "alert" .Error}}</section>
34{{else}}{{$file := asMap .Event}}{{$hash := or (.Query.Get "hash") (.Query.Get "sha")}}<section id="file-view" data-pubkey="{{.Actor}}">
35<h1>{{or (str (index $file "name")) "File"}}</h1>{{if eq (str (index $file "access")) "members"}}<p>Relay members can view and download this file.</p>{{end}}
36
37<file-workspace-root hash="{{str (index $file "sha256")}}" type="{{str (index $file "type")}}" hidden></file-workspace-root>
38<noscript><p>Enable JavaScript to decrypt files and browse folder manifests locally. Raw downloads remain available above.</p></noscript>
39
40{{$type := str (index $file "type")}}{{$raw := printf "/files/raw?hash=%s" (urlquery (str (index $file "sha256")))}}{{if hasPrefix $type "image/"}}<div id="media"><img src="{{$raw}}" alt=""></div>
41{{else if hasPrefix $type "video/"}}<div id="media"><video controls src="{{$raw}}"></video></div>
42{{else if hasPrefix $type "audio/"}}<div id="media"><audio controls src="{{$raw}}"></audio></div>
43{{else if eq $type "application/pdf"}}<div id="media"><a fx-ignore href="{{$raw}}">Open the PDF</a></div>
44{{else if index $file "binary"}}<p id="media">This is a binary file. Download it to view its contents.</p>
45{{else if index $file "content"}}{{sourceHTML .Event}}
46{{else}}<p id="media">This file is empty.</p>
47{{end}}<file-tools hash="{{str (index $file "sha256")}}" type="{{str (index $file "type")}}" size="{{str (index $file "size")}}" name="{{or (str (index $file "name")) (shortID (index $file "sha256"))}}"></file-tools><details id="file-facts"><summary>File details</summary><table id="facts">
48<tr><th>sha256</th><td><code>{{str (index $file "sha256")}}</code></td></tr>
49<tr><th>type</th><td>{{str (index $file "type")}}</td></tr>
50<tr><th>size</th><td>{{fileSize (index $file "size")}}</td></tr>
51{{with index $file "blossom_uri"}}<tr><th>blossom</th><td><code>{{.}}</code></td></tr>{{end}}
52<tr><th>download</th><td><a fx-ignore href="/files/raw?hash={{urlquery (str (index $file "sha256"))}}">raw</a></td></tr>
53</table></details>{{if .Actor}}<details id="manage-file"><summary>Manage file</summary><rpc-form method="deleteblob"><input type="hidden" name="param" value="{{$hash}}"><button>Delete file</button></rpc-form></details>{{end}}</section>
54{{end}}{{end}}
55
56{{define "approvals"}}<section>
57<h1>Approvals</h1>
58<p>Requests that wait for your decision. Review requests, answer questions and approve proposed changes.</p>
59{{if not .Actor}}<p><a href="{{signinURL .Base .Path .Query}}">Sign in</a> to see the requests addressed to your key.</p>
60{{else if .Error}}{{template "alert" .Error}}
61{{else}}{{$open := approvalViews .Event .URL "open"}}{{$done := approvalViews .Event .URL "answered" "expired"}}{{if $open}}{{range $item := $open}}<approval-item id="approval-{{$item.ID}}" data-type="{{$item.Type}}" data-state="{{$item.State}}">
62<header>{{template "nostrKey" $item.Asker}} in {{$item.Where}} | <time datetime="{{datetime $item.CreatedAt}}">{{when $item.CreatedAt}}</time>{{if $item.Expires}} | expires <time datetime="{{datetime $item.Expires}}">{{when $item.Expires}}</time>{{end}}</header>
63<p>{{$item.Title}}</p>{{with $item.Body}}<p>{{.}}</p>{{end}}{{with $item.About}}<pre>{{.}}</pre>{{end}}
64{{with $item.Grant}}<agent-grant-request request="{{$item.ID}}" data-review="{{.Raw}}"{{if .Error}} data-stale="true"{{end}}><h4>Proposed grant change</h4><table><thead><tr><th>scope</th><th>current</th><th>proposed</th></tr></thead><tbody>{{range .Rows}}{{if ne .Before .After}}<tr><th>{{.Label}}</th><td>{{.Before}}</td><td>{{.After}}</td></tr>{{end}}{{end}}</tbody></table>{{with .Error}}<p role="alert">{{.}}</p>{{else}}<button type="button" data-grant-approve disabled>Approve and grant</button>{{end}}<button type="button" data-grant-deny disabled>Deny</button></agent-grant-request>{{end}}
65<footer>{{if and (ne $item.Type "question") (ne $item.Type "grant")}}<nostr-react event="{{$item.ID}}" pubkey="{{$item.Asker}}" kind="{{$item.Kind}}"><button name="reaction" value="+">{{if eq $item.Type "wiki-proposal"}}Accept{{else if eq $item.Type "wiki-merge"}}Accept{{else}}Approve{{end}}</button> <button name="reaction" value="-">{{if eq $item.Type "wiki-proposal"}}Reject{{else if eq $item.Type "wiki-merge"}}Reject{{else}}Deny{{end}}</button></nostr-react> {{end}}<details><summary>Reply</summary><nostr-compose kind="1111" root="{{$item.ID}}" root-pubkey="{{$item.Asker}}" root-kind="{{$item.Kind}}"{{with $item.Coordinate}} coordinate="{{.}}"{{end}}><label>Reply <textarea name="content" required></textarea></label><button>Sign and reply</button></nostr-compose></details> <a href="{{$item.Open}}">open</a></footer>
66</approval-item>
67{{end}}{{else}}<p>Nothing waits for your decision.</p>
68{{end}}{{if $done}}<h3>Answered</h3>
69<table id="rows">
70<thead><tr><th>asker</th><th>request</th><th>answer</th></tr></thead>
71<tbody>
72{{range $item := $done}}<tr id="approval-{{$item.ID}}" data-state="{{$item.State}}"><td>{{template "nostrName" $item.Asker}}</td><td><a href="{{$item.Open}}">{{$item.Title}}</a>{{if eq $item.Decision "replied"}}{{with $item.AnswerContent}} <small>{{.}}</small>{{end}}{{end}}</td><td>{{if eq $item.State "expired"}}expired{{else}}{{$item.Decision}} | <time datetime="{{datetime $item.AnsweredAt}}">{{when $item.AnsweredAt}}</time>{{end}}</td></tr>
73{{end}}</tbody>
74</table>
75{{end}}{{with (asMap .Event).next_cursor}}<p id="pages"><a href="{{browsePageURL "/approvals" $.Query "cursor" .}}">next page</a></p>
76{{end}}<noscript><p>Approve and Deny need JavaScript and a connected signer.</p></noscript>{{end}}</section>
77{{end}}
78
79{{define "sites"}}<section>
80<h1>Sites</h1>
81<p>Static sites published to this relay as signed manifests. Each site is served on its own host.</p>
82{{if .Error}}{{template "alert" .Error}}
83{{else if .Feed}}<table id="rows">
84<thead><tr><th>author</th><th>site</th><th>updated</th></tr></thead>
85<tbody>
86{{range .Feed}}{{$s := asMap .}}<tr><td>{{template "nostrKey" (str (index $s "author"))}}</td><td><a href="{{str (index $s "url")}}">{{str (index $s "label")}}</a> <small>{{str (index $s "paths")}} paths</small></td><td>{{when (index $s "created_at")}}</td></tr>
87{{end}}</tbody>
88</table>
89{{else}}<p>No sites are published.</p>
90{{end}}</section>
91{{end}}
92
93{{define "profile"}}{{$data := asMap .Event}}{{$p := asMap (index $data "profile")}}<section>
94<h1>Profile</h1>
95<p>Your profile is a kind 0 event signed by your key. Saving publishes it to this relay first, then to the relays you list, so apps and people everywhere see the same name.</p>
96{{template "alert" .Error}}
97<profile-form pubkey="{{.Actor}}" existing="{{json (index $data "profile")}}">
98<label>Name <input name="name" maxlength="64" value="{{str (index $p "name")}}" placeholder="dami"></label>
99<label>Display name <input name="display_name" maxlength="64" value="{{str (index $p "display_name")}}"></label>
100<label>About <textarea name="about" maxlength="2000">{{str (index $p "about")}}</textarea></label>
101<label>Picture <input name="picture" type="url" value="{{str (index $p "picture")}}" placeholder="https://"></label>
102<label>Banner <input name="banner" type="url" value="{{str (index $p "banner")}}" placeholder="https://"></label>
103<label>Website <input name="website" type="url" value="{{str (index $p "website")}}" placeholder="https://"></label>
104<label>NIP-05 <input name="nip05" value="{{str (index $p "nip05")}}" placeholder="you@example.com"></label>
105<label>Lightning address <input name="lud16" value="{{str (index $p "lud16")}}" placeholder="you@wallet.example"></label>
106<label>Also publish to <textarea name="relays" placeholder="wss://relay.example">{{range index $data "relays"}}{{.}}
107{{end}}</textarea></label>
108<button>Sign and publish</button>
109<noscript><p>Saving needs JavaScript and a connected signer. You can also publish a kind 0 event from any client with this relay in its list.</p></noscript>
110</profile-form>
111{{with index $data "event"}}{{$e := asMap .}}<p><small>Current profile published <time datetime="{{datetime (index $e "created_at")}}">{{when (index $e "created_at")}}</time>; fields this form does not show are kept as they are.</small></p>{{else}}<p><small>This relay holds no profile for your key yet.</small></p>{{end}}
112</section>
113{{end}}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

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

Refs