diff options
Diffstat (limited to 'desk/web/board/thread.hoon')
-rw-r--r-- | desk/web/board/thread.hoon | 807 |
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) + == +== +-- |