Browse Source

Init post creation UI

develop
Apostolos Fanakis 4 years ago
parent
commit
b99f132be9
  1. 3
      packages/concordia-app/public/locales/en/translation.json
  2. 159
      packages/concordia-app/src/components/PostCreate/index.jsx
  3. 17
      packages/concordia-app/src/components/PostCreate/styles.css
  4. 8
      packages/concordia-app/src/components/PostList/PostListRow/index.jsx
  5. 1
      packages/concordia-app/src/index.jsx
  6. 1
      packages/concordia-app/src/layouts/MainLayout/index.jsx
  7. 3
      packages/concordia-app/src/layouts/MainLayout/styles.css
  8. 18
      packages/concordia-app/src/views/Topic/TopicCreate/index.jsx
  9. 7
      packages/concordia-app/src/views/Topic/TopicView/index.jsx
  10. 8
      packages/concordia-app/src/views/Topic/TopicView/styles.css

3
packages/concordia-app/public/locales/en/translation.json

@ -2,6 +2,9 @@
"board.header.no.topics.message": "There are no topics yet!", "board.header.no.topics.message": "There are no topics yet!",
"board.sub.header.no.topics.guest": "Sign up and be the first to post.", "board.sub.header.no.topics.guest": "Sign up and be the first to post.",
"board.sub.header.no.topics.user": "Be the first to post.", "board.sub.header.no.topics.user": "Be the first to post.",
"post.create.form.send.button": "Post",
"post.form.content.field.placeholder": "Message",
"post.form.subject.field.placeholder": "Subject",
"post.list.row.author.pre": "Post by", "post.list.row.author.pre": "Post by",
"post.list.row.post.id": "#{{id}}", "post.list.row.post.id": "#{{id}}",
"register.card.header": "Sign Up", "register.card.header": "Sign Up",

159
packages/concordia-app/src/components/PostCreate/index.jsx

@ -0,0 +1,159 @@
import React, {
memo, useCallback, useEffect, useState,
} from 'react';
import {
Button, Feed, Form, Icon, Image, Input, TextArea,
} from 'semantic-ui-react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import determineKVAddress from '../../utils/orbitUtils';
import { USER_DATABASE } from '../../constants/OrbitDatabases';
import { FETCH_USER_DATABASE } from '../../redux/actions/peerDbReplicationActions';
import { USER_PROFILE_PICTURE } from '../../constants/UserDatabaseKeys';
import { breeze } from '../../redux/store';
import './styles.css';
const { orbit } = breeze;
const PostCreate = (props) => {
const { id: postId, initialPostSubject } = props;
const [postSubject, setPostSubject] = useState(initialPostSubject);
const [postContent, setPostContent] = useState('');
const [userProfilePictureUrl, setUserProfilePictureUrl] = useState();
const [posting, setPosting] = useState(false);
const userAddress = useSelector((state) => state.user.address);
const users = useSelector((state) => state.orbitData.users);
const dispatch = useDispatch();
const { t } = useTranslation();
useEffect(() => {
if (userAddress) {
determineKVAddress({ orbit, dbName: USER_DATABASE, userAddress })
.then((userOrbitAddress) => {
const userFound = users
.find((user) => user.id === userOrbitAddress);
if (userFound) {
setUserProfilePictureUrl(userFound[USER_PROFILE_PICTURE]);
} else {
dispatch({
type: FETCH_USER_DATABASE,
orbit,
dbName: USER_DATABASE,
userAddress,
});
}
})
.catch((error) => {
console.error('Error during determination of key-value DB address:', error);
});
}
}, [dispatch, userAddress, users]);
const handleInputChange = useCallback((event) => {
if (posting) {
return;
}
switch (event.target.name) {
case 'postSubject':
setPostSubject(event.target.value);
break;
case 'postContent':
setPostContent(event.target.value);
break;
default:
break;
}
}, [posting]);
const savePost = useCallback(() => {
if (postSubject === '' || postContent === '') {
return;
}
setPosting(true);
}, [postContent, postSubject]);
return (
<Feed>
<Feed.Event>
<Feed.Label className="post-profile-picture">
{userProfilePictureUrl
? (
<Image
avatar
src={userProfilePictureUrl}
/>
)
: (
<Icon
name="user circle"
size="big"
inverted
color="black"
verticalAlign="middle"
/>
)}
</Feed.Label>
<Feed.Content>
<Feed.Summary>
<div>
<Input
placeholder={t('post.form.subject.field.placeholder')}
name="postSubject"
className="subject-input"
size="mini"
value={postSubject}
onChange={handleInputChange}
/>
<span className="post-summary-meta-index">
{t('post.list.row.post.id', { id: postId })}
</span>
</div>
</Feed.Summary>
<Feed.Extra>
<Form>
<TextArea
placeholder={t('post.form.content.field.placeholder')}
name="postContent"
className="content-input"
size="mini"
rows={4}
value={postContent}
onChange={handleInputChange}
/>
</Form>
</Feed.Extra>
<Feed.Meta>
<Feed.Like>
<Form.Button
animated
type="button"
color="green"
disabled={posting}
onClick={savePost || postSubject === '' || postContent === ''}
>
<Button.Content visible>
{t('post.create.form.send.button')}
</Button.Content>
<Button.Content hidden>
<Icon name="send" />
</Button.Content>
</Form.Button>
</Feed.Like>
</Feed.Meta>
</Feed.Content>
</Feed.Event>
</Feed>
);
};
PostCreate.propTypes = {
id: PropTypes.number.isRequired,
initialPostSubject: PropTypes.string.isRequired,
};
export default memo(PostCreate);

17
packages/concordia-app/src/components/PostCreate/styles.css

@ -0,0 +1,17 @@
.post-profile-picture {
margin: 5px 0 0 0;
}
.post-summary-meta-index {
float: right;
font-size: 12px;
opacity: 0.4;
}
.post-summary-meta-date {
float: right !important;
}
.subject-input {
min-width: 300px;
}

8
packages/concordia-app/src/components/PostList/PostListRow/index.jsx

@ -26,7 +26,7 @@ const PostListRow = (props) => {
const [postAuthor, setPostAuthor] = useState(null); const [postAuthor, setPostAuthor] = useState(null);
const [timeAgo, setTimeAgo] = useState(null); const [timeAgo, setTimeAgo] = useState(null);
const [postSubject, setPostSubject] = useState(null); const [postSubject, setPostSubject] = useState(null);
const [postMessage, setPostMessage] = useState(null); const [postContent, setPostContent] = useState(null);
const [postAuthorMeta, setPostAuthorMeta] = useState(null); const [postAuthorMeta, setPostAuthorMeta] = useState(null);
const userAddress = useSelector((state) => state.user.address); const userAddress = useSelector((state) => state.user.address);
const posts = useSelector((state) => state.orbitData.posts); const posts = useSelector((state) => state.orbitData.posts);
@ -67,7 +67,7 @@ const PostListRow = (props) => {
if (postFound) { if (postFound) {
setPostSubject(postFound[POST_SUBJECT]); setPostSubject(postFound[POST_SUBJECT]);
setPostMessage(postFound[POST_CONTENT]); setPostContent(postFound[POST_CONTENT]);
} }
}, [postId, posts]); }, [postId, posts]);
@ -130,11 +130,11 @@ const PostListRow = (props) => {
: <Placeholder><Placeholder.Line length="medium" /></Placeholder>} : <Placeholder><Placeholder.Line length="medium" /></Placeholder>}
</Feed.Summary> </Feed.Summary>
<Feed.Extra> <Feed.Extra>
{postMessage} {postContent}
</Feed.Extra> </Feed.Extra>
</Feed.Content> </Feed.Content>
</Dimmer.Dimmable> </Dimmer.Dimmable>
), [loading, postAuthor, postAuthorMeta, postId, postMessage, postSubject, t, timeAgo]); ), [loading, postAuthor, postAuthorMeta, postId, postContent, postSubject, t, timeAgo]);
}; };
PostListRow.defaultProps = { PostListRow.defaultProps = {

1
packages/concordia-app/src/index.jsx

@ -5,6 +5,7 @@ import App from './App';
import store from './redux/store'; import store from './redux/store';
import * as serviceWorker from './utils/serviceWorker'; import * as serviceWorker from './utils/serviceWorker';
import LoadingScreen from './components/LoadingScreen'; import LoadingScreen from './components/LoadingScreen';
import './assets/css/index.css';
render( render(
<Suspense fallback={<LoadingScreen />}> <Suspense fallback={<LoadingScreen />}>

1
packages/concordia-app/src/layouts/MainLayout/index.jsx

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import MainLayoutMenu from './MainLayoutMenu'; import MainLayoutMenu from './MainLayoutMenu';
import './styles.css';
const MainLayout = (props) => { const MainLayout = (props) => {
const { children } = props; const { children } = props;

3
packages/concordia-app/src/layouts/MainLayout/styles.css

@ -0,0 +1,3 @@
#main-layout {
height: 100%;
}

18
packages/concordia-app/src/views/Topic/TopicCreate/index.jsx

@ -22,7 +22,7 @@ const TopicCreate = (props) => {
const transactionStack = useSelector((state) => state.transactionStack); const transactionStack = useSelector((state) => state.transactionStack);
const transactions = useSelector((state) => state.transactions); const transactions = useSelector((state) => state.transactions);
const [subjectInput, setSubjectInput] = useState(''); const [subjectInput, setSubjectInput] = useState('');
const [messageInput, setMessageInput] = useState(''); const [contentInput, setContentInput] = useState('');
const [topicSubjectInputEmptySubmit, setTopicSubjectInputEmptySubmit] = useState(false); const [topicSubjectInputEmptySubmit, setTopicSubjectInputEmptySubmit] = useState(false);
const [topicMessageInputEmptySubmit, setTopicMessageInputEmptySubmit] = useState(false); const [topicMessageInputEmptySubmit, setTopicMessageInputEmptySubmit] = useState(false);
const [createTopicCacheSendStackId, setCreateTopicCacheSendStackId] = useState(''); const [createTopicCacheSendStackId, setCreateTopicCacheSendStackId] = useState('');
@ -40,8 +40,8 @@ const TopicCreate = (props) => {
case 'subjectInput': case 'subjectInput':
setSubjectInput(event.target.value); setSubjectInput(event.target.value);
break; break;
case 'messageInput': case 'contentInput':
setMessageInput(event.target.value); setContentInput(event.target.value);
break; break;
default: default:
break; break;
@ -75,7 +75,7 @@ const TopicCreate = (props) => {
.then(() => postsDb .then(() => postsDb
.put(postId, { .put(postId, {
[POST_SUBJECT]: subjectInput, [POST_SUBJECT]: subjectInput,
[POST_CONTENT]: messageInput, [POST_CONTENT]: contentInput,
}, { pin: true })) }, { pin: true }))
.then(() => { .then(() => {
history.push(`/topics/${topicId}`); history.push(`/topics/${topicId}`);
@ -85,7 +85,7 @@ const TopicCreate = (props) => {
}); });
} }
} }
}, [createTopicCacheSendStackId, history, messageInput, posting, subjectInput, transactionStack, transactions]); }, [createTopicCacheSendStackId, history, contentInput, posting, subjectInput, transactionStack, transactions]);
const validateAndPost = useCallback(() => { const validateAndPost = useCallback(() => {
if (subjectInput === '') { if (subjectInput === '') {
@ -93,14 +93,14 @@ const TopicCreate = (props) => {
return; return;
} }
if (messageInput === '') { if (contentInput === '') {
setTopicMessageInputEmptySubmit(true); setTopicMessageInputEmptySubmit(true);
return; return;
} }
setPosting(true); setPosting(true);
setCreateTopicCacheSendStackId(createTopic.cacheSend(...[], { from: account })); setCreateTopicCacheSendStackId(createTopic.cacheSend(...[], { from: account }));
}, [account, messageInput, subjectInput]); }, [account, contentInput, subjectInput]);
return ( return (
<Container> <Container>
@ -125,11 +125,11 @@ const TopicCreate = (props) => {
</label> </label>
<TextArea <TextArea
id="form-topic-create-field-message" id="form-topic-create-field-message"
name="messageInput" name="contentInput"
className={topicMessageInputEmptySubmit className={topicMessageInputEmptySubmit
? 'form-textarea-required' ? 'form-textarea-required'
: ''} : ''}
value={messageInput} value={contentInput}
placeholder={t('topic.create.form.content.field.placeholder')} placeholder={t('topic.create.form.content.field.placeholder')}
rows={5} rows={5}
autoheight="true" autoheight="true"

7
packages/concordia-app/src/views/Topic/TopicView/index.jsx

@ -13,6 +13,7 @@ import { TOPICS_DATABASE, USER_DATABASE } from '../../../constants/OrbitDatabase
import determineKVAddress from '../../../utils/orbitUtils'; import determineKVAddress from '../../../utils/orbitUtils';
import { USER_PROFILE_PICTURE } from '../../../constants/UserDatabaseKeys'; import { USER_PROFILE_PICTURE } from '../../../constants/UserDatabaseKeys';
import { TOPIC_SUBJECT } from '../../../constants/TopicsDatabaseKeys'; import { TOPIC_SUBJECT } from '../../../constants/TopicsDatabaseKeys';
import PostCreate from '../../../components/PostCreate';
const { contracts: { Forum: { methods: { getTopic: { cacheCall: getTopicChainData } } } } } = drizzle; const { contracts: { Forum: { methods: { getTopic: { cacheCall: getTopicChainData } } } } } = drizzle;
const { orbit } = breeze; const { orbit } = breeze;
@ -161,6 +162,12 @@ const TopicView = (props) => {
</Step.Group> </Step.Group>
</Dimmer.Dimmable> </Dimmer.Dimmable>
<PostList postIds={postIds || []} loading={postIds === null} /> <PostList postIds={postIds || []} loading={postIds === null} />
{topicSubject !== null && postIds !== null && (
<PostCreate
id={postIds.length}
initialPostSubject={topicSubject}
/>
)}
</Container> </Container>
); );
}; };

8
packages/concordia-app/src/views/Topic/TopicView/styles.css

@ -1,3 +1,11 @@
#topic-container {
height: 100%;
}
#topic-grid {
height: 100%;
}
#author-placeholder { #author-placeholder {
width: 150px !important; width: 150px !important;
} }

Loading…
Cancel
Save