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