aboutsummaryrefslogtreecommitdiff
path: root/frontend/src/components/pages/summary.js
diff options
context:
space:
mode:
authorNidboj132 <28981031+Nidboj132@users.noreply.github.com>2023-12-23 14:09:35 +0100
committerGitHub <noreply@github.com>2023-12-23 14:09:35 +0100
commit62c116189a1b66667f5843e3a069add05c2487f8 (patch)
treef051e5937b781ae3d67ef9854c2b3285158c3270 /frontend/src/components/pages/summary.js
parentfix: sql typo discussions delete (#142) (diff)
downloadlphub-62c116189a1b66667f5843e3a069add05c2487f8.tar.gz
lphub-62c116189a1b66667f5843e3a069add05c2487f8.tar.bz2
lphub-62c116189a1b66667f5843e3a069add05c2487f8.zip
feat: discussion page (#137)
Diffstat (limited to 'frontend/src/components/pages/summary.js')
-rw-r--r--frontend/src/components/pages/summary.js176
1 files changed, 175 insertions, 1 deletions
diff --git a/frontend/src/components/pages/summary.js b/frontend/src/components/pages/summary.js
index 7c72c48..228ce01 100644
--- a/frontend/src/components/pages/summary.js
+++ b/frontend/src/components/pages/summary.js
@@ -21,6 +21,10 @@ const fakedata={} //for debug
21 const [data, setData] = React.useState(null); 21 const [data, setData] = React.useState(null);
22 React.useEffect(() => { 22 React.useEffect(() => {
23 setData(null) 23 setData(null)
24 setDiscussionThread(null)
25 setCreatePostState(0)
26 setSelectedRun(0)
27 setCatState(1)
24 fetch(`https://lp.ardapektezol.com/api/v1/maps/${location.pathname.split('/')[2]}/summary`) 28 fetch(`https://lp.ardapektezol.com/api/v1/maps/${location.pathname.split('/')[2]}/summary`)
25 .then(r => r.json()) 29 .then(r => r.json())
26 .then(d => { 30 .then(d => {
@@ -40,6 +44,28 @@ const fakedata={} //for debug
40 // eslint-disable-next-line 44 // eslint-disable-next-line
41 }, [pageNumber,location.pathname]); 45 }, [pageNumber,location.pathname]);
42 46
47 const [discussions,setDiscussions] = React.useState(null)
48 function fetchDiscussions() {
49 fetch(`https://lp.ardapektezol.com/api/v1/maps/${location.pathname.split('/')[2]}/discussions`)
50 .then(r=>r.json())
51 .then(d=>setDiscussions(d.data.discussions))
52 }
53
54 React.useEffect(()=>{
55 fetchDiscussions()
56 },[location.pathname])
57
58
59
60const [discussionThread,setDiscussionThread] = React.useState(null)
61function openDiscussion(x){
62 fetch(`https://lp.ardapektezol.com/api/v1/maps/${location.pathname.split('/')[2]}/discussions/${x}`)
63 .then(r=>r.json())
64 .then(d=>setDiscussionThread(d.data.discussion))
65}
66const [discussionSearch, setDiscussionSearch] = React.useState("")
67
68
43 69
44 70
45const [navState, setNavState] = React.useState(0); // eslint-disable-next-line 71const [navState, setNavState] = React.useState(0); // eslint-disable-next-line
@@ -204,7 +230,9 @@ function YouTubeGetID(url){
204 } 230 }
205 231
206function TimeAgo(date) { 232function TimeAgo(date) {
207 const seconds = Math.floor((new Date() - date) / 1000); 233 // const seconds = Math.floor((new Date() - date) / 1000);
234
235 const seconds = Math.floor(((new Date(new Date() - (date.getTimezoneOffset()*-60000))) - date) / 1000);
208 236
209 let interval = Math.floor(seconds / 31536000); 237 let interval = Math.floor(seconds / 31536000);
210 if (interval > 1) {return interval + ' years ago';} 238 if (interval > 1) {return interval + ' years ago';}
@@ -239,6 +267,52 @@ function TicksToTime(ticks) {
239 return `${hours===0?"":hours+":"}${minutes===0?"":hours>0?minutes.toString().padStart(2, '0')+":":(minutes+":")}${minutes>0?seconds.toString().padStart(2, '0'):seconds}.${milliseconds.toString().padStart(3, '0')} (${ticks})`; 267 return `${hours===0?"":hours+":"}${minutes===0?"":hours>0?minutes.toString().padStart(2, '0')+":":(minutes+":")}${minutes>0?seconds.toString().padStart(2, '0'):seconds}.${milliseconds.toString().padStart(3, '0')} (${ticks})`;
240} 268}
241 269
270function PostComment() {
271
272 fetch(`https://lp.ardapektezol.com/api/v1/maps/${location.pathname.split('/')[2]}/discussions/${discussionThread.id}`,{
273 method:"POST",
274 headers:{authorization:token},
275 body:JSON.stringify({"comment":document.querySelector("#discussion-send>input").value})
276})
277.then(r=>r.json())
278.then(d=>{
279 document.querySelector("#discussion-send>input").value=""
280 openDiscussion(discussionThread.id)
281})
282}
283
284
285const [createPostState,setCreatePostState] = React.useState(0)
286function CreatePost() {
287
288 fetch(`https://lp.ardapektezol.com/api/v1/maps/${location.pathname.split('/')[2]}/discussions`,{
289 method:"POST",
290 headers:{authorization:token},
291 body:JSON.stringify({"title":document.querySelector("#discussion-create-title").value,"content":document.querySelector("#discussion-create-content").value})
292 })
293 .then(r=>r.json())
294 .then(d=>{
295 setCreatePostState(0)
296 fetchDiscussions()
297 })
298}
299
300function DeletePost(post) {
301if(window.confirm(`Are you sure you want to remove post: ${post.title}?`)){
302 console.log("deleted",post.id)
303 fetch(`https://lp.ardapektezol.com/api/v1/maps/${location.pathname.split('/')[2]}/discussions/${post.id}`,{
304 method:"DELETE",
305 headers:{authorization:token},
306 })
307 .then(r=>r.json())
308 .then(d=>{
309 fetchDiscussions()
310 })
311}
312}
313
314
315
242if(data!==null){ 316if(data!==null){
243return ( 317return (
244 <> 318 <>
@@ -365,6 +439,8 @@ return (
365 </div> 439 </div>
366 </section> 440 </section>
367 441
442 {/* Leaderboards */}
443
368 {lbData===null?"":lbData.success===false?( 444 {lbData===null?"":lbData.success===false?(
369 <section id='section6' className='summary2'> 445 <section id='section6' className='summary2'>
370 <h1 style={{textAlign:"center"}}>Map is not available for competitive boards.</h1> 446 <h1 style={{textAlign:"center"}}>Map is not available for competitive boards.</h1>
@@ -445,6 +521,104 @@ return (
445 </section> 521 </section>
446 )} 522 )}
447 523
524
525 {/* Discussions */}
526 <section id='section7' className='summary3'>
527
528 {discussionThread === null ? (
529 createPostState === 0 ? (
530 discussions !== null ? (
531 // Main screen
532 <>
533 <div id='discussion-search'>
534 <input type="text" value={discussionSearch} placeholder={"Search for posts..."} onChange={()=>setDiscussionSearch(document.querySelector("#discussion-search>input").value)} />
535 <div><button onClick={()=>setCreatePostState(1)}>New Post</button></div>
536 </div>
537 {discussions.filter(f=>f.title.includes(discussionSearch)).sort((a, b) => new Date(b.updated_at) - new Date(a.updated_at))
538 .map((e, i) => (
539 <div id='discussion-post'>
540
541 <button key={e.id} onClick={() => openDiscussion(e.id)}>
542 <span>{e.title}</span>
543
544 {token!==null?e.creator.steam_id===JSON.parse(atob(token.split(".")[1])).sub?
545 <button onClick={()=>DeletePost(e)}>Delete Post</button>
546 :<span></span>:<span></span>}
547 <span><b>{e.creator.user_name}:</b> {e.content}</span>
548 <span>last updated: {TimeAgo(new Date(e.updated_at.replace("T"," ").replace("Z","")))}</span>
549 </button>
550 </div>
551 ))}
552 </>
553 ):(
554
555 // Main screen (no posts)
556 <>
557 <div id='discussion-search'>
558 <input type="text" value={discussionSearch} placeholder={"Search for posts..."} onChange={()=>setDiscussionSearch(document.querySelector("#discussion-search>input").value)} />
559 <div><button onClick={()=>setCreatePostState(1)}>New Post</button></div>
560 </div>
561 <span style={{textAlign:"center",display:"block"}}>no discussions</span>
562</>
563 )
564 ):(
565 // Creating post
566 <div id='discussion-create'>
567 <span>Create post</span>
568 <button onClick={()=>setCreatePostState(0)}>X</button>
569 <div style={{gridColumn:"1 / span 2"}}>
570 <input id='discussion-create-title' placeholder='Title...'></input>
571 <input id='discussion-create-content' placeholder='Enter the comment...' ></input>
572 </div>
573 <div style={{placeItems:"end",gridColumn:"1 / span 2"}}>
574 <button id='discussion-create-button' onClick={()=>CreatePost()}>Post</button>
575 </div>
576
577 </div>
578
579 )):(
580 // Post screen
581 <div id='discussion-thread'>
582 <div>
583 <span>{discussionThread.title}</span>
584 <button onClick={()=>setDiscussionThread(null)}>X</button>
585 </div>
586
587 <div>
588 <img src={discussionThread.creator.avatar_link} alt="" />
589 <div>
590 <span>{discussionThread.creator.user_name}</span>
591 <span>{TimeAgo(new Date(discussionThread.created_at.replace("T"," ").replace("Z","")))}</span>
592 <span>{discussionThread.content}</span>
593 </div>
594 {discussionThread.comments!==null?
595 discussionThread.comments.sort((a, b) => new Date(a.date) - new Date(b.date))
596 .map(e=>(
597 <>
598 <img src={e.user.avatar_link} alt="" />
599 <div>
600 <span>{e.user.user_name}</span>
601 <span>{TimeAgo(new Date(e.date.replace("T"," ").replace("Z","")))}</span>
602 <span>{e.comment}</span>
603 </div>
604 </>
605
606 )):""}
607
608
609 </div>
610 <div id='discussion-send'>
611 <input type="text" placeholder={"Message"} onKeyDown={(e)=>e.key==="Enter"?PostComment():""}/>
612 <div><button onClick={()=>PostComment()}>Send</button></div>
613 </div>
614
615 </div>
616
617
618 )}
619
620 </section>
621
448 </main> 622 </main>
449 </> 623 </>
450 ) 624 )