mirror of
https://github.com/rjNemo/devbook_ts
synced 2026-06-06 02:36:39 +00:00
📬 Post (#12)
* update deploy test * switch picture to avatarUrl; addLike and removeLike methods placeholders * fix signup bug * fetch posts from db, can post and like posts * fetch posts from db, can post and like posts * add params to post route * connect to redux store * can add comments
This commit is contained in:
parent
3c00f9e999
commit
4880d2853d
9 changed files with 245 additions and 119 deletions
2
.github/workflows/deploy.yml
vendored
2
.github/workflows/deploy.yml
vendored
|
|
@ -47,6 +47,6 @@ jobs:
|
||||||
- name: Deploy to Firebase
|
- name: Deploy to Firebase
|
||||||
uses: w9jds/firebase-action@master
|
uses: w9jds/firebase-action@master
|
||||||
with:
|
with:
|
||||||
args: deploy
|
args: deploy --only hosting
|
||||||
env:
|
env:
|
||||||
FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
|
FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
|
||||||
|
|
|
||||||
9
src/constants/collections.ts
Normal file
9
src/constants/collections.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
/**
|
||||||
|
* Register all firestore collections
|
||||||
|
*/
|
||||||
|
enum Collections {
|
||||||
|
USERS = 'users',
|
||||||
|
POSTS = 'posts',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Collections;
|
||||||
|
|
@ -6,8 +6,8 @@ import Repo from '../types/Repo';
|
||||||
/** Shorter dev interface */
|
/** Shorter dev interface */
|
||||||
export interface DevSummary {
|
export interface DevSummary {
|
||||||
id?: string;
|
id?: string;
|
||||||
displayName: string;
|
displayName?: string;
|
||||||
avatarUrl: string;
|
avatarUrl?: string;
|
||||||
description: string;
|
description: string;
|
||||||
status: string;
|
status: string;
|
||||||
company: string;
|
company: string;
|
||||||
|
|
@ -40,18 +40,15 @@ export const getDescription = (status?: string, company?: string): string => {
|
||||||
* new Dev() returns a placeholder used when initializing a new profile.
|
* new Dev() returns a placeholder used when initializing a new profile.
|
||||||
* id is not specified to not overwrite document uid.
|
* id is not specified to not overwrite document uid.
|
||||||
*/
|
*/
|
||||||
export class Dev implements IDev {
|
export const blankDev: IDev = {
|
||||||
id?: string;
|
isActive: true,
|
||||||
isActive = true;
|
status: 'Developer',
|
||||||
displayName = '';
|
company: '',
|
||||||
status = 'Developer';
|
description: '',
|
||||||
company = '';
|
location: '',
|
||||||
avatarUrl = '';
|
skills: [],
|
||||||
description = '';
|
github: '',
|
||||||
location = '';
|
links: {
|
||||||
skills: string[] = [];
|
|
||||||
github: string = '';
|
|
||||||
links: Links = {
|
|
||||||
website: '',
|
website: '',
|
||||||
instagram: '',
|
instagram: '',
|
||||||
facebook: '',
|
facebook: '',
|
||||||
|
|
@ -59,12 +56,12 @@ export class Dev implements IDev {
|
||||||
twitter: '',
|
twitter: '',
|
||||||
github: '',
|
github: '',
|
||||||
youtube: '',
|
youtube: '',
|
||||||
};
|
},
|
||||||
bio = '';
|
bio: '',
|
||||||
experiences: Experience[] = [];
|
experiences: [],
|
||||||
educations: Education[] = [];
|
educations: [],
|
||||||
repos: Repo[] = [];
|
repos: [],
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sample Dev for development and tests
|
* sample Dev for development and tests
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ import Comment from '../types/Comment';
|
||||||
*/
|
*/
|
||||||
interface Post {
|
interface Post {
|
||||||
id: string;
|
id: string;
|
||||||
userID: string;
|
userID?: string;
|
||||||
name: string;
|
name?: string;
|
||||||
text: string;
|
text: string;
|
||||||
picture: string;
|
avatarUrl?: string;
|
||||||
likes: string[];
|
likes: string[];
|
||||||
comments: Comment[];
|
comments: Comment[];
|
||||||
// date: Date;
|
// date: Date;
|
||||||
|
|
@ -20,21 +20,21 @@ interface Post {
|
||||||
export const dummyPost: Post = {
|
export const dummyPost: Post = {
|
||||||
id: '12',
|
id: '12',
|
||||||
userID: '42',
|
userID: '42',
|
||||||
picture:
|
avatarUrl:
|
||||||
'https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50?s=200',
|
'https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50?s=200',
|
||||||
name: 'John Doe',
|
name: 'John Doe',
|
||||||
text:
|
text:
|
||||||
'Lorem ipsum dolor sit amet consectetur adipisicing elit. Sint possimus corporis sunt necessitatibus! Minus nesciunt soluta suscipit nobis. Amet accusamus distinctio cupiditate blanditiis dolor? Illo perferendis eveniet cum cupiditate aliquam?',
|
'Lorem ipsum dolor sit amet consectetur adipisicing elit. Sint possimus corporis sunt necessitatibus! Minus nesciunt soluta suscipit nobis. Amet accusamus distinctio cupiditate blanditiis dolor? Illo perferendis eveniet cum cupiditate aliquam?',
|
||||||
comments: [
|
comments: [
|
||||||
{
|
{
|
||||||
picture:
|
avatarUrl:
|
||||||
'https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50?s=200',
|
'https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50?s=200',
|
||||||
name: 'John Doe',
|
name: 'John Doe',
|
||||||
text:
|
text:
|
||||||
'Lorem ipsum dolor sit amet consectetur adipisicing elit. Sintpossimus corporis sunt necessitatibus! Minus nesciunt solutasuscipit nobis. Amet accusamus distinctio cupiditate blanditiis dolor? Illo perferendis eveniet cum cupiditate aliquam?',
|
'Lorem ipsum dolor sit amet consectetur adipisicing elit. Sintpossimus corporis sunt necessitatibus! Minus nesciunt solutasuscipit nobis. Amet accusamus distinctio cupiditate blanditiis dolor? Illo perferendis eveniet cum cupiditate aliquam?',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
picture:
|
avatarUrl:
|
||||||
'https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50?s=200',
|
'https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50?s=200',
|
||||||
name: 'Ruidy Nemo',
|
name: 'Ruidy Nemo',
|
||||||
text:
|
text:
|
||||||
|
|
|
||||||
|
|
@ -1,59 +1,113 @@
|
||||||
import React, {FC} from 'react';
|
import React, {FC} from 'react';
|
||||||
import Post, {dummyPost as post} from '../models/Post';
|
// Routing
|
||||||
|
import {useParams, Link} from 'react-router-dom';
|
||||||
|
import Routes from '../constants/routes';
|
||||||
|
// Redux
|
||||||
|
import {compose} from '@reduxjs/toolkit';
|
||||||
|
import {connect} from 'react-redux';
|
||||||
|
import {firestoreConnect, WithFirestoreProps} from 'react-redux-firebase';
|
||||||
|
import {RootState} from '../store';
|
||||||
|
import Collections from '../constants/collections';
|
||||||
|
// Typing
|
||||||
|
import Post from '../models/Post';
|
||||||
import Comment from '../types/Comment';
|
import Comment from '../types/Comment';
|
||||||
|
// Form
|
||||||
|
import useForm from '../hooks';
|
||||||
|
|
||||||
|
interface IProps extends WithFirestoreProps {
|
||||||
|
post: Post;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display a Post and the related comments. Shows a form to add a comment.
|
* Display a Post and the related comments. Shows a form to add a comment.
|
||||||
*/
|
*/
|
||||||
const PostPage: FC<Post> = () => (
|
const PostPage: FC<IProps> = ({post, firestore}) => {
|
||||||
<section className="container">
|
const newComment: Comment = {
|
||||||
<a href="posts.html" className="btn btn-light">
|
name: post.name ?? 'error',
|
||||||
Back To Posts
|
avatarUrl: post.avatarUrl ?? 'error',
|
||||||
</a>
|
text: '',
|
||||||
|
};
|
||||||
|
|
||||||
<div className="post bg-white p-1 my-1">
|
const {formData, handleChange, resetForm} = useForm(newComment);
|
||||||
<div>
|
|
||||||
<a href="profile.html">
|
|
||||||
<img className="round-img" src={post.picture} alt={post.name} />
|
|
||||||
<h4>{post.name}</h4>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="my-1">{post.text}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="post-form">
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
<div className="post-form-header bg-primary">
|
e.preventDefault();
|
||||||
<h3>Leave A Comment</h3>
|
|
||||||
</div>
|
|
||||||
<form className="form my-1">
|
|
||||||
<textarea
|
|
||||||
name="text"
|
|
||||||
cols={30}
|
|
||||||
rows={5}
|
|
||||||
placeholder="Comment on this post"
|
|
||||||
></textarea>
|
|
||||||
<input type="submit" className="btn btn-dark my-1" value="Submit" />
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="posts">
|
firestore
|
||||||
{post.comments.map((c: Comment, i: number) => (
|
.update(`${Collections.POSTS}/${post.id}`, {
|
||||||
<div className="post bg-white p-1 my-1" key={i}>
|
comments: [...post.comments, formData],
|
||||||
<div>
|
})
|
||||||
<a href="profile.html">
|
.then(() => resetForm())
|
||||||
<img className="round-img" src={c.picture} alt={c.name} />
|
.catch(err => console.error(err));
|
||||||
<h4>{c.name}</h4>
|
};
|
||||||
</a>
|
|
||||||
</div>
|
return (
|
||||||
<div>
|
<section className="container">
|
||||||
<p className="my-1">{c.text}</p>
|
<Link to={Routes.POSTS} className="btn btn-light">
|
||||||
</div>
|
Back To Posts
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<div className="post bg-white p-1 my-1">
|
||||||
|
<div>
|
||||||
|
<Link to={`${Routes.PROFILE}/${post.userID}`}>
|
||||||
|
<img className="round-img" src={post.avatarUrl} alt={post.name} />
|
||||||
|
<h4>{post.name}</h4>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
))}
|
<div>
|
||||||
</div>
|
<p className="my-1">{post.text}</p>
|
||||||
</section>
|
</div>
|
||||||
);
|
</div>
|
||||||
|
|
||||||
export default PostPage;
|
<div className="post-form">
|
||||||
|
<div className="post-form-header bg-primary">
|
||||||
|
<h3>Leave A Comment</h3>
|
||||||
|
</div>
|
||||||
|
<form className="form my-1" onSubmit={handleSubmit}>
|
||||||
|
<textarea
|
||||||
|
name="text"
|
||||||
|
cols={30}
|
||||||
|
rows={5}
|
||||||
|
placeholder="Comment on this post"
|
||||||
|
value={formData.text}
|
||||||
|
onChange={handleChange}
|
||||||
|
></textarea>
|
||||||
|
<input type="submit" className="btn btn-dark my-1" value="Submit" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="posts">
|
||||||
|
{post.comments?.map((c: Comment, i: number) => (
|
||||||
|
<div className="post bg-white p-1 my-1" key={i}>
|
||||||
|
<div>
|
||||||
|
<a href="profile.html">
|
||||||
|
<img className="round-img" src={c.avatarUrl} alt={c.name} />
|
||||||
|
<h4>{c.name}</h4>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="my-1">{c.text}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container to fetch id params from thr URI and pass it to Profile page
|
||||||
|
*/
|
||||||
|
const PostPageContainer: FC = () => {
|
||||||
|
const {id} = useParams();
|
||||||
|
const Component = compose<FC>(
|
||||||
|
firestoreConnect(() => [`${Collections.POSTS}/${id}`]),
|
||||||
|
connect(({firestore: {data}}: RootState) => ({
|
||||||
|
post: data.posts && {...data.posts[id], id},
|
||||||
|
})),
|
||||||
|
)(PostPage);
|
||||||
|
|
||||||
|
return <Component />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PostPageContainer;
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,65 @@
|
||||||
import React, {FC} from 'react';
|
import React, {FC, useState} from 'react';
|
||||||
import Post, {dummyPost as post} from '../models/Post';
|
// Redux
|
||||||
import Header from '../components/Header';
|
import {compose} from '@reduxjs/toolkit';
|
||||||
|
import {connect, useSelector} from 'react-redux';
|
||||||
|
import {firestoreConnect, WithFirestoreProps} from 'react-redux-firebase';
|
||||||
|
import {RootState} from '../store';
|
||||||
|
import {selectProfile} from '../store/firebase';
|
||||||
|
// Routing
|
||||||
|
import {Link} from 'react-router-dom';
|
||||||
|
import Routes from '../constants/routes';
|
||||||
|
// Style
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||||
import {faThumbsUp, faThumbsDown} from '@fortawesome/free-solid-svg-icons';
|
import {faThumbsUp} from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import Header from '../components/Header';
|
||||||
|
// Typing
|
||||||
|
import Post, {dummyPost as post} from '../models/Post';
|
||||||
|
import Collections from '../constants/collections';
|
||||||
|
|
||||||
|
interface IProps extends WithFirestoreProps {
|
||||||
|
posts: Post[];
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* A Dev's Posts list
|
* A Dev's Posts list
|
||||||
*/
|
*/
|
||||||
const Posts: FC = () => {
|
const Posts: FC<IProps> = ({posts, firestore, firebase}) => {
|
||||||
const posts: Post[] = [post, post];
|
const [text, setText] = useState('');
|
||||||
|
const {avatarUrl, displayName} = useSelector(selectProfile);
|
||||||
|
const id = firebase.auth().currentUser?.uid;
|
||||||
|
|
||||||
|
const newPost = {
|
||||||
|
userID: id,
|
||||||
|
name: displayName,
|
||||||
|
text,
|
||||||
|
avatarUrl,
|
||||||
|
likes: [] as string[],
|
||||||
|
comments: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) =>
|
||||||
|
setText(e.target.value);
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
firestore
|
||||||
|
.add(Collections.POSTS, newPost)
|
||||||
|
.then(() => setText(''))
|
||||||
|
.catch(err => console.error(err));
|
||||||
|
};
|
||||||
|
// const removeLike = (e: React.MouseEvent<HTMLButtonElement>) =>
|
||||||
|
// new Error('Not implemented yet.');
|
||||||
|
const addLike = (postID: string) => (
|
||||||
|
e: React.MouseEvent<HTMLButtonElement>,
|
||||||
|
) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const post = posts.find(p => p.id === postID);
|
||||||
|
|
||||||
|
if (post) {
|
||||||
|
firestore
|
||||||
|
.update(`${Collections.POSTS}/${post.id}`, {likes: [...post.likes, id]})
|
||||||
|
.catch(err => console.error(err));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="container">
|
<section className="container">
|
||||||
|
|
@ -18,41 +69,55 @@ const Posts: FC = () => {
|
||||||
<h3>Say Something</h3>
|
<h3>Say Something</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form className="form my-1">
|
<form className="form my-1" onSubmit={handleSubmit}>
|
||||||
<textarea cols={30} rows={5} placeholder="Create a post"></textarea>
|
<textarea
|
||||||
|
cols={30}
|
||||||
|
rows={5}
|
||||||
|
placeholder="Create a post"
|
||||||
|
value={text}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
<input type="submit" value="Submit" className="btn btn-dark my-1" />
|
<input type="submit" value="Submit" className="btn btn-dark my-1" />
|
||||||
<div className="posts">
|
|
||||||
{posts.map((post: Post) => (
|
|
||||||
<div className="post bg-white p-1 my-1" key={post.id}>
|
|
||||||
<div>
|
|
||||||
<a href="profile.html">
|
|
||||||
<img
|
|
||||||
src={post.picture}
|
|
||||||
alt={post.name}
|
|
||||||
className="round-img"
|
|
||||||
/>
|
|
||||||
<h4>{post.name}</h4>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="my-1">{post.text}</p>
|
|
||||||
<button className="btn btn-light">
|
|
||||||
<FontAwesomeIcon icon={faThumbsUp} /> {post.likes.length}
|
|
||||||
</button>
|
|
||||||
<button className="btn btn-light">
|
|
||||||
<FontAwesomeIcon icon={faThumbsDown} />
|
|
||||||
</button>
|
|
||||||
<a href="post.html" className="btn btn-primary">
|
|
||||||
Discussion
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
<div className="posts">
|
||||||
|
{posts?.map((post: Post) => (
|
||||||
|
<div className="post bg-white p-1 my-1" key={post.id}>
|
||||||
|
<div>
|
||||||
|
<Link to={`${Routes.PROFILE}/${post.userID}`}>
|
||||||
|
<img
|
||||||
|
src={post.avatarUrl}
|
||||||
|
alt={post.name}
|
||||||
|
className="round-img"
|
||||||
|
/>
|
||||||
|
<h4>{post.name}</h4>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="my-1">{post.text}</p>
|
||||||
|
<button className="btn btn-light" onClick={addLike(post.id)}>
|
||||||
|
<FontAwesomeIcon icon={faThumbsUp} /> {post.likes?.length}
|
||||||
|
</button>
|
||||||
|
{/* <button className="btn btn-light" onClick={removeLike}>
|
||||||
|
<FontAwesomeIcon icon={faThumbsDown} />
|
||||||
|
</button> */}
|
||||||
|
<Link
|
||||||
|
to={`${Routes.POST}/${post.id}`}
|
||||||
|
className="btn btn-primary"
|
||||||
|
>
|
||||||
|
Discussion
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Posts;
|
export default compose<FC>(
|
||||||
|
firestoreConnect(() => ['posts']), // or { collection: 'users' }
|
||||||
|
connect((state: RootState) => ({
|
||||||
|
posts: state.firestore.ordered.posts,
|
||||||
|
})),
|
||||||
|
)(Posts);
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,18 @@ import Routes from '../constants/routes';
|
||||||
// Redux
|
// Redux
|
||||||
import {WithFirebaseProps} from 'react-redux-firebase';
|
import {WithFirebaseProps} from 'react-redux-firebase';
|
||||||
import {enhance} from '../store/firebase';
|
import {enhance} from '../store/firebase';
|
||||||
import User, {newUser} from '../models/User';
|
|
||||||
// Style
|
// Style
|
||||||
import GoogleButton from 'react-google-button';
|
import GoogleButton from 'react-google-button';
|
||||||
import Alert from '../components/Alert';
|
import Alert from '../components/Alert';
|
||||||
import Header from '../components/Header';
|
import Header from '../components/Header';
|
||||||
|
// Typing
|
||||||
|
import IDev, {blankDev} from '../models/Dev';
|
||||||
|
import User, {newUser} from '../models/User';
|
||||||
// Form
|
// Form
|
||||||
import useForm from '../hooks';
|
import useForm from '../hooks';
|
||||||
import {Dev} from '../models/Dev';
|
|
||||||
|
|
||||||
// extends withFirebaseProps type to ad profile info
|
// extends withFirebaseProps type to ad profile info
|
||||||
interface IProps extends Dev, WithFirebaseProps<User> {
|
interface IProps extends IDev, WithFirebaseProps<User> {
|
||||||
isEmpty: boolean;
|
isEmpty: boolean;
|
||||||
isLoaded: boolean;
|
isLoaded: boolean;
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +58,7 @@ const SignUp: FC<IProps> = ({firebase, isEmpty, isLoaded, isActive}) => {
|
||||||
firebase
|
firebase
|
||||||
.createUser({email, password}, newUser(name, email))
|
.createUser({email, password}, newUser(name, email))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
firebase.updateProfile(new Dev(), {useSet: true, merge: true});
|
firebase.updateProfile(blankDev, {useSet: true, merge: true});
|
||||||
resetForm();
|
resetForm();
|
||||||
})
|
})
|
||||||
.catch(err => setError(err));
|
.catch(err => setError(err));
|
||||||
|
|
@ -82,7 +83,7 @@ const SignUp: FC<IProps> = ({firebase, isEmpty, isLoaded, isActive}) => {
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (!exists)
|
if (!exists)
|
||||||
firebase.updateProfile(new Dev(), {useSet: true, merge: true});
|
firebase.updateProfile(blankDev, {useSet: true, merge: true});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(err => setError(err));
|
.catch(err => setError(err));
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ const Router: FC = () => (
|
||||||
component={AddExperience}
|
component={AddExperience}
|
||||||
/>
|
/>
|
||||||
<PrivateRoute exact path={Routes.ADD_EDUCATION} component={AddEducation} />
|
<PrivateRoute exact path={Routes.ADD_EDUCATION} component={AddEducation} />
|
||||||
<PrivateRoute exact path={Routes.POST} component={PostPage} />
|
<PrivateRoute exact path={`${Routes.POST}/:id`} component={PostPage} />
|
||||||
<PrivateRoute exact path={Routes.POSTS} component={Posts} />
|
<PrivateRoute exact path={Routes.POSTS} component={Posts} />
|
||||||
<Route component={NotFound} />
|
<Route component={NotFound} />
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ interface Comment {
|
||||||
// userID: string;
|
// userID: string;
|
||||||
text: string;
|
text: string;
|
||||||
name: string;
|
name: string;
|
||||||
picture: string;
|
avatarUrl: string;
|
||||||
// date: Date;
|
// date: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue