diff options
author | polwex <polwex@sortug.com> | 2025-06-27 22:53:52 +0700 |
---|---|---|
committer | polwex <polwex@sortug.com> | 2025-06-27 22:53:52 +0700 |
commit | 328ebe85135912678bdacd3381126ffd66ef2761 (patch) | |
tree | 365962bf45302f2a440f766a4f3c9e0a962dbe47 /desk/web/components |
init
Diffstat (limited to 'desk/web/components')
-rw-r--r-- | desk/web/components/chat.hoon | 127 | ||||
-rw-r--r-- | desk/web/components/common.hoon | 11 | ||||
-rw-r--r-- | desk/web/components/date.hoon | 18 | ||||
-rw-r--r-- | desk/web/components/modals.hoon | 20 | ||||
-rw-r--r-- | desk/web/components/poll.hoon | 208 | ||||
-rw-r--r-- | desk/web/components/post-header.hoon | 111 | ||||
-rw-r--r-- | desk/web/components/post-list.hoon | 148 | ||||
-rw-r--r-- | desk/web/components/post-text.hoon | 182 | ||||
-rw-r--r-- | desk/web/components/post.hoon | 274 | ||||
-rw-r--r-- | desk/web/components/sidebar.hoon | 38 | ||||
-rw-r--r-- | desk/web/components/thread-preview.hoon | 70 | ||||
-rw-r--r-- | desk/web/components/user.hoon | 28 | ||||
-rw-r--r-- | desk/web/components/welcome.hoon | 20 |
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 |