mirror of https://gitlab.com/ecentrics/concordia
Ezerous
4 years ago
25 changed files with 512 additions and 209 deletions
@ -0,0 +1,22 @@ |
|||
import React from 'react'; |
|||
|
|||
class ErrorBoundary extends React.Component { |
|||
constructor(props) { |
|||
super(props); |
|||
this.state = { hasError: false }; |
|||
} |
|||
|
|||
static getDerivedStateFromError() { |
|||
return { hasError: true }; |
|||
} |
|||
|
|||
render() { |
|||
const { props: { children }, state: { hasError } } = this; |
|||
if (hasError) { |
|||
return <h1>Something went wrong.</h1>; // TODO: Make a better "Something went wrong" screen |
|||
} |
|||
return children; |
|||
} |
|||
} |
|||
|
|||
export default ErrorBoundary; |
@ -1,4 +0,0 @@ |
|||
#topic-list{ |
|||
height: auto; |
|||
clear: both; |
|||
} |
@ -1,3 +1,7 @@ |
|||
#post-list{ |
|||
height: 100%; |
|||
} |
|||
|
|||
#post-list-pagination{ |
|||
padding: 1rem 0; |
|||
} |
|||
|
@ -1,7 +1,8 @@ |
|||
.particles { |
|||
position: fixed; |
|||
right: 0; |
|||
top: 0; |
|||
bottom: 0; |
|||
right: 0; |
|||
left: 0; |
|||
background: var(--secondary-color); |
|||
} |
|||
|
@ -1,67 +0,0 @@ |
|||
import React, { useMemo } from 'react'; |
|||
import { Button, Header } from 'semantic-ui-react'; |
|||
import { useSelector } from 'react-redux'; |
|||
import { useHistory } from 'react-router'; |
|||
import { useTranslation } from 'react-i18next'; |
|||
import PropTypes from 'prop-types'; |
|||
import PaginatedTopicList from '../../../components/Pagination/PaginatedTopicList'; |
|||
import './styles.css'; |
|||
|
|||
const Board = (props) => { |
|||
const { numberOfTopics } = props; |
|||
const hasSignedUp = useSelector((state) => state.user.hasSignedUp); |
|||
const history = useHistory(); |
|||
const { t } = useTranslation(); |
|||
|
|||
const boardContents = useMemo(() => ( |
|||
<> |
|||
{hasSignedUp |
|||
? ( |
|||
<Button |
|||
id="new-topic-button" |
|||
className="primary-button" |
|||
content="New Topic" |
|||
icon="plus" |
|||
labelPosition="left" |
|||
positive |
|||
onClick={() => history.push('/topics/new')} |
|||
/> |
|||
) |
|||
: null} |
|||
{/* eslint-disable-next-line no-nested-ternary */} |
|||
{numberOfTopics > 0 |
|||
// eslint-disable-next-line react/jsx-no-undef |
|||
? (<PaginatedTopicList />) |
|||
: (!hasSignedUp |
|||
? ( |
|||
<div id="no-topic-message" className="vertical-center-in-parent unselectable"> |
|||
<Header textAlign="center" as="h2"> |
|||
{t('board.header.no.topics.message')} |
|||
</Header> |
|||
<Header textAlign="center" as="h3"> |
|||
{t('board.sub.header.no.topics.guest')} |
|||
</Header> |
|||
</div> |
|||
) |
|||
: ( |
|||
<div id="no-topic-message" className="vertical-center-in-parent unselectable"> |
|||
<Header textAlign="center" as="h2"> |
|||
{t('board.header.no.topics.message')} |
|||
</Header> |
|||
<Header textAlign="center" as="h3"> |
|||
{t('board.sub.header.no.topics.user')} |
|||
</Header> |
|||
</div> |
|||
))} |
|||
|
|||
</> |
|||
), [numberOfTopics, hasSignedUp, t, history]); |
|||
|
|||
return (boardContents); |
|||
}; |
|||
|
|||
Board.propTypes = { |
|||
numberOfTopics: PropTypes.number.isRequired, |
|||
}; |
|||
|
|||
export default Board; |
@ -1,15 +0,0 @@ |
|||
#no-topic-message { |
|||
padding-top: 22rem; |
|||
clear: both; |
|||
} |
|||
|
|||
#no-topic-message > .header { |
|||
color: white; |
|||
} |
|||
|
|||
#new-topic-button{ |
|||
float:right; |
|||
margin-top: 1px; |
|||
margin-bottom: 2em; |
|||
margin-right: 0; |
|||
} |
@ -1,3 +1,19 @@ |
|||
#home-container { |
|||
height: 100%; |
|||
} |
|||
|
|||
#no-topic-message { |
|||
padding-top: 22rem; |
|||
clear: both; |
|||
} |
|||
|
|||
#no-topic-message > .header { |
|||
color: white; |
|||
} |
|||
|
|||
#new-topic-button{ |
|||
float:right; |
|||
margin-top: 1px; |
|||
margin-bottom: 2em; |
|||
margin-right: 0; |
|||
} |
|||
|
@ -0,0 +1,89 @@ |
|||
import React, { |
|||
useEffect, useMemo, useState, |
|||
} from 'react'; |
|||
import { useSelector } from 'react-redux'; |
|||
import { useTranslation } from 'react-i18next'; |
|||
import { FORUM_CONTRACT } from 'concordia-shared/src/constants/contracts/ContractNames'; |
|||
import { Header } from 'semantic-ui-react'; |
|||
import { drizzle } from '../../../redux/store'; |
|||
import { ITEMS_PER_PAGE } from '../../../components/PaginationComponent'; |
|||
import PostList from '../../../components/PostList'; |
|||
|
|||
const { |
|||
contracts: { |
|||
[FORUM_CONTRACT]: { |
|||
methods: { |
|||
getUserPostCount: { cacheCall: getUserPostCountChainData }, |
|||
getUserPosts: { cacheCall: getUserPostsChainData }, |
|||
}, |
|||
}, |
|||
}, |
|||
} = drizzle; |
|||
|
|||
const ProfilePostList = (props) => { |
|||
const { username, profileAddress } = props; |
|||
const [pageNumber, setPageNumber] = useState(1); |
|||
|
|||
const [userPostCount, setUserPostCount] = useState(null); |
|||
const [postIds, setPostIds] = useState([]); |
|||
|
|||
const [getUserPostCountCallHash, setGetUserPostCountCallHash] = useState(null); |
|||
const [getUserPostsCallHash, setGetUserPostsCallHash] = useState(null); |
|||
|
|||
const getUserPostCountResult = useSelector((state) => state.contracts[FORUM_CONTRACT].getUserPostCount[getUserPostCountCallHash]); |
|||
const getUserPostsResult = useSelector((state) => state.contracts[FORUM_CONTRACT].getUserPosts[getUserPostsCallHash]); |
|||
|
|||
const { t } = useTranslation(); |
|||
|
|||
useEffect(() => { |
|||
if (getUserPostCountCallHash === null) { |
|||
setGetUserPostCountCallHash(getUserPostCountChainData(profileAddress)); |
|||
} |
|||
}, [getUserPostCountCallHash, profileAddress]); |
|||
|
|||
useEffect(() => { |
|||
if (userPostCount !== null && userPostCount !== 0) { |
|||
const startIndex = Math.max(userPostCount - ITEMS_PER_PAGE * pageNumber, 0); |
|||
const endIndex = userPostCount - ITEMS_PER_PAGE * (pageNumber - 1) - 1; |
|||
setGetUserPostsCallHash(getUserPostsChainData(profileAddress, startIndex, endIndex)); |
|||
} |
|||
}, [pageNumber, profileAddress, userPostCount]); |
|||
|
|||
useEffect(() => { |
|||
if (getUserPostCountResult) { |
|||
setUserPostCount(parseInt(getUserPostCountResult.value, 10)); |
|||
} |
|||
}, [getUserPostCountResult, userPostCount]); |
|||
|
|||
useEffect(() => { |
|||
if (getUserPostsResult) { |
|||
setPostIds(getUserPostsResult.value.slice().reverse().map(Number)); |
|||
} |
|||
}, [getUserPostsResult, userPostCount]); |
|||
|
|||
const handlePageChange = (event, data) => { |
|||
setPageNumber(data.activePage); |
|||
}; |
|||
|
|||
return useMemo(() => { |
|||
if (postIds.length && postIds.length !== 0) { |
|||
return ( |
|||
<PostList |
|||
postIds={postIds} |
|||
numberOfItems={userPostCount} |
|||
onPageChange={handlePageChange} |
|||
/> |
|||
); |
|||
} |
|||
if (userPostCount === 0) { |
|||
return ( |
|||
<Header textAlign="center" as="h2"> |
|||
{t('profile.user.has.no.posts.header.message', { user: username })} |
|||
</Header> |
|||
); |
|||
} |
|||
return null; |
|||
}, [t, postIds, userPostCount, username]); |
|||
}; |
|||
|
|||
export default ProfilePostList; |
@ -0,0 +1,89 @@ |
|||
import React, { |
|||
useEffect, useMemo, useState, |
|||
} from 'react'; |
|||
import { useSelector } from 'react-redux'; |
|||
import { useTranslation } from 'react-i18next'; |
|||
import { FORUM_CONTRACT } from 'concordia-shared/src/constants/contracts/ContractNames'; |
|||
import { Header } from 'semantic-ui-react'; |
|||
import { drizzle } from '../../../redux/store'; |
|||
import { ITEMS_PER_PAGE } from '../../../components/PaginationComponent'; |
|||
import TopicList from '../../../components/TopicList'; |
|||
|
|||
const { |
|||
contracts: { |
|||
[FORUM_CONTRACT]: { |
|||
methods: { |
|||
getUserTopicCount: { cacheCall: getUserTopicCountChainData }, |
|||
getUserTopics: { cacheCall: getUserTopicsChainData }, |
|||
}, |
|||
}, |
|||
}, |
|||
} = drizzle; |
|||
|
|||
const ProfileTopicList = (props) => { |
|||
const { username, profileAddress } = props; |
|||
const [pageNumber, setPageNumber] = useState(1); |
|||
|
|||
const [userTopicCount, setUserTopicCount] = useState(null); |
|||
const [topicIds, setTopicIds] = useState([]); |
|||
|
|||
const [getUserTopicCountCallHash, setGetUserTopicCountCallHash] = useState(null); |
|||
const [getUserTopicsCallHash, setGetUserTopicsCallHash] = useState(null); |
|||
|
|||
const getUserTopicCountResult = useSelector((state) => state.contracts[FORUM_CONTRACT].getUserTopicCount[getUserTopicCountCallHash]); |
|||
const getUserTopicsResult = useSelector((state) => state.contracts[FORUM_CONTRACT].getUserTopics[getUserTopicsCallHash]); |
|||
|
|||
const { t } = useTranslation(); |
|||
|
|||
useEffect(() => { |
|||
if (getUserTopicCountCallHash === null) { |
|||
setGetUserTopicCountCallHash(getUserTopicCountChainData(profileAddress)); |
|||
} |
|||
}, [getUserTopicCountCallHash, profileAddress]); |
|||
|
|||
useEffect(() => { |
|||
if (userTopicCount !== null && userTopicCount !== 0) { |
|||
const startIndex = Math.max(userTopicCount - ITEMS_PER_PAGE * pageNumber, 0); |
|||
const endIndex = userTopicCount - ITEMS_PER_PAGE * (pageNumber - 1) - 1; |
|||
setGetUserTopicsCallHash(getUserTopicsChainData(profileAddress, startIndex, endIndex)); |
|||
} |
|||
}, [pageNumber, profileAddress, userTopicCount]); |
|||
|
|||
useEffect(() => { |
|||
if (getUserTopicCountResult) { |
|||
setUserTopicCount(parseInt(getUserTopicCountResult.value, 10)); |
|||
} |
|||
}, [getUserTopicCountResult, userTopicCount]); |
|||
|
|||
useEffect(() => { |
|||
if (getUserTopicsResult) { |
|||
setTopicIds(getUserTopicsResult.value.slice().reverse().map(Number)); |
|||
} |
|||
}, [getUserTopicsResult, userTopicCount]); |
|||
|
|||
const handlePageChange = (event, data) => { |
|||
setPageNumber(data.activePage); |
|||
}; |
|||
|
|||
return useMemo(() => { |
|||
if (topicIds.length && topicIds.length !== 0) { |
|||
return ( |
|||
<TopicList |
|||
topicIds={topicIds} |
|||
numberOfItems={userTopicCount} |
|||
onPageChange={handlePageChange} |
|||
/> |
|||
); |
|||
} |
|||
if (userTopicCount === 0) { |
|||
return ( |
|||
<Header textAlign="center" as="h2"> |
|||
{t('profile.user.has.no.topics.header.message', { user: username })} |
|||
</Header> |
|||
); |
|||
} |
|||
return null; |
|||
}, [t, topicIds, userTopicCount, username]); |
|||
}; |
|||
|
|||
export default ProfileTopicList; |
@ -0,0 +1,82 @@ |
|||
import React, { |
|||
useEffect, useMemo, useState, |
|||
} from 'react'; |
|||
import { useSelector } from 'react-redux'; |
|||
import { FORUM_CONTRACT } from 'concordia-shared/src/constants/contracts/ContractNames'; |
|||
import PostList from '../../../../components/PostList'; |
|||
import { ITEMS_PER_PAGE } from '../../../../components/PaginationComponent'; |
|||
import { drizzle } from '../../../../redux/store'; |
|||
|
|||
const { |
|||
contracts: { |
|||
[FORUM_CONTRACT]: { |
|||
methods: { |
|||
getTopicPostCount: { cacheCall: getTopicPostCountChainData }, |
|||
getTopicPosts: { cacheCall: getTopicPostsChainData }, |
|||
}, |
|||
}, |
|||
}, |
|||
} = drizzle; |
|||
|
|||
const TopicPostList = (props) => { |
|||
const { |
|||
topicId, loading, focusOnPost, |
|||
} = props; |
|||
const [pageNumber, setPageNumber] = useState(1); |
|||
|
|||
const [topicPostCount, setTopicPostCount] = useState(null); |
|||
const [postIds, setPostIds] = useState([]); |
|||
|
|||
const [getTopicPostCountCallHash, setGetTopicPostCountCallHash] = useState(null); |
|||
const [getTopicPostsCallHash, setGetTopicPostsCallHash] = useState(null); |
|||
|
|||
const getTopicPostCountResult = useSelector((state) => state.contracts[FORUM_CONTRACT].getTopicPostCount[getTopicPostCountCallHash]); |
|||
const getTopicPostsResult = useSelector((state) => state.contracts[FORUM_CONTRACT].getTopicPosts[getTopicPostsCallHash]); |
|||
|
|||
useEffect(() => { |
|||
if (getTopicPostCountCallHash === null) { |
|||
setGetTopicPostCountCallHash(getTopicPostCountChainData(topicId)); |
|||
} |
|||
}, [getTopicPostCountCallHash, topicId]); |
|||
|
|||
useEffect(() => { |
|||
if (topicPostCount !== null && topicPostCount !== 0) { |
|||
const startIndex = ITEMS_PER_PAGE * (pageNumber - 1); |
|||
const endIndex = Math.min(ITEMS_PER_PAGE * pageNumber - 1, topicPostCount - 1); |
|||
setGetTopicPostsCallHash(getTopicPostsChainData(topicId, startIndex, endIndex)); |
|||
} |
|||
}, [pageNumber, topicId, topicPostCount]); |
|||
|
|||
useEffect(() => { |
|||
if (getTopicPostCountResult) { |
|||
setTopicPostCount(parseInt(getTopicPostCountResult.value, 10)); |
|||
} |
|||
}, [getTopicPostCountResult, topicPostCount]); |
|||
|
|||
useEffect(() => { |
|||
if (getTopicPostsResult) { |
|||
setPostIds(getTopicPostsResult.value.slice().map(Number)); |
|||
} |
|||
}, [getTopicPostsResult, topicPostCount]); |
|||
|
|||
const handlePageChange = (event, data) => { |
|||
setPageNumber(data.activePage); |
|||
}; |
|||
|
|||
return useMemo(() => { |
|||
if (postIds.length && postIds.length !== 0) { |
|||
return ( |
|||
<PostList |
|||
postIds={postIds} |
|||
numberOfItems={topicPostCount} |
|||
onPageChange={handlePageChange} |
|||
loading={loading} |
|||
focusOnPost={focusOnPost} |
|||
/> |
|||
); |
|||
} |
|||
return null; |
|||
}, [postIds, topicPostCount, loading, focusOnPost]); |
|||
}; |
|||
|
|||
export default TopicPostList; |
Loading…
Reference in new issue