juni
TeamProject_뉴스피드 프로젝트 본문
import { useRef, useState } from 'react';
import { Quill } from 'react-quill';
import 'react-quill/dist/quill.snow.css';
import { useDispatch, useSelector } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import { createPost } from '../../redux/slices/postSlice';
import { supabase } from '../../service/supabase';
import {
Button,
ButtonContainer,
Container,
// Subtitle,
Form,
Header,
Input,
Label,
StyledReactQuill,
Title,
Wrapper
} from './WritePostPage.styles';
const ImageBlot = Quill.import('formats/image');
ImageBlot.className = 'custom-image';
Quill.register(ImageBlot, true);
const WritePostPage = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const { user } = useSelector((state) => state.user.userInfo);
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const [imageURL, setImageURL] = useState('');
const [previewImageURL, setPreviewImageURL] = useState(null);
const quillRef = useRef(null);
const imageInputRef = useRef(null);
const handleCreatePost = () => {
if (title && content) {
dispatch(createPost({ title, content, imageURL: imageURL, name: user.name }));
navigate('/');
} else {
alert('제목과 내용을 입력해주세요.');
}
};
const handleGoBack = (e) => {
const confirmGoBack = window.confirm('변경 사항이 저장되지 않을 수 있습니다. 페이지를 나가시겠습니까?');
if (confirmGoBack) {
navigate('/');
} else {
e.preventDefault();
}
};
async function handleImageInputChange(files) {
const [file] = files;
if (!file) {
setPreviewImageURL(null);
return;
}
const previewURL = URL.createObjectURL(file);
setPreviewImageURL(previewURL);
const { data } = await supabase.storage.from('avatars').upload(`avatar_${Date.now()}.png`, file);
const imageURL = `https://dkodekduyiphnphkezzv.supabase.co/storage/v1/object/public/avatars/${data.path}`;
setImageURL(imageURL);
}
const handleImageButtonClick = (e) => {
e.preventDefault();
imageInputRef.current.click();
};
const modules = {
toolbar: [
[{ header: '1' }, { header: '2' }, { font: [] }],
[{ size: [] }],
['underline', 'strike', 'blockquote', 'code-block'],
[{ color: [] }, { background: [] }],
['image']
]
};
return (
<Wrapper>
<Container>
<Header>
<Title>썸네일 이미지 미리보기</Title>
<br />
{previewImageURL && <img src={previewImageURL} alt="Preview" style={{ maxWidth: '200px' }} />}
</Header>
<Form>
<Label>게시글 제목</Label>
<Input type="text" value={title} onChange={(e) => setTitle(e.target.value)} />
<Label>게시글 내용</Label>
<StyledReactQuill ref={quillRef} value={content} onChange={setContent} modules={modules} />
<ButtonContainer>
<Button onClick={handleCreatePost}>업로드</Button>
<Button onClick={handleImageButtonClick}>썸네일 이미지 업로드</Button>
<input
onChange={(e) => handleImageInputChange(e.target.files)}
type="file"
ref={imageInputRef}
style={{ display: 'none' }}
/>
<Button
onClick={(e) => {
handleGoBack(e);
}}
>
뒤로가기
</Button>
</ButtonContainer>
</Form>
</Container>
</Wrapper>
);
};
export default WritePostPage;
import { useState, useEffect, useRef } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useParams, useNavigate } from 'react-router-dom';
import { fetchPosts, deletePost, updatePost } from '../../redux/slices/postSlice';
import { supabase } from '../../service/supabase';
import {
Wrapper,
Container,
Header,
Title,
// Subtitle,
Form,
Label,
Input,
StyledReactQuill,
ButtonContainer,
Button
} from './EditPostPage.styles';
const EditPostPage = () => {
const { id } = useParams();
const postId = parseInt(id, 10);
const dispatch = useDispatch();
const navigate = useNavigate();
const post = useSelector((state) => state.posts.posts.find((post) => post.id === postId));
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const [imageURL, setImageURL] = useState('');
const [previewImageURL, setPreviewImageURL] = useState(null);
const quillRef = useRef(null);
const imageInputRef = useRef(null);
const handleUpdatePost = () => {
if (title && content) {
dispatch(updatePost({ id: postId, title, content, imageURL: imageURL }));
navigate('/');
} else {
alert('제목과 내용을 입력해주세요.');
}
};
const handleDeletePost = (e) => {
const confirmDelete = confirm('게시글을 삭제하시겠습니까?');
if (confirmDelete) {
dispatch(deletePost(postId));
navigate('/');
} else {
e.preventDefault();
}
};
const handleGoBack = (e) => {
const confirmGoBack = confirm('변경 사항이 저장되지 않을 수 있습니다. 페이지를 나가시겠습니까?');
if (confirmGoBack) {
navigate('/');
} else {
e.preventDefault();
}
};
async function handleImageInputChange(files) {
const [file] = files;
if (!file) {
setPreviewImageURL(null);
return;
}
const previewURL = URL.createObjectURL(file);
setPreviewImageURL(previewURL);
const { data } = await supabase.storage.from('avatars').upload(`avatar_${Date.now()}.png`, file);
const imageURL = `https://dkodekduyiphnphkezzv.supabase.co/storage/v1/object/public/avatars/${data.path}`;
setImageURL(imageURL);
}
const handleImageButtonClick = (e) => {
e.preventDefault();
imageInputRef.current.click();
};
const modules = {
toolbar: [
[{ header: '1' }, { header: '2' }, { font: [] }],
[{ size: [] }],
['underline', 'strike', 'blockquote', 'code-block'],
[{ color: [] }, { background: [] }],
['image']
]
};
useEffect(() => {
if (!post) {
dispatch(fetchPosts());
} else {
setTitle(post.title);
setContent(post.content);
setImageURL(post.images || '');
setPreviewImageURL(post.images);
}
}, [dispatch, post]);
return (
<Wrapper>
<Container>
<Header>
<Title>썸네일 이미지 미리보기</Title>
<br />
{previewImageURL ? (
<img src={previewImageURL} alt="Preview" style={{ maxWidth: '500px' }} />
) : (
post && post.images && <img src={post.images} style={{ maxWidth: '500px' }} />
)}
</Header>
<Form>
<Label>게시글 제목</Label>
<Input type="text" value={title} onChange={(e) => setTitle(e.target.value)} />
<Label>게시글 내용</Label>
<StyledReactQuill ref={quillRef} value={content} onChange={setContent} modules={modules} />
<ButtonContainer>
<Button onClick={handleUpdatePost}>수정</Button>
<Button onClick={handleImageButtonClick}>썸네일 이미지 업로드</Button>
<input
onChange={(e) => handleImageInputChange(e.target.files)}
type="file"
ref={imageInputRef}
style={{ display: 'none' }}
/>
<Button
onClick={(e) => {
handleDeletePost(e);
}}
isRed
>
삭제
</Button>
<Button
onClick={(e) => {
handleGoBack(e);
}}
>
뒤로가기
</Button>
</ButtonContainer>
</Form>
</Container>
</Wrapper>
);
};
export default EditPostPage;
게시글 추가 / 수정 페이지에서 사용자의 편의성을 위해 되묻는 기능 , 변경 사항 새로고침 방지로 유지 기능
섬네일 이미지 기능 등 전체적인 구조를 개선함
그 외에도 자잘한 버그 및 홈페이지 관련 로직 개선함
위는 예시화면임
'프로젝트 > 미니 프로젝트' 카테고리의 다른 글
TeamProject_뉴스피드 프로젝트 (0) | 2024.06.10 |
---|---|
TeamProject_뉴스피드 프로젝트 (0) | 2024.06.06 |
TeamProject_뉴스피드 프로젝트 (0) | 2024.06.04 |
TeamProject_뉴스피드 프로젝트 (0) | 2024.06.03 |
TeamProject_뉴스피드 프로젝트 (0) | 2024.06.01 |