qmining-page/src/components/forum.js

462 lines
11 KiB
JavaScript

import React, { useState, useEffect } from 'react'
import fetch from 'unfetch'
import LoadingIndicator from '../components/LoadingIndicator'
import Sleep from '../components/sleep'
import NewsEntry from '../components/newsEntry'
import Composer from '../components/composer'
import Header from '../components/header'
import Modal from '../components/modal'
import styles from './forum.module.css'
import constants from '../constants.json'
const forumPostPerPage = 5
function fetchEntry(postKey, forumName) {
return new Promise((resolve) => {
fetch(
`${constants.apiUrl}forumEntry?forumName=${forumName}&postKey=${postKey}`,
{
credentials: 'include',
}
)
.then((resp) => {
return resp.json()
})
.then((res) => {
resolve(res)
})
})
}
function fetchForum(forumName, from) {
return new Promise((resolve) => {
fetch(
`${constants.apiUrl}forumEntries?forumName=${forumName}&getContent=true${
from ? `&from=${from}` : ''
}&count=${forumPostPerPage}`,
{
credentials: 'include',
}
)
.then((resp) => {
return resp.json()
})
.then((res) => {
resolve(res)
})
})
}
function addPost(title, content, forumName) {
return new Promise((resolve) => {
fetch(constants.apiUrl + 'addPost', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
forumName: forumName,
title: title,
content: content,
}),
})
.then((res) => {
return res.json()
})
.then((res) => {
resolve(res)
})
})
}
function updateForumPost(forum, postKey, postData) {
return Object.keys(forum).reduce((acc, key) => {
const entry = forum[key]
if (key === postKey) {
acc = {
...acc,
[key]: postData,
}
} else {
acc = {
...acc,
[key]: entry,
}
}
return acc
}, {})
}
const NewsEntryContainer = ({
postKey,
setNews,
news,
userId,
newsEntryData,
onTitleClick,
forumName,
}) => {
const [error, setError] = useState(false)
useEffect(() => {
if (!newsEntryData && !error) {
fetchEntry(postKey, forumName)
.then((res) => {
const { success, entry, msg } = res
if (success) {
setNews({ [postKey]: entry, ...news })
} else {
alert(msg)
setError(true)
}
})
.catch((e) => {
console.error(e)
setError(true)
})
}
}, [])
if (!newsEntryData) {
return <LoadingIndicator />
}
if (error) {
return <div>Lil fuckup</div>
}
return (
<NewsEntry
onTitleClick={onTitleClick}
uid={userId}
key={postKey}
newsKey={postKey}
newsItem={newsEntryData}
onPostDelete={() => {
fetch(constants.apiUrl + 'rmPost', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
forumName: forumName,
postKey: postKey,
}),
})
.then((res) => {
return res.json()
})
.then((res) => {
const { success, msg } = res
if (success) {
setNews(
Object.keys(news).reduce((acc, key) => {
const entry = news[key]
if (key !== postKey) {
acc = {
...acc,
[key]: entry,
}
}
return acc
}, {})
)
} else {
alert(msg)
}
})
}}
onNewsReact={({ reaction, isDelete }) => {
fetch(constants.apiUrl + 'react', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
reaction: reaction,
postKey: postKey,
isDelete: isDelete,
forumName: forumName,
}),
})
.then((res) => {
return res.json()
})
.then((res) => {
setNews(updateForumPost(news, postKey, res.postData))
})
}}
onCommentReact={({ path, reaction, isDelete }) => {
fetch(constants.apiUrl + 'react', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: 'reaction',
postKey: postKey,
path: path,
reaction: reaction,
isDelete: isDelete,
forumName: forumName,
}),
})
.then((res) => {
return res.json()
})
.then((res) => {
const { success, postData, msg } = res
if (success) {
setNews(updateForumPost(news, postKey, postData))
} else {
alert(msg)
}
})
}}
onDelete={(path) => {
fetch(constants.apiUrl + 'comment', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: 'delete',
path: path,
postKey: postKey,
forumName: forumName,
}),
})
.then((res) => {
return res.json()
})
.then((res) => {
const { success, postData, msg } = res
if (success) {
setNews(updateForumPost(news, postKey, postData))
} else {
alert(msg)
}
})
}}
onComment={(path, content) => {
fetch(constants.apiUrl + 'comment', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: 'add',
path: path,
content: content,
postKey: postKey,
forumName: forumName,
}),
})
.then((res) => {
return res.json()
})
.then((res) => {
const { success, postData, msg } = res
if (success) {
setNews(updateForumPost(news, postKey, postData))
} else {
alert(msg)
}
})
}}
/>
)
}
export default function Forum({
router,
globalData,
globalState,
setGlobalState,
forumName,
children,
allowPost,
}) {
const userId = globalData.userId
const [news, setNews] = useState(null)
const [nextEntryKey, setNextEntryKey] = useState()
const [fetchingForum, setFetchingForum] = useState(false)
const [postInModalKey, setPostInModalKey] = useState()
const [isUploading, setIsUploading] = useState(false)
useEffect(() => {
if (globalState[forumName]) {
const { entries, nextKey } = globalState[forumName]
setNextEntryKey(nextKey)
setNews(entries)
} else {
setFetchingForum(true)
fetchForum(forumName).then((res) => {
setFetchingForum(false)
const { entries, nextKey } = res
setNextEntryKey(nextKey)
setNews(entries)
setGlobalState({ [forumName]: res })
})
}
}, [])
useEffect(() => {
const postKey = router.query.postKey
? decodeURIComponent(router.query.postKey)
: ''
if (postKey) {
setPostInModalKey(postKey)
}
}, [router.query.postKey])
const renderNews = () => {
if (news) {
const newsItems = Object.keys(news).map((postKey) => {
const newsEntryData = news[postKey]
return (
<NewsEntryContainer
forumName={forumName}
onTitleClick={() => {
setPostInModalKey(postKey)
router.replace(
`${router.pathname}?postKey=${encodeURIComponent(postKey)}`,
undefined,
{ shallow: true }
)
}}
key={postKey}
postKey={postKey}
setNews={setNews}
news={news}
userId={userId}
newsEntryData={newsEntryData}
/>
)
})
return (
<div>
{allowPost && (
<Composer
onSubmit={(title, content) => {
setIsUploading(true)
addPost(title, content, forumName).then((res) => {
const { success, newPostKey, newEntry, msg } = res
if (success) {
setNews({ [newPostKey]: newEntry, ...news })
} else {
alert(msg)
}
setIsUploading(false)
})
}}
/>
)}
{isUploading && (
<div
style={{
display: 'flex',
alignItems: 'center',
flexDirection: 'column',
}}
>
{'Feltöltés ...'}
<LoadingIndicator />
</div>
)}
<div>{newsItems}</div>
{nextEntryKey ? (
<div
className={styles.loadMoreButton}
onClick={() => {
if (fetchingForum) {
return
}
setFetchingForum(true)
fetchForum(forumName, nextEntryKey).then((res) => {
setFetchingForum(false)
const { entries, nextKey } = res
setNextEntryKey(nextKey)
setNews({ ...news, ...entries })
setGlobalState({
news: {
entries: { ...news, ...entries },
nextKey: nextKey,
},
})
})
}}
>
{fetchingForum ? (
<LoadingIndicator />
) : (
'Több bejegyzés betöltése'
)}
</div>
) : (
<div
style={{
padding: 16,
display: 'flex',
justifyContent: 'center',
fontStyle: 'italic',
}}
>
{newsItems.length === 0 ? 'Üres fórum' : 'The end'}
</div>
)}
</div>
)
} else {
return <LoadingIndicator />
}
}
return (
<div>
<Header />
<Sleep />
{children || null}
{renderNews()}
{postInModalKey && (
<Modal
closeClick={() => {
setPostInModalKey(undefined)
router.replace(router.pathname, undefined, { shallow: true })
}}
>
{news ? (
<NewsEntryContainer
postKey={postInModalKey}
setNews={setNews}
news={news}
userId={userId}
newsEntryData={news[postInModalKey]}
/>
) : (
<LoadingIndicator />
)}
</Modal>
)}
</div>
)
}