diff --git a/components/post/editor.jsx b/components/post/editor.jsx index 59b5b6f..4765e01 100644 --- a/components/post/editor.jsx +++ b/components/post/editor.jsx @@ -1,51 +1,88 @@ import React, { useState } from 'react'; import { useCurrentUser } from '@/hooks/index'; -export default function PostEditor() { +export default function PostEditor({ edit, makeEdit, text, Id }) { const [user] = useCurrentUser(); const [msg, setMsg] = useState(null); if (!user) { - return ( -
- Please sign in to post -
- ); + return
Please sign in to post
; } + const discard = () => { + makeEdit(); + }; + async function hanldeSubmit(e) { e.preventDefault(); - const body = { - content: e.currentTarget.content.value, - }; - if (!e.currentTarget.content.value) return; - e.currentTarget.content.value = ''; - const res = await fetch('/api/posts', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(body), - }); - if (res.ok) { - setMsg('Posted!'); - setTimeout(() => setMsg(null), 5000); + if (edit === true) { + const body = { + content: e.currentTarget.content.value, + postId: Id, + }; + // if (!e.currentTarget.content.value) return; + const res = await fetch('/api/posts', { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + if (res.ok) { + makeEdit('Edited!', body.content); + } else { + makeEdit(res.text(), text); + } + } else { + const body = { + content: e.currentTarget.content.value, + }; + if (!e.currentTarget.content.value) return; + e.currentTarget.content.value = ''; + const res = await fetch('/api/posts', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + if (res.ok) { + setMsg('Posted!'); + setTimeout(() => setMsg(null), 5000); + } } } return ( <> -

- {msg} -

-
+

{msg}

+ - + + {edit === true ? ( + + ) : null}
); diff --git a/components/post/posts.jsx b/components/post/posts.jsx index 39c5952..513c1df 100644 --- a/components/post/posts.jsx +++ b/components/post/posts.jsx @@ -1,43 +1,94 @@ -import React from 'react'; +import React, { useState } from 'react'; import { useSWRInfinite } from 'swr'; import Link from 'next/link'; import { useUser } from '@/hooks/index'; import fetcher from '@/lib/fetch'; +import { useCurrentUser } from '@/hooks/index'; import { defaultProfilePicture } from '@/lib/default'; +import PostEditor from './editor'; function Post({ post }) { + const [edit, setEdit] = useState(false); + const [content, setContent] = useState(post.content); + const [msg, setMsg] = useState(''); const user = useUser(post.creatorId); + const [currUser] = useCurrentUser(); + const makeEdit = (msg, text) => { + setEdit(false); + setMsg(msg); + setTimeout(() => setMsg(null), 1500); + if (text) setContent(text); + }; return ( <> -
+
+

{msg}

{user && ( - {user.name} + {user.name} {user.name} )} -

- {post.content} -

+ {edit === true ? ( + + ) : ( +

{content}

+ )} {new Date(post.createdAt).toLocaleString()} + {user?._id === currUser?._id && ( + + )}
); @@ -46,38 +97,34 @@ function Post({ post }) { const PAGE_SIZE = 10; export function usePostPages({ creatorId } = {}) { - return useSWRInfinite((index, previousPageData) => { - // reached the end - if (previousPageData && previousPageData.posts.length === 0) return null; + return useSWRInfinite( + (index, previousPageData) => { + // reached the end + if (previousPageData && previousPageData.posts.length === 0) return null; - // first page, previousPageData is null - if (index === 0) { - return `/api/posts?limit=${PAGE_SIZE}${ - creatorId ? `&by=${creatorId}` : '' - }`; - } + // first page, previousPageData is null + if (index === 0) { + return `/api/posts?limit=${PAGE_SIZE}${creatorId ? `&by=${creatorId}` : ''}`; + } - // using oldest posts createdAt date as cursor - // We want to fetch posts which has a datethat is - // before (hence the .getTime() - 1) the last post's createdAt - const from = new Date( - new Date( - previousPageData.posts[previousPageData.posts.length - 1].createdAt, - ).getTime() - 1, - ).toJSON(); + // using oldest posts createdAt date as cursor + // We want to fetch posts which has a datethat is + // before (hence the .getTime() - 1) the last post's createdAt + const from = new Date( + new Date(previousPageData.posts[previousPageData.posts.length - 1].createdAt).getTime() - 1 + ).toJSON(); - return `/api/posts?from=${from}&limit=${PAGE_SIZE}${ - creatorId ? `&by=${creatorId}` : '' - }`; - }, fetcher, { - refreshInterval: 10000, // Refresh every 10 seconds - }); + return `/api/posts?from=${from}&limit=${PAGE_SIZE}${creatorId ? `&by=${creatorId}` : ''}`; + }, + fetcher, + { + refreshInterval: 10000, // Refresh every 10 seconds + } + ); } export default function Posts({ creatorId }) { - const { - data, error, size, setSize, - } = usePostPages({ creatorId }); + const { data, error, size, setSize } = usePostPages({ creatorId }); const posts = data ? data.reduce((acc, val) => [...acc, ...val.posts], []) : []; const isLoadingInitialData = !data && !error; @@ -87,19 +134,20 @@ export default function Posts({ creatorId }) { return (
- {posts.map((post) => )} + {posts.map((post) => ( + + ))} {!isReachingEnd && ( - + )}
); diff --git a/db/post.js b/db/post.js index 7c04b9b..41abf30 100644 --- a/db/post.js +++ b/db/post.js @@ -17,11 +17,29 @@ export async function getPosts(db, from = new Date(), by, limit) { .toArray(); } +export async function editPost(db, { content, postId }) { + return db + .collection('posts') + .findOneAndUpdate( + { _id: postId }, + { + $set: { + content, + }, + }, + { returnOriginal: false }, + ) + .then(({ value }) => value); +} + export async function insertPost(db, { content, creatorId }) { - return db.collection('posts').insertOne({ - _id: nanoid(12), - content, - creatorId, - createdAt: new Date(), - }).then(({ ops }) => ops[0]); + return db + .collection('posts') + .insertOne({ + _id: nanoid(12), + content, + creatorId, + createdAt: new Date(), + }) + .then(({ ops }) => ops[0]); } diff --git a/lib/default.js b/lib/default.js index 15081ae..dd0363f 100644 --- a/lib/default.js +++ b/lib/default.js @@ -1,2 +1,2 @@ // https://github.com/tobiaslins/avatar -export const defaultProfilePicture = (id) => `https://avatar.tobi.sh/${id}` +export const defaultProfilePicture = (id) => `https://avatar.tobi.sh/${id}`; diff --git a/lib/mail.js b/lib/mail.js index 41b5a10..f26512e 100644 --- a/lib/mail.js +++ b/lib/mail.js @@ -8,7 +8,7 @@ sgMail.setApiKey(process.env.SENDGRID_API_KEY); export async function sendMail(msg) { try { await sgMail.send(msg); - } catch(e) { - throw new Error(`Could not send email: ${e.message}`) + } catch (e) { + throw new Error(`Could not send email: ${e.message}`); } } diff --git a/pages/api/posts/index.js b/pages/api/posts/index.js index a7fe4ca..d5603bd 100644 --- a/pages/api/posts/index.js +++ b/pages/api/posts/index.js @@ -1,6 +1,6 @@ import nc from 'next-connect'; import { all } from '@/middlewares/index'; -import { getPosts, insertPost } from '@/db/index'; +import { editPost, getPosts, insertPost } from '@/db/index'; const handler = nc(); @@ -20,9 +20,26 @@ handler.get(async (req, res) => { // This is safe to cache because from defines // a concrete range of posts res.setHeader('cache-control', `public, max-age=${maxAge}`); + return null; } res.send({ posts }); + return posts; +}); + +handler.patch(async (req, res) => { + if (!req.user) { + return res.status(401).send('unauthenticated'); + } + + if (!req.body.content) return res.status(400).send('You must write something'); + + const post = await editPost(req.db, { + content: req.body.content, + postId: req.body.postId, + }); + + return res.json({ post }); }); handler.post(async (req, res) => { diff --git a/pages/api/user/index.js b/pages/api/user/index.js index 1e41e63..36d3ae5 100644 --- a/pages/api/user/index.js +++ b/pages/api/user/index.js @@ -28,6 +28,7 @@ handler.get(async (req, res) => { if (!req.user) return res.json({ user: null }); const { password, ...u } = req.user; res.json({ user: u }); + return null; }); handler.patch(upload.single('profilePicture'), async (req, res) => {