Skip to content

Commit

Permalink
feat : 지도 편지 작성 (#395)
Browse files Browse the repository at this point in the history
* chore : nav z-index 수정

* chore : 알람 수정

* feat : 지도 편지 생성

* feat : 지도 편지 작성 api 연동

* chore : 수정

* chore : 안 쓰는 error 수정
  • Loading branch information
HelloWook authored Dec 9, 2024
1 parent 18930a6 commit a16eb82
Show file tree
Hide file tree
Showing 11 changed files with 361 additions and 18 deletions.
2 changes: 1 addition & 1 deletion src/components/Common/NavigationBar/NavigationBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const NavigationBar = () => {
];

return (
<nav className="flex justify-around bg-white border-y p-3 max-w-[473px] fixed min-w-[375px] w-full bottom-0 z-10">
<nav className="flex justify-around bg-white border-y p-3 max-w-[473px] fixed min-w-[375px] w-full bottom-0 z-[9999]">
{navItems.map((item) => (
<div className="flex justify-center flex-1" key={item.id}>
<NavigationItem
Expand Down
2 changes: 1 addition & 1 deletion src/components/Common/TextArea/TextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export const TextArea = ({
lineHeight: lineHeight
}}
ref={textAreaRef}
placeholder="편지를 작성하세요..."
placeholder="편지를 작성하세요."
value={value}
onChange={handleInputChange}
readOnly={isReadonly}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ type LettetProps = {
letter: string;
setFont: React.Dispatch<React.SetStateAction<string>>;
setLetter: React.Dispatch<React.SetStateAction<string>>;
description?: string;
setDescription?: React.Dispatch<React.SetStateAction<string>>;
};

export const LetterInputForm = ({
Expand All @@ -22,7 +24,9 @@ export const LetterInputForm = ({
font,
letter,
setFont,
setLetter
setLetter,
description,
setDescription
}: LettetProps) => {
return (
<>
Expand All @@ -45,6 +49,15 @@ export const LetterInputForm = ({
font={font}
/>
</div>

{description && setDescription && (
<input
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="힌트를 입력해주세요"
className={`z-10 w-full bg-transparent border-none focus:border-none focus:outline-none text-wrap ${font ? font : 'font-sans'}`}
/>
)}
</div>
<SelectSlider
font={font}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ export const PostLetterCotainer = () => {

const { addToast } = useToastStore();

const [title, setTitle] = useState<string>(storedTitle || ' ');
const [title, setTitle] = useState<string>(
storedTitle || '제목을 입력해주세요'
);
const [letter, setLetter] = useState<string>(storedLetter || '1');
const [letterContent, setLetterContent] = useState<string>(
storedContent || ' '
storedContent || '내용을 입력해주세요'
);
const [font, setFont] = useState<string>(storedFont || 'initial');

Expand All @@ -55,7 +57,7 @@ export const PostLetterCotainer = () => {
navigate(-1);
}}
handleSuccesClick={() => {
if (!title.trim() && !letterContent.trim()) {
if (!title.trim() || !letterContent.trim()) {
addToast(
'공백을 제외한 제목과 내용을 입력해주세요',
'warning'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { Margin } from '@/components/Common/Margin/Margin';
import { TextArea } from '@/components/Common/TextArea/TextArea';
import { TopBar } from '@/components/Common/TopBar/TopBar';
import { ThemeWrapper } from '@/components/CreatLetterPage/ThemeWrapper/ThemeWrapper';
import { SelectSlider } from '@/components/SelectItemPage/SelectSlider/SelectSlider';
import { useAutoSave, useToastStore } from '@/hooks';
import React, { useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { useLocalStorage } from '@/hooks/useLocalStorage'; // 로컬 스토리지 훅 가져오기

const CreateMapLetterCotainer = () => {
const { addToast } = useToastStore();

const { lat, lot } = useParams<{ lat: string; lot: string }>();

// useLocalStorage 훅을 사용하여 로컬 저장소에 값 저장 및 불러오기
const { setValue: saveTitle, storedValue: storedTitle } = useLocalStorage(
'maptitle',
''
);
const { setValue: saveLetterContent, storedValue: storedContent } =
useLocalStorage('mapcontent', '');
const { setValue: saveFont, storedValue: storedFont } = useLocalStorage(
'mapfont',
'initial'
);
const { setValue: saveLetter, storedValue: storedLetter } = useLocalStorage(
'mapletter',
'1'
);
const { setValue: saveDescription, storedValue: storedDescription } =
useLocalStorage('mapdescription', '');

// 위도와 경도를 로컬 스토리지에 저장
localStorage.setItem('maplat', lat?.slice(1) || '');
localStorage.setItem('maplot', lat?.slice(1) || '');

// 상태 관리
const [title, setTitle] = useState<string>(storedTitle || '');
const [letter, setLetter] = useState<string>(storedLetter || '1');
const [letterContent, setLetterContent] = useState<string>(
storedContent || ''
);
const [font, setFont] = useState<string>(storedFont || 'initial');
const [description, setDescription] = useState<string>(
storedDescription || ''
);

const navigate = useNavigate();

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const inputValue = e.target.value;
setTitle(inputValue);
};

const saveLetterData = () => {
saveTitle(title);
saveLetterContent(letterContent);
saveFont(font);
saveLetter(letter);
saveDescription(description);
};

useAutoSave(saveLetterData, 50000);

return (
<>
<TopBar
handleBackClick={() => {
navigate(-1);
}}
handleSuccesClick={() => {
if (
!title.trim() ||
!letterContent.trim() ||
!description.trim()
) {
addToast(
'공백을 제외한 제목과 내용을 입력해주세요',
'warning'
);
return;
}
saveLetterData();
navigate('/letter/map/select');
}}
/>

<ThemeWrapper themeId={Number(letter)}>
<>
<Margin top={20} />
<div className="relative flex flex-col justify-center w-9/12 m-auto py-9">
<input
onChange={handleChange}
value={title}
type="text"
placeholder="제목을 입력해주세요"
className={`z-10 w-full bg-transparent border-none focus:border-none focus:outline-none text-wrap ${font ? font : 'font-sans'}`}
maxLength={20}
/>
<img src={'/to_line.f4c129e6.svg'} />

<div className="relative z-10">
<TextArea
value={letterContent}
setValue={setLetterContent}
font={font}
/>
</div>

<input
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="힌트를 입력해주세요"
className={`z-10 w-full bg-transparent border-none focus:border-none focus:outline-none text-wrap ${font ? font : 'font-sans'}`}
/>
<img src={'/to_line.f4c129e6.svg'} />
</div>

<SelectSlider
font={font}
letter={letter}
setFont={setFont}
setLetter={setLetter}
/>
</>
</ThemeWrapper>
</>
);
};

export default CreateMapLetterCotainer;
27 changes: 27 additions & 0 deletions src/hooks/useCreateMapLetter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useMutation } from '@tanstack/react-query';
import { createMapLetter } from '@/service/letter/create/createMapLetter';
import { useToastStore } from './useToastStore';
import { useNavigate } from 'react-router-dom';

export const useCreateMapLetter = () => {
const { addToast } = useToastStore();
const navigate = useNavigate();
const mutation = useMutation({
mutationFn: createMapLetter,
onSuccess: () => {
addToast('지도 편지를 전송했습니다.', 'success');
navigate('/letter/success');
localStorage.removeItem('maptitle');
localStorage.removeItem('mapcontent');
localStorage.removeItem('mapdescription');
localStorage.removeItem('mapfont');
localStorage.removeItem('mapletter');
localStorage.removeItem('maplat');
localStorage.removeItem('maplot');
},
onError: () => {
addToast('지도 편지를 실패했습니다.', 'error');
}
});
return mutation;
};
13 changes: 2 additions & 11 deletions src/hooks/usePushNotification .ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,23 @@ export const usePushNotification = () => {
const { addToast } = useToastStore();

useEffect(() => {
const isNotificationSet = localStorage.getItem('isNofication');

if (isNotificationSet === 'true') {
return;
}

// 알림 권한 요청
Notification.requestPermission().then((permission) => {
if (permission === 'granted') {
getToken(firebaseMessaging, {
vapidKey: import.meta.env.VITE_FCM_VAPID_KEY
})
.then((token) => {
localStorage.setItem('isNofication', 'true');
addToast('알람 설정을 성공했습니다.', 'success');
postToken({ token });
})
.catch((error) => {
addToast('알람 설정에 실패했습니다.', 'error');
console.error(error);
});
} else {
addToast('알람 설정을 거부했습니다.', 'success');
addToast('알람 설정을 거부했습니다.', 'warning');
}
});

// 알림 메시지를 받을 때마다 처리
onMessage(firebaseMessaging, (payload) => {
if (payload.notification) {
addToast(`${payload.notification.body}`, 'success');
Expand Down
10 changes: 10 additions & 0 deletions src/pages/Map/Create/CreateMapLetterPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import CreateMapLetterCotainer from '@/components/CreateMapLetter/CreateMapLetterCotainer/CreateMapLetterCotainer';
import React from 'react';

export const CreateMapLetterPage = () => {
return (
<div className="w-full h-full">
<CreateMapLetterCotainer />
</div>
);
};
Loading

0 comments on commit a16eb82

Please sign in to comment.