summaryrefslogtreecommitdiff
path: root/desk/web/components
diff options
context:
space:
mode:
authorpolwex <polwex@sortug.com>2025-06-27 22:53:52 +0700
committerpolwex <polwex@sortug.com>2025-06-27 22:53:52 +0700
commit328ebe85135912678bdacd3381126ffd66ef2761 (patch)
tree365962bf45302f2a440f766a4f3c9e0a962dbe47 /desk/web/components
init
Diffstat (limited to 'desk/web/components')
-rw-r--r--desk/web/components/chat.hoon127
-rw-r--r--desk/web/components/common.hoon11
-rw-r--r--desk/web/components/date.hoon18
-rw-r--r--desk/web/components/modals.hoon20
-rw-r--r--desk/web/components/poll.hoon208
-rw-r--r--desk/web/components/post-header.hoon111
-rw-r--r--desk/web/components/post-list.hoon148
-rw-r--r--desk/web/components/post-text.hoon182
-rw-r--r--desk/web/components/post.hoon274
-rw-r--r--desk/web/components/sidebar.hoon38
-rw-r--r--desk/web/components/thread-preview.hoon70
-rw-r--r--desk/web/components/user.hoon28
-rw-r--r--desk/web/components/welcome.hoon20
13 files changed, 1255 insertions, 0 deletions
diff --git a/desk/web/components/chat.hoon b/desk/web/components/chat.hoon
new file mode 100644
index 0000000..f616a16
--- /dev/null
+++ b/desk/web/components/chat.hoon
@@ -0,0 +1,127 @@
+/- c=tlon-channels
+/+ sr=sortug, kaji, sigil=sigil-sigil, lib=boke
+=< html
+|%
+++ css ^~ %- trip
+'''
+#chat-box{
+ height: 100%;
+ width: 100%;
+ border: 1px solid var(--text-color);
+ display: flex;
+ flex-direction: column;
+
+ input[type=text]{
+ outline: none;
+ }
+
+ header{
+ border-bottom: 1px solid var(--text-color);
+ display: flex;
+ padding: 0.5rem 2rem;
+
+ h3{
+ margin: 0;
+ }
+ }
+}
+#chat-container{
+ flex-grow: 1;
+ overflow-y: scroll;
+ word-break: break-all;
+ padding: 0.5rem;
+
+
+ .chat-msg {
+ display: flex;
+ min-height: 4rem;
+
+ .left {
+ margin-right: 1rem;
+
+ & .anon-avatar{
+ width: 32px;
+ height: 32px;
+ }
+ }
+ .right {
+ width: 100%;
+ .metadata {
+ display: flex;
+ color: rgb(100, 100, 100);
+ font-size: 0.8rem;
+
+ .author{
+ font-family: "Anonymous Pro";
+ margin-right: 0.5rem;
+ }
+ }
+ .chat-content{
+ font-family: Inter;
+ }
+ }
+ }
+}
+#chat-container .chat-content img{
+ max-width: 80%;
+ display: block;
+ margin: 0 auto;
+}
+#chat-composer{
+ display: flex;
+}
+#text-input{
+ flex-grow: 1;
+}
+.row{
+ display: flex;
+}
+.f1{
+ display: flex;
+ justify-content: space-between;
+}
+'''
+++ script ^~ %- trip
+'''
+document.addEventListener('DOMContentLoaded', (event) => {
+ const room = document.getElementById("chat-container");
+ if (room) room.scrollTop = room.scrollHeight
+})
+
+document.addEventListener('kaji-fact', (event) => {
+ const room = document.getElementById("chat-container");
+ if (room) setTimeout(() => room.scrollTop = room.scrollHeight, 50)
+})
+'''
+++ post-to-manx
+|= post=@t
+;div.chat-msg
+ ;p:"(trip post)"
+==
+++ html
+ |= =bowl:gall ^- manx
+ =/ pat /chat/(scot %p our.bowl)/chat/posts/newest/200/post
+ =/ io ~(. io:sr bowl)
+ =/ scrying (scry:io %channels pat paged-posts:c)
+ =/ chat-list (tap:on-posts:c posts.scrying)
+ ;div#chat-box
+ ;style:"{css}"
+ ;script:"{script}"
+ ;header.f1
+ ;h3: Bloody Shovel Public Chat
+ ;div: ~hostyr-docteg-mothep/spandrell
+ ==
+ ;div#chat-container
+ ;* %+ turn chat-list |= [=time up=(unit post:c)]
+ ?~ up ;p:"Deleted post"
+ (tlon-chat-post-to-manx:lib u.up)
+ ==
+ ;form
+ =id "chat-composer"
+ =kaji "poke"
+ =action "add-chat"
+ ;input#text-input(type "text", name "input", autocomplete "off");
+ ;input(type "submit", value "Submit");
+ ==
+ ==
+--
diff --git a/desk/web/components/common.hoon b/desk/web/components/common.hoon
new file mode 100644
index 0000000..553103a
--- /dev/null
+++ b/desk/web/components/common.hoon
@@ -0,0 +1,11 @@
+/+ sr=sortug
+|%
+++ cursor
+|= [cursor=(unit @da) label=tape name=@t tag=?] ^- manx
+?~ cursor ;div;
+=/ type ?: tag "t" "b"
+=/ path "/page/{type}/{(trip name)}/{(scow:parsing:sr %uw u.cursor)}"
+;div.cursor(kaji "iscroll", path path, cont "#post-list", where "bottom")
+ ;button:"{label} posts"
+==
+--
diff --git a/desk/web/components/date.hoon b/desk/web/components/date.hoon
new file mode 100644
index 0000000..1526af0
--- /dev/null
+++ b/desk/web/components/date.hoon
@@ -0,0 +1,18 @@
+/+ sr=sortug
+|_ [date=@da link=?]
+++ $
+^- marl
+=+ [[a y] m [d h mm s f]]=(yore date)
+=/ ys %- trip (ud-to-cord:string:sr y)
+?: link
+;=
+ ;a/"{ys}":"{ys}"
+ ;a/"{ys}/{<m.m>}":"/{<m.m>}"
+ ;a/"{ys}/{<m.m>}/{<d.d>}":"/{<d.d>}"
+==
+;=
+ ;span:"{ys}"
+ ;span:"/{<m.m>}"
+ ;span:"/{<d.d>}"
+==
+-- \ No newline at end of file
diff --git a/desk/web/components/modals.hoon b/desk/web/components/modals.hoon
new file mode 100644
index 0000000..7068a97
--- /dev/null
+++ b/desk/web/components/modals.hoon
@@ -0,0 +1,20 @@
+/- boke
+/+ kaji
+|_ [s=state:boke =bowl:gall pat=path]
+++ $
+?+ pat (error-page:kaji 404)
+[%login ~] login
+==
+++ login
+;div#modal-body
+ ;h3: Login with an Urbit ship
+ ;form
+ =kaji "navi"
+ ;fieldset
+ ;label: Urbit ID
+ ;input(type "text", name "ship");
+ ==
+ ;input(type "submit", value "Login");
+ ==
+==
+-- \ No newline at end of file
diff --git a/desk/web/components/poll.hoon b/desk/web/components/poll.hoon
new file mode 100644
index 0000000..3184ec0
--- /dev/null
+++ b/desk/web/components/poll.hoon
@@ -0,0 +1,208 @@
+/- *polls
+/+ sr=sortug, kaji, lib=boke
+|%
+++ css ^~ %- trip
+'''
+.new-poll{
+ border: 1px solid black;
+ width: 80%;
+ margin: auto;
+ margin-bottom: 1rem;
+ padding: 1rem;
+ #poll-title{
+ font-size: 1.5rem;
+ padding: 0.3rem;
+ margin-bottom: 0.5rem;
+ text-align: center;
+ }
+ & input[type=text]{
+ display: block;
+ line-height: 1.5rem;
+ width: 80%;
+ margin: 0.3rem auto;
+ }
+ #final-row{
+ border-top: 1px solid black;
+ }
+ #bet-input{
+ width: 30%;
+ display: inline-block;
+ margin-left: 1ch;
+ }
+}
+@media (max-width: 800px){
+ #final-row{
+ display: block;
+ }
+}
+
+'''
+++ script ^~ %- trip
+'''
+function newOpts(){
+ const button = document.getElementById("add-opt-button");
+ const container = document.getElementById("options");
+
+ button.addEventListener("click", (e) => {
+ console.log(e, "clck")
+ e.preventDefault();
+ e.stopPropagation();
+
+ const currentOpts = container.querySelectorAll(".opt");
+ const index = currentOpts.length;
+
+ const input = document.createElement("input");
+ input.classList.add("opt");
+ input.type = "text";
+ input.name = `poll-opt-${index}`;
+ input.placeholder = "New Option";
+ input.autocomplete = "off";
+ container.appendChild(input);
+ })
+}
+setTimeout(newOpts, 500);
+'''
+++ form
+|= =bowl:gall ^- manx
+;form
+;div.new-poll
+ ;style:"{css}"
+ ;input#poll-title(type "text", name "poll-title", placeholder "Poll Question");
+ ;div#options
+ ;input.opt(type "text", name "poll-opt-0", placeholder "First Option", autocomplete "off");
+ ;input.opt(type "text", name "poll-opt-1", placeholder "Second Option", autocomplete "off");
+ ==
+ ;div.f1
+ ;div
+ ;label: Multiple Choice
+ ;input(type "checkbox", name "poll-multiple");
+ ==
+ ;button#add-opt-button(type "button"):"Add"
+ ==
+ ;div#final-row.f1
+ ;div
+ ;label: Require betting
+ ;input#bet-input(type "text", name "poll-min-bet");
+ ==
+ ;div
+ ;label: Expires
+ ;input(type "datetime-local", name "poll-expiry");
+ ==
+ ==
+ ;script:"{script}"
+==
+==
++$ opt
+ $: text=@t
+ votes=(set @p)
+ ==
+++ show
+|= [=poll =bowl:gall] ^- manx
+ =/ poll-type=tape ?: ?=(%exc -.votes.poll) "Single choice poll" "Multiple choice poll"
+ =/ pid-string (enc:kaji [author.poll time.poll])
+ =/ total=@ud
+ ?: ?=(%exc -.votes.poll)
+ ~(wyt by p.votes.poll)
+ %- ~(rep by p.votes.poll) |= [it=[@ud m=(map @p (set @ud))] acc=@ud]
+ (add acc ~(wyt by m.it))
+
+ |^
+ ?: (gth now.bowl expiry.poll) expired-poll fresh-poll
+ ++ fresh-poll ^- manx
+ =/ expiry-string (post-date-ago:lib now.bowl expiry.poll %yau)
+ ;div.poll
+ ;div.top-row
+ ;h6:"Open Poll"
+ ;h3.hs:"{(trip text.poll)}"
+ ==
+ ;div.options
+ ;* %+ mapi:sr options.poll fresh-opt
+ ==
+ ;div.bottom-row.f1
+ ;div.poll-type:"{poll-type}"
+ ;div.expiry:"Expires in {expiry-string}"
+ ==
+ ==
+ ++ fresh-opt
+ |= [i=@ud text=@t]
+ =/ payload (enc:kaji [author.poll time.poll i])
+ =/ stats (opt-stats i)
+ =/ pct (scow %ud pct.stats)
+ =/ style "width: {pct}%;"
+ =/ classes
+ ?: ?=(%exc -.votes.poll)
+ =/ have-voted (~(get by p.votes.poll) src.bowl)
+ ?~ have-voted
+ "poll-option open"
+ ?: .=(u.have-voted i)
+ "poll-option voted"
+ "poll-option not-voted"
+ :: inc
+ =/ opt-votes (~(get by p.votes.poll) i)
+ ?~ opt-votes "poll-option open"
+ =/ my-votes (~(get by u.opt-votes) src.bowl)
+ ?~ my-votes "poll-option open"
+ ?: (~(has in u.my-votes) i)
+ "poll-option voted"
+ "poll-option open"
+
+
+ ;div
+ =class classes
+ =kaji "poke"
+ =action "vote-poll"
+ =name "option"
+ =payload payload
+
+ ;div.poll-option-color(style style);
+ ;div.f1.inner
+ =name "option-{(scow %ud i)}"
+ =payload "vote"
+ ;div.poll-option-name.hs:"{(trip text)}"
+ ;div:"{pct}%"
+ ==
+ ==
+ ++ expired-poll ^- manx
+ =/ expiry-string (post-date-ago:lib expiry.poll now.bowl %yau)
+ ;div.poll
+ ;div.top-row
+ ;h6:"Closed Poll"
+ ;h3.hs:"{(trip text.poll)}"
+ ==
+ ;div.options
+ ;* %+ mapi:sr options.poll dead-opt
+ ==
+ ;div.bottom-row
+ ;div.poll-type:"{poll-type}"
+ ;div.expiry:"Expired {expiry-string} ago"
+ ==
+ ==
+ ++ dead-opt
+ |= [i=@ud text=@t] ^- manx
+ =/ stats (opt-stats i)
+ =/ pct (scow %ud pct.stats)
+ =/ style "width: {pct}%;"
+ ;div.poll-option
+ ;div.poll-option-color(style style);
+ ;div.inner.f1
+ ;div.poll-option-name.hs:"{(trip text)}"
+ ;div:"{pct}%"
+ ==
+ ==
+ ++ opt-stats
+ |= i=@ud ^- [count=@ud pct=@ud voters=(set @p)]
+ =/ voters
+ ?: ?=(%exc -.votes.poll)
+ =/ all-votes ~(tap by p.votes.poll)
+ %+ roll all-votes |= [[sip=@p vote=@ud] acc=(set @p)]
+ ?. .=(vote i) acc %- ~(put in acc) sip
+ ::
+ =/ all-votes (~(get by p.votes.poll) i)
+ ?~ all-votes ~
+ ~(key by u.all-votes)
+ =/ voter-count ~(wyt in voters)
+ =/ pct ?: .=(total 0) 0 (div (mul voter-count 100) total)
+ [voter-count pct voters]
+
+ --
+--
diff --git a/desk/web/components/post-header.hoon b/desk/web/components/post-header.hoon
new file mode 100644
index 0000000..a9cb1e2
--- /dev/null
+++ b/desk/web/components/post-header.hoon
@@ -0,0 +1,111 @@
+/- tp=trill-post
+/+ sr=sortug, lib=boke
+=< html
+|%
+++ css ^~ %- trip
+'''
+ #post-header{
+ -webkit-locale: "en";
+ text-size-adjust: 100%;
+ color: rgb(0, 0, 0);
+ font-size: 16px;
+ font-weight: 400;
+ text-rendering: optimizelegibility;
+ -webkit-font-smoothing: antialiased;
+ line-height: 1.618;
+ text-shadow: rgb(0, 0, 0) 0px 0px 0px;
+ align-items: center;
+ border-bottom-color: rgb(223, 223, 223);
+ border-bottom-style: dotted;
+ border-bottom-width: 1px;
+ box-sizing: border-box;
+ margin-left: 0px;
+ margin-right: 0px;
+ padding-bottom: 30px;
+ padding-left: 32px;
+ padding-right: 32px;
+ padding-top: 30px;
+ text-align: center;
+
+ & h2{
+ font-size: 2.1rem;
+ text-transform: uppercase;
+ }
+
+ & #post-meta, .comment-meta, .entry-footer {
+ font-size: 90%;
+ font-style: italic;
+ color: #969696;
+ }
+ @media (min-width: 768px){
+ & .col-md-4 {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+ }
+ & .breadcrumbs {
+ list-style: none;
+ display: inline-block;
+ margin: 0;
+ padding: 0;
+ border: none;
+ background: transparent;
+ text-indent: 0;
+
+ }
+ & .breadcrumbs li {
+ display: inline-block;
+ margin: 0 0.3rem;
+ padding: 0;
+ border: none;
+ background: transparent;
+ text-indent: 0;
+ }
+ & .tags{
+ display: block;
+ text-transform: capitalize;
+ }
+
+ }
+'''
+++ html
+|= p=post:tp ^- manx
+=/ url (trip (spat (title-to-path:lib title.p id.p))) :: TODO check for relative/absolute path
+=/ date (date-to-tape:string:sr id.p "-")
+
+=+ [[a y] m [d h mm s f]]=(yore id.p)
+=/ ys %- trip (ud-to-cord:string:sr y)
+
+::
+;div#post-header
+ ;style:"{css}"
+ ;h2:"{(trip title.p)}"
+ ;div#post-meta
+ ;span
+ ; Posted by Spandrell on
+ ;ul.breadcrumbs
+ ;li
+ ;a/"{ys}":"{ys}"
+ ==
+ ; /
+ ;li
+ ;a/"{ys}/{<m.m>}":"{<m.m>}"
+ ==
+ ; /
+ ;li
+ ;a/"{ys}/{<m.m>}/{<d.d>}":"{<d.d>}"
+ ==
+ ==
+ ==
+ ;+ ?: .=(0 ~(wyt in tags.p)) ;span:""
+ ;div.tags
+ ;* %+ mapi:sr ~(tap in tags.p)
+ |= [i=@ t=@t] ^- manx
+ ?. .=(i (dec ~(wyt in tags.p)))
+ ;a/"/tags/{(trip t)}":"{(trip t)}, "
+ ;a/"/tags/{(trip t)}":"{(trip t)}"
+ ==
+ ==
+==
+-- \ No newline at end of file
diff --git a/desk/web/components/post-list.hoon b/desk/web/components/post-list.hoon
new file mode 100644
index 0000000..427aa72
--- /dev/null
+++ b/desk/web/components/post-list.hoon
@@ -0,0 +1,148 @@
+/- tp=trill-post
+/+ sr=sortug, plib=trill-utils, lib=boke, kaji
+/= content /web/components/post-text
+|%
+++ abbreviated-post
+|= [cm=content-map:tp button=manx] ^- (list manx)
+=/ abbreviated (abbreviate-post:plib cm 1.000)
+=/ blocks (latest-contents:plib cm)
+?: .=(blocks abbreviated) %+ turn blocks block:content
+%+ snoc (turn abbreviated block:content) button
+
+++ post-snippet
+|= [p=post:tp children=full-graph:tp] ^- manx
+=/ url (trip (spat (title-to-path:lib title.p id.p))) :: TODO check for relative/absolute path
+=+ [[a y] m [d h mm s f]]=(yore id.p)
+=/ ys %- trip (ud-to-cord:string:sr y)
+:: =/ author (scot %p p.author.p)
+=/ count (houyi:plib children)
+=/ comment-count ?: .=(count 0) "No" "{<count>}"
+::
+=/ continue-button
+;div.continue-button-wrapper
+ ;a.continue-button/"{url}":"Continue"
+==
+=/ comment-div
+ ;span
+ ;a/"{url}#comments":"{comment-count} comments"
+ ==
+=/ tag-links
+%+ mapi:sr ~(tap in tags.p)
+ |= [i=@ t=@t] ^- manx
+ ?. .=(i (dec ~(wyt in tags.p)))
+ ;a/"/tags/{(trip t)}":"{(trip t)}, "
+ ;a/"/tags/{(trip t)}":"{(trip t)} "
+
+::
+;article
+ ;header
+ ;h2
+ ;a/"{url}":"{(trip title.p)}"
+ ==
+ ==
+ ;main
+ ;* (abbreviated-post contents.p continue-button)
+ ==
+ ;div.post-metadata
+ ;span
+ ; Posted on
+ ;a/"{ys}":"{ys}"
+ ;a/"{ys}/{<m.m>}":"/{<m.m>}"
+ ;a/"{ys}/{<m.m>}/{<d.d>}":"/{<d.d>}"
+ ==
+ :: ;span
+ :: ; by
+ :: ;a/"by/{author}":"{author}"
+ :: ==
+ ==
+ ;+ ?: .=(0 ~(wyt in tags.p)) ;footer ;+ comment-div ==
+ ;footer
+ ; Tagged as
+ ;span.tags-links
+ ;* tag-links
+ ==
+ ;+ comment-div
+ ==
+==
+++ css ^~ %- trip
+'''
+#post-list{
+ :: top: 0;
+ margin: auto;
+
+ & article{
+ margin: 0 0 1.5em;
+ margin-top: 40px;
+ border-bottom: 1px dotted #dfdfdf;
+ padding: 0 2rem;
+ padding-bottom: 40px;
+
+ & header h2{
+ font-size: 1.8rem;
+ text-align: center;
+ letter-spacing: 1px;
+ text-transform: uppercase;
+ line-height: 1.4em;
+ font-weight: 500;
+ }
+ & .post-metadata, article footer{
+ font-size: 90%;
+ font-style: italic;
+ color: #969696;
+ }
+
+ & main{
+ margin: 1.5em 0 0;
+ }
+ }
+ & .tags-links{
+ text-transform: capitalize;
+ margin-right: 1ch;
+ }
+
+ & .continue-button-wrapper{
+ margin-top: 1.5rem;
+ margin-bottom: 1.5rem;
+ }
+ & .continue-button{
+ font-family: 'Montserrat', sans-serif;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ font-size: 13px;
+ -webkit-transition: all .4s ease;
+ -o-transition: all .4s ease;
+ transition: all .4s ease;
+ padding: 0.5rem, 0rem, 0.5rem, 1.25rem;
+ line-height: 1.5;
+ border-radius: 0.2rem;
+ }
+ & .cursor{
+ text-align: center;
+ margin: 1rem;
+ }
+}
+
+
+'''
+++ test
+|= [p=post:tp children=full-graph:tp] ^- manx
+=/ pid [author.p id.p]
+=/ pat /blog/snip/(scot %uw (jam pid))
+(fetch:kaji pat)
+++ cursor
+|= [cursor=(unit @da) label=tape]
+?~ cursor ;div;
+=/ path "/page/t/blog/{(scow:parsing:sr %uw `@uw`u.cursor)}"
+;div.cursor(kaji "iscroll", path path, cont "#post-list", where "bottom")
+ ;button:"{label} posts"
+==
+++ html
+|= p=page:tp
+^- manx
+;div#post-list
+ ;style:"{css}"
+ ;+ (cursor newer.p "Newer")
+ ;* (turn p.p post-snippet)
+ ;+ (cursor older.p "Older")
+==
+--
diff --git a/desk/web/components/post-text.hoon b/desk/web/components/post-text.hoon
new file mode 100644
index 0000000..0335e4e
--- /dev/null
+++ b/desk/web/components/post-text.hoon
@@ -0,0 +1,182 @@
+/- b=boke, tp=trill-post
+/+ sr=sortug, plib=trill-utils
+/= poll-div /web/components/poll
+|_ [s=state:b =bowl:gall]
+++ content
+|= c=content-map:tp ^- (list manx)
+=/ latest (pry:corm:tp c)
+?~ latest ~
+(content-list +.u.latest)
+++ content-list
+|= c=content-list:tp ^- (list manx)
+(turn c block)
+++ block
+|= b=block:tp ^- manx
+?+ -.b ;p;
+%paragraph (pg +.b)
+%blockquote (bq +.b)
+%heading (heading +.b)
+:: %table (table +.b)
+%list (htmlist +.b)
+%media (media +.b)
+%codeblock (codeblock +.b)
+%eval (eval +.b)
+%ref (refblock +.b)
+==
+++ eval
+|= txt=@t ^- manx
+:: Ream can crash if the cord is wrong
+:: soften instead
+=/ uhoon (rush txt vest)
+?~ uhoon ;p:"The hoon you tried to run ({(trip txt)}) is invalid."
+=/ run %- mule |.
+%+ slap !>(..zuse) u.uhoon
+?: ?=(%.n -.run) :: if virtualization fails get a (list tank)
+;p
+ ;span:"Evaluation of {(trip txt)} failed:"
+ ;br;
+ ;* %+ turn p.run |= t=tank ;span:"{~(ram re t)}"
+==
+;p:"{(text p.run)}"
+++ pg
+|= l=(list inline:tp) ^- manx
+;p
+;* %+ turn l inline
+==
+++ bq
+|= l=(list inline:tp) ^- manx
+;blockquote
+;* %+ turn l inline
+==
+:: ++ table
+:: |= rows=(list (list content-list:tp))
+:: ;table
+:: ;* %+ turn rows
+:: |= l=(list content-list:tp)
+:: ;tr
+:: ;* %+ turn l
+:: |= cl=content-list:tp
+:: ;td
+:: ;* (turn cl block)
+:: ==
+:: ==
+:: ==
+++ refblock
+|= [app=term src=@p pat=path]
+ ?+ app ;span;
+ %polls (poll-loader pat)
+ ==
+++ poll-loader
+|= scry-path=(pole knot) ^- manx
+?. ?=([ship=@t id=@t ~] scry-path) ;span;
+=/ usip (slaw %p ship.scry-path)
+?~ usip ;span;
+=/ uid (slaw %da id.scry-path)
+?~ uid ;span;
+=/ upoll (~(get by polls.s) [u.usip u.uid])
+?~ upoll ;span:"Poll not found"
+(show:poll-div u.upoll bowl)
+
+
+++ htmlist
+|= [l=(list content-list:tp) ordered=?]
+?: ordered
+;ol
+ ;* %+ turn l li
+==
+;ul
+ ;* %+ turn l li
+==
+++ li
+|= l=content-list:tp ^- manx
+;li
+ ;* (turn l block)
+==
+++ media
+|= m=media:tp ^- manx
+?- -.m
+%video ;video@"{(trip p.m)}";
+%audio ;audio@"{(trip p.m)}";
+%images ;div.images
+ ;* %+ turn p.m
+ |= [url=@t caption=@t] ;img@"{(trip url)}"(alt (trip caption));
+ ==
+==
+++ codeblock
+|= [code=@t lang=@t] :: TODO lang suff
+;pre
+;code:"{(trip code)}"
+==
+++ heading
+|= [pp=@t q=@] ^- manx
+=/ p (trip pp)
+?: .=(1 q) ;h1:"{p}"
+?: .=(2 q) ;h2:"{p}"
+?: .=(3 q) ;h3:"{p}"
+?: .=(4 q) ;h4:"{p}"
+?: .=(5 q) ;h5:"{p}"
+?: .=(6 q) ;h6:"{p}"
+;p:""
+++ inline
+|= l=inline:tp ^- manx
+?- -.l
+ %text (parse-text p.l)
+ %italic ;i:"{(trip p.l)}"
+ %bold ;strong:"{(trip p.l)}"
+ %strike ;del:"{(trip p.l)}"
+ %ship ;span.ship:"{(trip (scot %p p.l))}"
+ %codespan ;code:"{(trip p.l)}"
+ %link ?. (is-image:web:sr href.l)
+ ;a/"{(trip href.l)}"(target "_blank"):"{(trip show.l)}"
+ ;a/"{(trip href.l)}"
+ =target "_blank"
+ ;img@"{(trip href.l)}"(alt (trip show.l));
+ ==
+ %date
+ =/ s (time:enjs:format p.l)
+ ?. ?=(%n -.s) ;span;
+ ;span.timestamp(timestamp (trip p.s)):""
+ %note ;span;
+ %break ;br;
+ %underline ;span.underline:"{(trip p.l)}"
+ %sup ;sup:"{(trip p.l)}"
+ %sub ;sub:"{(trip p.l)}"
+ %ruby ;ruby
+ "{(trip p.l)}"
+ ;rt:"{(trip q.l)}"
+ ==
+==
+++ parse-text
+|= txt=@t ^- manx
+=/ tpe (trip txt)
+=/ youtube (rush txt youtube:parsing:sr)
+?^ youtube
+:: ;a/"{tpe}"
+:: ;img@"https://i.ytimg.com/vi/{u.youtube}/hqdefault.jpg";
+:: ==
+;iframe.youtube-frame@"https://www.youtube.com/embed/{u.youtube}";
+=/ twatter-status (rush txt twatter:parsing:sr)
+?^ twatter-status
+;div :: goddamn twatter embeds insert themselves as last child
+ ;a.parsed-twatter(status u.twatter-status):"{tpe}"
+==
+=/ trimmed (rush txt trim:parsing:sr)
+?~ trimmed ~& parsing-error=txt ;span.parsing-error;
+=/ link=(unit purl:eyre) (rust u.trimmed link:parsing:sr)
+?^ link
+ ?^ p.q.u.link
+ ?: (is-img u.p.q.u.link) ;img@"{u.trimmed}";
+ ;a.parsed-link/"{tpe}"(target "_blank"):"{tpe}" :: normal link
+ ;a.parsed-link/"{tpe}"(target "_blank"):"{tpe}" :: normal link
+;span:"{tpe}"
+
+++ is-img
+|= t=@ta
+=/ img-set %- silt
+:~ ~.webp
+ ~.png
+ ~.jpeg
+ ~.jpg
+==
+(~(has in img-set) t)
+--
diff --git a/desk/web/components/post.hoon b/desk/web/components/post.hoon
new file mode 100644
index 0000000..488c308
--- /dev/null
+++ b/desk/web/components/post.hoon
@@ -0,0 +1,274 @@
+/- tp=trill-post
+/+ plib=trill-utils, sr=sortug, kaji, lib=boke
+/= content /web/components/post-text
+/= post-header /web/components/post-header
+|_ [[=post:tp children=full-graph:tp] who=@p]
+++ css ^~ %- trip
+'''
+#post-text{
+ -webkit-locale: "en";
+ text-size-adjust: 100%;
+ color: rgb(0, 0, 0);
+ line-height: 1.618;
+ text-align: left;
+ text-shadow: rgb(0, 0, 0) 0px 0px 0px;
+ border-bottom-color: rgb(223, 223, 223);
+ border-bottom-style: dotted;
+ border-bottom-width: 1px;
+ box-sizing: border-box;
+ display: block;
+ margin-bottom: 24px;
+ margin-left: 0px;
+ margin-right: 0px;
+ margin-top: 0px;
+ padding-bottom: 40px;
+ padding-left: 32px;
+ padding-right: 32px;
+ padding-top: 4px;
+}
+#comments{
+ -webkit-locale: "en";
+ text-size-adjust: 100%;
+ color: rgb(0, 0, 0);
+ line-height: 1.618;
+ text-align: left;
+ text-shadow: rgb(0, 0, 0) 0px 0px 0px;
+ box-sizing: border-box;
+ display: block;
+ margin-bottom: 32px;
+ padding-bottom: 0px;
+ padding-left: 16px;
+ padding-right: 16px;
+ padding-top: 0px;
+}
+#comments h3{
+ -webkit-locale: "en";
+ text-size-adjust: 100%;
+ color: rgb(0, 0, 0);
+ text-align: left;
+ text-shadow: rgb(0, 0, 0) 0px 0px 0px;
+ font-size: 18.72px;
+ font-weight: 400;
+ box-sizing: border-box;
+ display: block;
+ line-height: 1.2;
+ margin-block-end: 9.36px;
+ margin-block-start: 9.36px;
+ margin-bottom: 9.36px;
+ margin-inline-end: 0px;
+ margin-inline-start: 0px;
+ margin-left: 0px;
+ margin-right: 0px;
+ margin-top: 9.36px;
+}
+#comment-list{
+ -webkit-locale: "en";
+ text-size-adjust: 100%;
+ color: rgb(0, 0, 0);
+ line-height: 1.618;
+ text-align: left;
+ text-shadow: rgb(0, 0, 0) 0px 0px 0px;
+ box-sizing: border-box;
+ display: block;
+ list-style-image: none;
+ list-style-position: outside;
+ list-style-type: none;
+ margin-block-end: 24px;
+ margin-block-start: 0px;
+ margin-bottom: 24px;
+ margin-inline-end: 0px;
+ margin-inline-start: 0px;
+ margin-left: 0px;
+ margin-right: 0px;
+ margin-top: 0px;
+ padding-inline-start: 0px;
+ padding-left: 0px;
+}
+.comment-list li{
+ -webkit-locale: "en";
+ text-size-adjust: 100%;
+ color: rgb(0, 0, 0);
+ line-height: 1.618;
+ text-shadow: rgb(0, 0, 0) 0px 0px 0px;
+ list-style-image: none;
+ list-style-position: outside;
+ list-style-type: none;
+ border-left-color: rgb(223, 223, 223);
+ border-left-style: solid;
+ border-left-width: 1px;
+ box-sizing: border-box;
+ display: list-item;
+ padding-bottom: 16px;
+ padding-left: 10px;
+ padding-right: 0px;
+ padding-top: 16px;
+ text-align: left;
+}
+.comment-author{
+ font-weight: 700;
+ color: var(--text);
+ font-style: normal;
+ font-size: 1rem;
+ margin-right: 1rem;
+}
+.comment-content{
+ font-size: 90%;
+}
+#reply-title{
+ display: inline;
+}
+.reply-prompt{
+ color: var(--hong);
+ font-size: 0.9rem;
+}
+'''
+++ script %- trip
+'''
+document.addEventListener('alpine:init', () => {
+ console.log(Alpine, Date.now())
+ Alpine.store("comment", {
+ text: "",
+ set(s){
+ this.text = s
+ }
+ });
+ Alpine.store("replying_to", {
+ show(s){
+ return this.to === s
+ },
+ to: "",
+ open(s){
+ this.to = s
+ }
+ });
+ Alpine.bind('bigReplyButton', () => ({
+ type: 'button',
+ 'x-on:click'(){
+ Alpine.store('replying_to').open('main');
+ }
+ }));
+ Alpine.bind('ReplyButton', () => ({
+ type: 'button',
+ 'x-on:click'() {
+ Alpine.store("replying_to").open(this.$el.id)
+ }
+ }));
+ Alpine.bind("cancelButton", () => ({
+ 'x-on:click'() {
+ Alpine.store("replying_to").open("")
+ }
+ }))
+ Alpine.bind("collapseButton", () => ({
+ 'x-on:click'() {
+ Alpine.store("replying_to").open("")
+ }
+ }))
+})
+'''
+++ post-content
+^- manx
+;div#post-text
+ ;style:"{css}"
+ ;* (content contents.post)
+==
+++ comments
+=/ cs (tap:form:tp children)
+=/ l (houyi:plib children)
+=/ length ?: .=(0 l) "No" "{<l>}"
+=/ js "$store.replying_to.show('main')"
+^- manx
+;div#comments
+ ;a/"/board/p/{(board-path:lib post)}":"Switch to Board View"
+ ;h3:"{length} comments"
+ ;span.fl(x-bind "bigReplyButton"):"Leave a reply"
+ ;div(x-show js)
+ ;+ (reply-box-f [author.post id.post])
+ ==
+ ;ul.comment-list
+ =id "children{(scow %ud id.post)}"
+ ;* (turn cs comment)
+ ==
+==
+++ grandchildren
+|= p=full-node:tp ^- manx
+=/ cs (tap:form:tp children.p)
+;ul.comment-list
+ =id "children{(scow %ud id.p.p)}"
+ ;* (turn cs comment)
+==
+++ comment
+|= [=pid:tp [p=post:tp c=full-graph:tp]]
+^- manx
+=/ author (get-name:lib -.pid)
+=/ time (datetime-to-tape:string:sr id.pid "/")
+=/ id=tape "{<`@ud`id.pid>}"
+=/ js=tape "$store.replying_to.show('{id}')"
+:: =/ uncollapse=manx ;span.cp(x-bind "collapseButton"):"[+] ({<(houyi p)>})"
+:: =/ collapse ;span.cp(x-bind "collapseButton"):"[-]"
+;li
+ =id "comment{id}"
+ ;div.comment-meta
+ ;div.row
+ ;a.comment-author/"/u/{author}":"{author}"
+ ; |
+ ;a.comment-time/"{time}":"{time}"
+ ; |
+ ==
+ ==
+ ;div.comment-content
+ ;* (content contents.p)
+ ==
+ ;div(x-show js)
+ ;+ (reply-box-f pid)
+ ==
+ ;span.fl.reply-prompt(id id, x-bind "ReplyButton"):"reply"
+ ;+ (grandchildren [p c])
+==
+++ reply-box-f
+|= =pid:tp
+?: .=(%pawn (clan:title who))
+;p#respond:"You must login with an Urbit ID to comment"
+(reply-box pid)
+++ reply-box
+|= =pid:tp
+^- manx
+=/ post-meta (enc:kaji [[author.post id.post] pid])
+=/ parent-id=tape "{<`@ud`id.pid>}"
+=/ author %- trip (scot %p who)
+;div#respond
+ ;span
+ =id "cancel-reply"
+ =class "fl"
+ =x-bind "cancelButton"
+ ; Cancel reply
+ ==
+ ;form#reply-form
+ =kaji "poke"
+ =action "add-post"
+ ;label(for "comment");
+ ;textarea#comment
+ =name "comment"
+ =cols "45"
+ =rows "8"
+ =maxlength "6552"
+ =autocomplete "off"
+ ;
+ ==
+ ;div#author-data
+ ;span:"Posting as "
+ ;span:"{author}"
+ ==
+ ;input(type "hidden", name "post-meta", value post-meta);
+ ;input#submit-comment(type "submit", value "Poast");
+ ==
+==
+++ html ^- manx
+~& post
+~& >> children
+ ;div
+ ;+ (post-header post)
+ ;+ post-content
+ ;+ comments
+ ;script:"{script}"
+ ==
+--
diff --git a/desk/web/components/sidebar.hoon b/desk/web/components/sidebar.hoon
new file mode 100644
index 0000000..f0fa536
--- /dev/null
+++ b/desk/web/components/sidebar.hoon
@@ -0,0 +1,38 @@
+/- tp=trill-post
+/+ *sortug
+=< html
+|%
+++ css ^~ %- trip
+'''
+#sidebar{
+ width: 25%;
+}
+'''
+++ recent-comments
+:: =/ comments ;; (list [c=post:tp p=post:tp]) (retrieve /recent-comments bowl)
+;section#recent-comments
+ ;h6.widget-title:"Recent Comments"
+ :: ;ul
+ :: ;* %+ turn comments |= [c=post:tp p=post:tp] ^- manx
+ :: =/ author ""
+ :: =/ excerpt ""
+ :: ;li
+ :: ;span.comment-author-link:"{author}"
+ :: ;span:"on"
+ :: ;a/"{path.p}":"{(trip title.p)}"
+ :: ;span.excerpt:"{excerpt}"
+ :: ==
+ :: ==
+==
+++ html
+ ;div#sidebar
+ ;style:"{css}"
+ ;div#searchbox
+ ;input(type "text");
+ :: bestposts
+ :: recentposts
+ ;+ recent-comments
+ :: archivedropdown
+ ==
+ ==
+-- \ No newline at end of file
diff --git a/desk/web/components/thread-preview.hoon b/desk/web/components/thread-preview.hoon
new file mode 100644
index 0000000..758f6d3
--- /dev/null
+++ b/desk/web/components/thread-preview.hoon
@@ -0,0 +1,70 @@
+/- tp=trill-post, cnt=contact
+/+ lib=boke, sigil=sigil-sigil, sr=sortug, plib=trill-utils, ui=trill-ui, wall
+/= user /web/components/user
+
+|_ [t=thread:tp contacts=whoms:cnt =bowl:gall]
++* wal ~(. wall src.bowl)
+++ is-blog (~(has in tags.t) 'blog')
+++ pat %- trip %- spat ?: is-blog (blog-path:lib t) (weld /board path.t)
+++ wide
+:: =/ blocks (abbreviate-post:plib content.p.fn 500)
+=/ u (user ship.pid.t contacts 40)
+=/ reply-count (scow:parsing:sr %ud (lent replies.t))
+=/ ttags (tags:wal ~(tap in tags.t))
+;div.thread-preview
+ ;div.thread-author
+ ;+ -.u
+ ;+ +.u
+ ==
+ ;div.thread-name
+ ;div
+ ;a/"{pat}"
+ ;h2:"{(trip title.t)}"
+ ==
+ ;div.thread-tags
+ ;* %+ turn ttags |= tg=@t ;a.tag/"/board?t={(trip tg)}": {(trip tg)}
+ ==
+ ==
+ ==
+ ;div.reply-count
+ ;div:"Posted {(post-date-ago:lib id.pid.t now.bowl %tam)} ago"
+ ;div:"{reply-count} replies"
+ ==
+ ;div.dates
+ ;* last-reply
+ ==
+==
+++ last-reply ^- marl
+ ?~ replies.t ~
+ =/ usr (user ship.i.replies.t contacts 40)
+ ;+ ;div.last-reply
+ ;span: Last reply {(post-date-ago:lib id.i.replies.t now.bowl %tam)} ago
+ ;div.last-reply-author
+ ;span: by
+ ;+ name.usr
+ ==
+ ==
+++ mobile
+ =/ usr (user ship.pid.t contacts 40)
+ =/ reply-count (scow:parsing:sr %ud (lent replies.t))
+
+;a.thread-preview/"{pat}"
+;div.thread-inner
+ ;h2:"{(trip title.t)}"
+ ;div.meta
+ ;div.author
+ ;div.by-author
+ ;span:"by"
+ ;+ +.usr
+ ==
+ ;div:"{(post-date-ago:lib id.pid.t now.bowl %yau)} ago"
+ ==
+ ;* ?~ replies.t ~
+ ;+ ;div.reply
+ ;div:"{reply-count} replies"
+ ;div: Last {(post-date-ago:lib id.i.replies.t now.bowl %tam)} ago
+ ==
+ ==
+==
+==
+--
diff --git a/desk/web/components/user.hoon b/desk/web/components/user.hoon
new file mode 100644
index 0000000..871c029
--- /dev/null
+++ b/desk/web/components/user.hoon
@@ -0,0 +1,28 @@
+/- cnt=contact
+/+ sigil=sigil-sigil, lib=boke
+/+ lib=boke
+|_ [u=@p =whoms:cnt avatar-size=@ud]
+++ $ ^- [avatar=manx name=manx]
+=/ ming (get-name:lib u)
+=/ sig sigil(size avatar-size)
+ =/ sigl ?: (lth u (bex 64))
+ ;div.avatar ;+ (sig u) ==
+ =/ random (random-avatar:lib (jam u))
+ ;img.avatar@"{random}";
+=/ nam ;div.name:"{ming}"
+
+=/ prof (~(get by whoms) [%.y u])
+?~ prof [sigl nam]
+=/ avatar
+ =/ avatar-data (~(get by info.u.prof) %avatar)
+ ?~ avatar-data sigl
+ ?. ?=(%look -.u.avatar-data) sigl
+ ;img.avatar@"{(trip +.u.avatar-data)}";
+=/ name-div
+ =/ name-data (~(get by info.u.prof) %nickname)
+ ?~ name-data nam
+ ?. ?=(%text -.u.name-data) nam
+ ;div.name:"{(trip +.u.name-data)}"
+
+[avatar name-div]
+--
diff --git a/desk/web/components/welcome.hoon b/desk/web/components/welcome.hoon
new file mode 100644
index 0000000..c3c07f5
--- /dev/null
+++ b/desk/web/components/welcome.hoon
@@ -0,0 +1,20 @@
+=< html
+|%
+++ css ^~ %- trip
+'''
+#welcome{
+height: 114px;
+text-align: center;
+border-bottom-color: rgb(223, 223, 223);
+border-bottom-style: dotted;
+border-bottom-width: 1px;
+}
+'''
+++ html
+ |= who=@p
+ ;div#welcome
+ ;style:"{css}"
+ ;h4:"Welcome to the all-new Spandrell site."
+ ;h4:"{(trip (scot %p who))}"
+ ==
+-- \ No newline at end of file