본문 바로가기

카테고리 없음

TeamProject_240605 (뉴스피드 프로젝트)

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);
    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);
    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;

 

게시글 추가 / 수정 페이지에서 사용자의 편의성을 위해 되묻는 기능 , 변경 사항 새로고침 방지로 유지 기능

섬네일 이미지 기능 등 전체적인 구조를 개선함

그 외에도 자잘한 버그 및 홈페이지 관련 로직 개선함

 

 

위는 예시화면임