mirror of https://gitlab.com/ecentrics/concordia
Apostolos Fanakis
4 years ago
9 changed files with 205 additions and 13 deletions
@ -0,0 +1,49 @@ |
|||
import React from 'react'; |
|||
import PropTypes from 'prop-types'; |
|||
import { List } from 'semantic-ui-react'; |
|||
import { PLACEHOLDER_TYPE_POST, PLACEHOLDER_TYPE_TOPIC } from '../../constants/PlaceholderTypes'; |
|||
|
|||
const Placeholder = (props) => { |
|||
const { placeholderType, extra } = props; |
|||
|
|||
switch (placeholderType) { |
|||
case PLACEHOLDER_TYPE_TOPIC: |
|||
return ( |
|||
<> |
|||
<List.Header> |
|||
<List.Icon name="right triangle" /> |
|||
topicSubject |
|||
</List.Header> |
|||
<List.Content> |
|||
username |
|||
Number of Replies |
|||
timestamp |
|||
</List.Content> |
|||
</> |
|||
); |
|||
case PLACEHOLDER_TYPE_POST: |
|||
return ( |
|||
<div>LOADING POST</div> |
|||
); |
|||
default: |
|||
return <div />; |
|||
} |
|||
}; |
|||
|
|||
const TopicPlaceholderExtra = PropTypes.PropTypes.shape({ |
|||
topicId: PropTypes.number.isRequired, |
|||
}); |
|||
|
|||
const PostPlaceholderExtra = PropTypes.PropTypes.shape({ |
|||
postIndex: PropTypes.number.isRequired, |
|||
}); |
|||
|
|||
Placeholder.propTypes = { |
|||
placeholderType: PropTypes.string.isRequired, |
|||
extra: PropTypes.oneOfType([ |
|||
TopicPlaceholderExtra.isRequired, |
|||
PostPlaceholderExtra.isRequired, |
|||
]), |
|||
}; |
|||
|
|||
export default Placeholder; |
@ -0,0 +1,52 @@ |
|||
import React, { useContext, useMemo } from 'react'; |
|||
import { List } from 'semantic-ui-react'; |
|||
import PropTypes from 'prop-types'; |
|||
import AppContext from '../../AppContext'; |
|||
|
|||
const TopicListRow = (props) => { |
|||
const { topicData, topicId } = props; |
|||
|
|||
const { |
|||
breeze: { |
|||
orbit: { |
|||
stores, |
|||
}, |
|||
}, |
|||
} = useContext(AppContext.Context); |
|||
|
|||
const topicSubject = useMemo(() => { |
|||
const topicsDb = Object.values(stores).find((store) => store.dbname === 'topics'); |
|||
|
|||
return topicsDb.get(topicId); |
|||
}, [stores, topicId]); |
|||
|
|||
return ( |
|||
<> |
|||
<List.Header> |
|||
<List.Icon name="right triangle" /> |
|||
{topicSubject && topicSubject.subject} |
|||
</List.Header> |
|||
<List.Content> |
|||
{topicData.username} |
|||
{topicData.numberOfReplies} |
|||
{' '} |
|||
replies |
|||
timestamp |
|||
</List.Content> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
const TopicData = PropTypes.PropTypes.shape({ |
|||
userAddress: PropTypes.string.isRequired, |
|||
username: PropTypes.string.isRequired, |
|||
timestamp: PropTypes.string.isRequired, |
|||
numberOfReplies: PropTypes.number.isRequired, |
|||
}); |
|||
|
|||
TopicListRow.propTypes = { |
|||
topicData: TopicData.isRequired, |
|||
topicId: PropTypes.number.isRequired, |
|||
}; |
|||
|
|||
export default TopicListRow; |
@ -0,0 +1,79 @@ |
|||
import React, { |
|||
useCallback, |
|||
useContext, useEffect, useMemo, useState, |
|||
} from 'react'; |
|||
import PropTypes from 'prop-types'; |
|||
import { useSelector } from 'react-redux'; |
|||
import { List } from 'semantic-ui-react'; |
|||
import AppContext from '../AppContext'; |
|||
import TopicListRow from './TopicListRow'; |
|||
import { PLACEHOLDER_TYPE_TOPIC } from '../../constants/PlaceholderTypes'; |
|||
import Placeholder from '../Placeholder'; |
|||
import './styles.css'; |
|||
import { useHistory } from 'react-router'; |
|||
|
|||
const TopicList = (props) => { |
|||
const { topicIds } = props; |
|||
const { drizzle: { contracts: { Forum: { methods: { getTopic } } } } } = useContext(AppContext.Context); |
|||
const [getTopicCallHashes, setGetTopicCallHashes] = useState([]); |
|||
const getTopicResults = useSelector((state) => state.contracts.Forum.getTopic); |
|||
const drizzleStatus = useSelector((state) => state.drizzleStatus); |
|||
const history = useHistory(); |
|||
|
|||
useEffect(() => { |
|||
// TODO: is the drizzleStatus check necessary? |
|||
if (drizzleStatus.initialized && !drizzleStatus.failed && getTopicCallHashes.length === 0) { |
|||
setGetTopicCallHashes(topicIds.map((topicId) => ({ |
|||
id: topicId, |
|||
hash: getTopic.cacheCall(topicId), |
|||
}))); |
|||
} |
|||
}, [drizzleStatus.failed, drizzleStatus.initialized, getTopic, getTopicCallHashes, topicIds]); |
|||
|
|||
const handleTopicClick = useCallback((topicId) => { |
|||
history.push(`/topics/${topicId}`); |
|||
}, [history]); |
|||
|
|||
const topics = useMemo(() => topicIds |
|||
.map((topicId) => { |
|||
const getTopicHash = getTopicCallHashes.find((getTopicCallHash) => getTopicCallHash.id === topicId); |
|||
|
|||
if (getTopicHash && getTopicResults[getTopicHash.hash] !== undefined) { |
|||
const topicData = { |
|||
userAddress: getTopicResults[getTopicHash.hash].value[0], |
|||
username: getTopicResults[getTopicHash.hash].value[1], |
|||
timestamp: getTopicResults[getTopicHash.hash].value[2] * 1000, |
|||
numberOfReplies: getTopicResults[getTopicHash.hash].value[3].length, |
|||
}; |
|||
return ( |
|||
<List.Item key={topicId} className="list-item" name={topicId} onClick={() => handleTopicClick(topicId)}> |
|||
<TopicListRow |
|||
topicData={topicData} |
|||
topicId={topicId} |
|||
/> |
|||
</List.Item> |
|||
); |
|||
} |
|||
|
|||
return ( |
|||
<List.Item key={topicId} className="list-item" name={topicId} onClick={() => handleTopicClick(topicId)}> |
|||
<Placeholder |
|||
placeholderType={PLACEHOLDER_TYPE_TOPIC} |
|||
extra={{ topicId }} |
|||
/> |
|||
</List.Item> |
|||
); |
|||
}), [getTopicCallHashes, getTopicResults, handleTopicClick, topicIds]); |
|||
|
|||
return ( |
|||
<List selection divided id="topic-list" size="big"> |
|||
{topics} |
|||
</List> |
|||
); |
|||
}; |
|||
|
|||
TopicList.propTypes = { |
|||
topicIds: PropTypes.arrayOf(PropTypes.number).isRequired, |
|||
}; |
|||
|
|||
export default TopicList; |
@ -0,0 +1,7 @@ |
|||
#topic-list{ |
|||
height: 100%; |
|||
} |
|||
|
|||
.list-item { |
|||
text-align: start; |
|||
} |
@ -0,0 +1,2 @@ |
|||
export const PLACEHOLDER_TYPE_TOPIC = 'PLACEHOLDER_TYPE_TOPIC'; |
|||
export const PLACEHOLDER_TYPE_POST = 'PLACEHOLDER_TYPE_POST'; |
@ -0,0 +1,3 @@ |
|||
#home-container { |
|||
height: 100%; |
|||
} |
Loading…
Reference in new issue