summaryrefslogtreecommitdiff
path: root/desk/web/board/thread.hoon
diff options
context:
space:
mode:
Diffstat (limited to 'desk/web/board/thread.hoon')
-rw-r--r--desk/web/board/thread.hoon807
1 files changed, 807 insertions, 0 deletions
diff --git a/desk/web/board/thread.hoon b/desk/web/board/thread.hoon
new file mode 100644
index 0000000..98811f8
--- /dev/null
+++ b/desk/web/board/thread.hoon
@@ -0,0 +1,807 @@
+/- boke, tp=trill-post, cons=contact
+/+ sr=sortug, plib=trill-utils, ui=trill-ui, sigil=sigil-sigil, lib=boke, kaji, constants, wall
+/= post-text /web/components/post-text
+/= user /web/components/user
+|_ [=thread:tp children=cpage:tp focus=(unit pid:tp) search=(unit @t) contacts=whoms:cons =state:boke =bowl:gall is-mobile=?]
++* wal ~(. wall src.bowl)
+ pt ~(. post-text [state bowl])
+++ css ^~ %- trip
+'''
+body>main{
+ width: 100%!important;
+}
+@media (max-width: 800px){
+ body>main{
+ padding: 0!important;
+ }
+}
+#board-thread{
+ max-width: 1280px;
+ margin: auto;
+
+/* base styles */
+ & header{
+ margin-top: 1rem;
+ & img{
+ width: 20px;
+ margin-left: 1rem;
+ }
+ }
+
+ & p{
+ font-size: 1.1rem;
+ }
+
+}
+
+.reply{
+ display: grid;
+ border: 1px solid silver;
+
+ & .author{
+
+ & .avatar{
+ max-height: 100px;
+ max-width: 64px;
+ display: block;
+ margin: auto;
+ }
+ & .name{
+ font-weight: 500;
+ color: var(--hong);
+ text-align: center;
+ word-break: break-word;
+ }
+ }
+
+ & .metadata {
+ & p{
+ margin: -10px 0 0 0;
+ font-size: 14px;
+ }
+ & .timestamps span{
+ margin-right: 0.3rem;
+ }
+ & .buttons{
+ display: flex;
+ & img{
+ height: 20px;
+ width: 20px;
+ margin: 5px 5px;
+ cursor: pointer;
+ }
+ }
+
+ }
+
+ & .content{
+ text-align: left:
+ word-wrap: break-word;
+
+ & img{
+ max-width: 100%;
+ }
+
+ & p{
+ padding: 0.5rem;
+ font-weight: 400;
+ overflow: hidden;
+ word-break: break-word;
+ white-space: normal;
+ margin: 1rem 0;
+ }
+ & blockquote{
+ margin: 1rem 2rem 1rem 1rem;
+ padding: 0.4rem;
+ border: 1px solid grey;
+ border-radius: 2%;
+ }
+ & .youtube-frame{
+ display: block;
+ margin: auto;
+ width: 70%;
+ min-height: 30vw;
+ }
+ }
+ & footer{
+ width: 100%;
+ padding: 0 0.5rem;
+ .tags{
+ padding: 0;
+ font-size: 0.9rem;
+ flex-wrap: wrap;
+
+ a{
+ margin-left: 0.1rem;
+ }
+ }
+ }
+
+}
+
+
+
+ /* desktop */
+
+ @media (min-width: 1000px){
+ #posts{
+ display: flex;
+ }
+ #replies{
+ width: 83.33%;
+ }
+ }
+ @media(min-width: 800px){
+
+ #sidebar{
+ position: sticky;
+ top: 4rem;
+ right: 0;
+ align-self: flex-start;
+
+ & button, & .button{
+ display: block;
+ margin: 1rem auto;
+ width: 100%;
+ }
+ }
+
+ .reply{
+ padding: 1rem;
+ padding-left: 0.4rem;
+ margin: 1rem;
+ grid-template-columns: 10% 90%;
+ grid-template-rows: 40px auto;
+
+ & .author{
+ grid-column-start: 1;
+ grid-column-end: 2;
+ grid-row-start: 1;
+ grid-row-end: 3;
+ }
+
+ & .metadata{
+ display: flex;
+ justify-content: space-between;
+ border-bottom: 1px solid grey; /* TODO darkmode */
+ grid-column-start: 2;
+ grid-column-end: 3;
+ grid-row-start: 1;
+ grid-row-end: 2;
+ }
+
+ & .content{
+ grid-column-start: 2;
+ grid-column-end: 3;
+ grid-row-start: 2;
+ grid-row-end: 3;
+
+ & img{
+ margin: 0 auto;
+ max-height: 300px;
+ }
+ }
+ }
+ }
+ /* mobile */
+ @media(max-width: 800px){
+ & header{
+ padding: 0 1rem;
+ & h1{
+ font-size: 1.8rem;
+ }
+ }
+ #replies{
+ margin-bottom: 3rem;
+ }
+
+ #posts{
+ display: block;
+ width: 100%;
+
+ & > footer{
+ background-color: black;
+ display:flex;
+ justify-content: space-evenly;
+ width: 100vw;
+ position: fixed;
+ bottom: 0;
+ height: 3rem;
+
+ & button{
+ height: 3rem;
+ }
+ }
+ & .replies{
+ margin-bottom: 3rem;
+ }
+ }
+
+ .reply{
+ padding-top: 10px;
+ grid-template-columns: 100px auto;
+ grid-template-rows: 80px auto;
+
+ & .author{
+ grid-column-start; 1;
+ grid-column-end: 2;
+ grid-row-start: 1;
+ grid-row-end: 2;
+
+ & .avatar{
+ height: 60px;
+ width: 60px;
+ display: block;
+ margin: 0 auto;
+ }
+ & .name{
+ font-size: 0.9rem;
+ }
+ }
+ & .metadata{
+ grid-column-start: 2;
+ grid-column-end: 3;
+ grid-row-start: 1;
+ grid-row-end: 2;
+ padding: 0 0.2rem;
+ border-bottom: 1px solid grey;
+
+ /* & .date{
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }*/
+ .updated-date{
+ display: none;
+ }
+
+ & .buttons{
+ width: 100%;
+ justify-content: space-between;
+ margin-top: 0.5rem;
+ }
+ }
+ & .content{
+ grid-column-start: 1;
+ grid-column-end: 3;
+ grid-row-start: 2;
+ grid-row-end: 3;
+
+ & img{
+ max-height: 300px;
+ }
+
+ }
+ }
+ }
+
+
+}
+
+#page-nav{
+ .button{
+ width: 30px;
+ height: 30px;
+ }
+}
+.icon-button{
+ padding: 0.2rem;
+ & img{
+ width: 20px;
+ }
+}
+.replying{
+ margin: 1rem;
+ padding: 0.5rem;
+ width: fit-content;
+ cursor: pointer;
+ background-color: rgb(100, 100, 100, 0.3);
+ border-radius: 0.5rem;
+ & span{
+ margin: 0 0.4rem;
+ }
+}
+.edit-box{
+ display: block;
+ & textarea{
+ display: block;
+ width: 100%;
+ height: 200px;
+ outline: none;
+ resize: none;
+ }
+ & label{
+ margin-right: 1rem;
+ }
+}
+.reply-box{
+ & .bordered{
+ border: 1px solid black;
+ & blockquote{
+ margin:0;
+ padding: 0.3rem;
+ border-bottom: 1px solid grey;
+ & .content{
+ color: green;
+ white-space: pre-wrap;
+ }
+ & .content::before{
+ content: "> ";
+ }
+ }
+ & .quoting-author{
+ display: flex;
+ gap: 1ch;
+ }
+ & textarea{
+ resize: none;
+ display: block;
+ border: none;
+ outline: none;
+ width: 100%;
+ }
+ }
+ & .reply-buttons{
+ margin-top: 0.8rem;
+ display: flex;
+ gap: 0.5rem;
+ justify-content: end;
+ align-items: center;
+
+ & input[type=image]{
+ width: 30px;
+ }
+
+ }
+}
+/* poll */
+/* base */
+
+.poll{
+ border: 1px solid black;
+ margin: 1rem auto;
+ & .bottom-row{
+ height: 1rem;
+ }
+ & .top-row{
+ position: relative;
+ height: 2rem;
+ & h6{
+ position: absolute;
+ left: 1%;
+ font-size: 1.1rem;
+ }
+ & h3{
+ position: absolute;
+ left: 50%;
+ transform: translateX(-50%);
+ font-size: 1.4rem;
+ max-width: 65%;
+ }
+ }
+ & .poll-type{
+ font-size: 0.7rem;
+ float: left;
+ }
+ & .expiry{
+ font-size: 0.8rem;
+ float: right;
+ }
+ & .poll-option{
+ position: relative;
+ border: 2px solid grey;
+ width: 80%;
+ height: 2rem;
+ margin: 0.3rem auto 0.3rem auto;
+ & .inner{
+ padding: 0 0.5rem;
+ z-index: 20;
+ }
+ & .poll-option-color{
+ z-index: 19;
+ position: absolute;
+ top: 0;
+ left: 0;
+ background-color: var(--huang);
+ opacity: 0.6;
+ height: 100%;
+ }
+ }
+ & .poll-option-name{
+ max-width: 75%;
+ }
+
+ & .poll-option.open:hover{
+ border:2px solid green;
+ cursor: pointer;
+ }
+ & .poll-option.not-voted{
+ cursor: pointer;
+ }
+ & .poll-option.voted{
+ border: 2px solid blue;
+ }
+}
+@media (min-width: 800px){
+ .poll{
+ width: 50%;
+ padding: 1rem;
+ }
+
+}
+@media (max-width: 800px){
+ .poll{
+ width: 80%;
+ padding: 0.3rem;
+ }
+
+}
+#poll-button{
+ border: none;
+ background: url('https://s3.spandrell.ch/assets/board/ui/poll.svg');
+ background-size: cover;
+ width: 40px;
+ height: 40px;
+}
+.copied{
+ border: 2px solid green;
+ border-radius: 50%;
+}
+'''
+++ thread-js ^~ %- trip
+'''
+function threadJS(){
+ const buttons = document.querySelectorAll(".buttons");
+ buttons.forEach(el => {
+ const parent = el.closest(".reply");
+ if (!parent) return
+ const cb = el.querySelector(".copy-button")
+
+ cb.addEventListener("click", () => {
+ const bp = `https://spandrell.ch/board`
+ const path = document.getElementById("board-thread").getAttribute("path");
+ navigator.clipboard.writeText(bp + path).then(res => {
+ console.log(res, "write to clipboard")
+ cb.classList.add("copied");
+ setTimeout(() => cb.classList.remove("copied"), 1000)
+ }).catch(er => {
+ console.log(er, "couldn't write to clipboard")
+ })
+ })
+
+ const db = el.querySelector(".down-button")
+ db.addEventListener("click", () => {
+ parent.querySelector("footer").scrollIntoView();
+ })
+ });
+ const up = document.querySelector("#go-up")
+ up.addEventListener("click", () => {
+ document.querySelector("#top").scrollIntoView();
+ })
+ const down = document.querySelector("#go-down")
+ down.addEventListener("click", () => {
+ document.querySelector("#bottom").scrollIntoView();
+ })
+}
+
+function scanTweets(){
+ const nodes = document.querySelectorAll(".parsed-twatter");
+ for (let n of nodes){
+ const status = n.getAttribute("status");
+ twttr.widgets.createTweet(status, n.parentElement, {
+ conversation: true
+ })
+ }
+}
+window.addEventListener('load', () => {
+ scanTweets();
+ threadJS();
+})
+function composerJS(){
+ const upl = document.getElementById("upload-img");
+ upl.addEventListener("click", (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ })
+
+}
+'''
+++ $ ^- manx
+:: =/ index (mul (dec page) board-page-size:constants)
+:: =/ is-op .=(thread.p.fn [author.p.fn id.p.fn])
+=/ is-op .y
+=/ author=@p ship.pid.thread
+=/ path-string (trip (spat path.thread))
+;div#board-thread.fsy
+ =path path-string
+ ;div#top;
+ ;style:"{css}"
+ ;header.fxc
+ :: ;+ ?: is-op
+ ;h1.tc:"{(trip title.thread)}"
+ :: ;h3.tc:"From {(trip title.p.fn)}"
+ ;* ?. ?|((is-admin:lib src.bowl) .=(author src.bowl)) ~
+ :~(edit-button delete-button)
+ ==
+ ;div#posts
+ ;+ inline
+ ;+ ?. is-mobile sidebar mobile-footer
+ ==
+ ;script:"{thread-js}"
+==
+++ inline
+ ;div#replies
+ :: ;* (cursor newer.children "Newer" search)
+ ;* %+ mapi:sr p.children thread-child
+ ;* (cursor bot.children "Older" search)
+ ==
+++ cursor
+|= [cursor=@ud label=tape search=(unit @t)] ^- marl
+ =/ len (lent replies.thread)
+ ?: (gte +(cursor) len) ~
+ =/ thread-path (enc:kaji pid.thread)
+ =/ cursor-path (enc:kaji cursor)
+ =/ path ?~ search
+ "/board/f/replies/{thread-path}/{cursor-path}"
+ "/board/f/replies/{thread-path}/{cursor-path}?query={(trip u.search)}"
+
+ ;+ ;div.cursor(kaji "iscroll", path path, cont "#replies", where "bottom")
+ ;button:"{label} posts"
+ ==
+
+++ edit-button
+=/ pid-string (enc:kaji pid.thread)
+;a/"/board/edit/{pid-string}"
+ ;img@"https://s3.spandrell.ch/assets/board/ui/edit.svg";
+==
+++ delete-button
+=/ pid-string (enc:kaji pid.thread)
+;a
+ =kaji "poke"
+ =action "del-thread"
+ =name "pid"
+ =payload pid-string
+ ;img@"https://s3.spandrell.ch/assets/board/ui/delete.svg";
+==
+
+++ thread-child
+|= [post-index=@ud =post:tp] ^- manx
+:: =/ base-index (mul (dec page) board-page-size:constants)
+:: =/ pind (add base-index +(post-index))
+=/ post-index (add post-index top.children)
+=/ usr ?: is-mobile
+ (user author.post contacts 60)
+ (user author.post contacts 64)
+=/ pid-string (enc:kaji [author.post id.post])
+=/ jammed-parents %- enc:kaji [op=pid.thread parent=[author.post id.post]]
+;div.reply(id pid-string)
+ ;div.author
+ ;+ avatar.usr
+ ;+ name.usr
+ ==
+ ;div.metadata
+ ;+ (date post post-index)
+ ;* ?: .=(post-index (dec (lent p.children))) ;+ ;div#bottom; ~
+ ;+ (buttons post pid-string)
+ ==
+ ;div.content
+ ;* (quote post)
+ ;* (content:pt contents.post)
+ ;* ?: .=(post-index 0) ;+ (footer post) ~
+ ==
+ ;+ (reply-box post name.usr jammed-parents)
+==
+++ quote
+|= p=post:tp ^- marl
+?~ parent.p ~
+?: .=(u.parent.p thread.p) ~
+=/ usr (user ship.u.parent.p contacts 0)
+;+ ;div.replying.flex
+ =kaji "modal"
+ =path "/board/f/snip/{(enc:kaji u.parent.p)}"
+ ;span:"Replying to:"
+ ;+ name.usr
+ ==
+
+
+++ date
+|= [=post:tp post-index=@ud]
+=/ date-string (datetime-to-tape:string:sr id.post "-")
+=/ posted ;span.posted-date:"Posted: {date-string}"
+=/ last-edit=time key:head:(pop:corm:tp contents.post)
+=/ =marl ?: .=(last-edit id.post) posted^~
+;= ;+ posted
+ ;span.updated-date:"Edited: {(datetime-to-tape:string:sr last-edit "-")}"
+==
+;div.date
+ ;p.timestamps
+ ;* marl
+ ==
+ ;p.meta
+ ;span:"Post #{(scow:parsing:sr %ud +(post-index))}/{<+((lent replies.thread))>}"
+ ;span.engagement:""
+ ==
+==
+++ buttons
+|= [=post:tp id=tape]
+:: =/ votes %+ roll ~(tap by reacts.engagement.post) |= [[s=@p [r=@t *]] acc=@]
+:: ?: ?|(=(r "thumbsup") =(r "👍"))
+;div.buttons
+ ;* ?. ?|((is-admin:lib src.bowl) .=(author.post src.bowl)) ~
+ ;+ ;img@"https://s3.spandrell.ch/assets/board/ui/edit.svg"
+ =kaji "scry"
+ =path "/board/f/edit/{id}"
+ =swap "swap"
+ =where "outer"
+ =targ "[id=\"{id}\"] .content"
+ ;
+ ==
+ ;img.copy-button@"https://s3.spandrell.ch/assets/board/ui/copy.svg";
+ ;img
+ =src "https://s3.spandrell.ch/assets/board/ui/upvote.svg"
+ ;
+ ==
+ ;img@"https://s3.spandrell.ch/assets/board/ui/downvote.svg"
+ ;
+ ==
+ ;img.down-button@"https://s3.spandrell.ch/assets/board/ui/down.svg";
+ ;img@"https://s3.spandrell.ch/assets/board/ui/reply.svg"
+ =kaji "toggle"
+ =targ ".reply-box"
+ =modal "1"
+ ;
+ ==
+ ;img.collapse-button
+ =src "https://s3.spandrell.ch/assets/board/ui/collapse.svg"
+ =kaji "toggle"
+ =targ ".content/.avatar/.uncollapse-button/.collapse-button"
+ ;
+ ==
+ ;img.uncollapse-button
+ =src "https://s3.spandrell.ch/assets/board/ui/uncollapse.svg"
+ =kaji "toggle"
+ =targ ".content/.avatar/.uncollapse-button/.collapse-button"
+ =hidden ""
+ ;
+ ==
+==
+++ reply-box
+|= [parent=post:tp author=manx jammed-parents=tape] ^- manx
+=/ parent-text=tape (content-to-md:ui contents.parent)
+=/ snip ?: (gth (lent parent-text) 150)
+ "{(scag 150 parent-text)}..."
+ parent-text
+=/ error-div "err{jammed-parents}"
+=/ subscription (subscription-type:wal now.bowl)
+:: %- serve-modal:kaji :_ ~
+;div.reply-box
+ =hidden ""
+ ;form
+ =kaji "poke"
+ =action "add-reply"
+ ;div.bordered
+ ;blockquote
+ ;div.quoting-author
+ ;span:"Replying to"
+ ;+ author
+ ==
+ ;div.content:"{snip}"
+ ==
+ ;textarea
+ =name "text"
+ =rows "10"
+ =placeholder "Your reply"
+ ;
+ ==
+ ==
+ ;input(type "hidden", name "parents", value jammed-parents);
+ ;input(type "hidden", name "title", value "");
+ ;input(type "hidden", name "error-div", value "#{error-div}");
+ ;span.error-div(id error-div);
+ ;div.reply-buttons
+ ;* ?~ subscription ~
+ ;= ;button#poll-button(type "button")
+ =kaji "scry"
+ =swap "add"
+ =cont ".bordered"
+ =path "/board/f/poll/new"
+ ;
+ ==
+ ;input#upload-img@"https://s3.spandrell.ch/assets/board/octicons/image-24.svg"(type "image");
+ ==
+ ;input(type "submit", value "Send");
+ ==
+ :: ==
+ ==
+==
+++ footer
+|= =post:tp
+=/ count ~(wyt in tags.post)
+=/ tags ?. (gth count 0) ;span;
+=/ tag-links
+%+ mapi:sr ~(tap in tags.post)
+ |= [i=@ t=@t] ^- manx =/ tt (trip t) =/ path "/board?t={tt}"
+ =/ show ?: .=(i (dec count)) tt "{tt}, "
+ ;a/"{path}":"{show}"
+;div.tags.flex
+ ; Tags:
+ ;* tag-links
+==
+;footer
+ ;+ tags
+==
+
+ ++ mobile-footer
+ =/ blog-path (trip (spat (blog-path:lib thread)))
+ ;footer
+ ;a/"{blog-path}"
+ ;button:"Blog view"
+ ==
+ ;button#go-up:"Up"
+ ;button#go-down:"Down"
+ ;button
+ =kaji "toggle"
+ =targ ".reply-box"
+ =modal "1"
+ ; Reply
+ ==
+ ==
+
+ ++ sidebar
+ :: =/ from +((mul (dec page) board-page-size:constants))
+ :: =/ to
+ :: =/ added (add from board-page-size:constants)
+ :: ?: (gth added (lent children)) (lent children) added
+ :: =/ froms (number:string:sr from)
+ :: =/ tos (number:string:sr to)
+ =/ post-count (number:string:sr (lent p.children))
+ =/ blog-path (trip (spat (blog-path:lib thread)))
+:: =/ max-page +((div (lent children) board-page-size:constants))
+ :: =/ page-count %- number:string:sr max-page
+ :: =/ prev-page %- number:string:sr ?: .=(page 1) 1 (dec page)
+ :: =/ curr-page (number:string:sr page)
+ :: =/ next-page %- number:string:sr ?: .=(page max-page) page +(page)
+ ;div#sidebar
+ :: ;div#search-box
+ :: ;form.flex
+ :: ;input(name "thread-search", placeholder "Search on Thread");
+ :: ;button.icon-button
+ :: ;img@"https://s3.spandrell.ch/assets/board/ui/search.svg"(type "image");
+ :: ==
+ :: ==
+ :: ==
+ ;a/"{blog-path}"
+ ;button:"Blog view"
+ ==
+ ;button#go-up:"Go To Top"
+ ;button#go-down:"Go To Bottom"
+ ;button
+ =kaji "toggle"
+ =targ ".reply-box"
+ =modal "1"
+ ; Reply
+ ==
+ ==
+++ snippet
+|= =post:tp ^- manx
+=/ date-string (datetime-to-tape:string:sr id.post "-")
+=/ usr (user author.post contacts 64)
+;div.snippet.reply
+ ;div.author
+ ;+ avatar.usr
+ ;+ name.usr
+ ==
+ ;div.metadata
+ ;div.date
+ ;p.timestamps
+ ;span.posted-date:"Posted: {date-string}"
+ ==
+ ==
+ ==
+ ;div.content
+ ;* (quote post)
+ ;* (content:pt contents.post)
+ :: ;+ (footer post)
+ ==
+==
+--