Skip to content

Commit

Permalink
feat : 지도 타깃 편지 (#409)
Browse files Browse the repository at this point in the history
* feat : nofication hook 제거

* feat : fcm 토큰 등록

* feat : 지도 타겟 편지 개발

* chore : 사용하지 않는 콘솔 제거
  • Loading branch information
HelloWook authored Dec 9, 2024
1 parent b48cc64 commit 8eb0caa
Show file tree
Hide file tree
Showing 10 changed files with 268 additions and 68 deletions.
1 change: 1 addition & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

export const App = () => {
const queryClient = new QueryClient();

return (
<QueryClientProvider client={queryClient}>
<ToastContainer />
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 @@ -71,7 +71,7 @@ export const TextArea = ({
<React.Fragment key={index}>
<img
src={'/to_line.f4c129e6.svg'}
className="w-full"
className="z-30 w-full"
style={{
objectFit: 'contain'
}}
Expand Down
55 changes: 55 additions & 0 deletions src/components/Common/usePushNotification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { getToken, onMessage } from 'firebase/messaging';
import { firebaseMessaging } from '@/util/firebase';
import { postToken } from '@/service/nofication/postToken';

// 푸시 알림 권한 요청 및 토큰 가져오기
const handlePushNotificationPermission = () => {
if (Notification.permission === 'granted') {
// 권한이 허용된 경우
getToken(firebaseMessaging, {
vapidKey: import.meta.env.VITE_FCM_VAPID_KEY
})
.then((token) => {
console.log('알람 설정을 성공했습니다.');
postToken({ token }); // 서버에 토큰 전송
})
.catch((error) => {
console.error('FCM Error:', error);
});
} else if (Notification.permission === 'denied') {
console.log('알람 설정을 거부했습니다.');
} else {
// 권한을 아직 요청하지 않은 경우
Notification.requestPermission().then((permission) => {
if (permission === 'granted') {
getToken(firebaseMessaging, {
vapidKey: import.meta.env.VITE_FCM_VAPID_KEY
})
.then((token) => {
console.log('알람 설정을 성공했습니다.');
postToken({ token }); // 서버에 토큰 전송
})
.catch((error) => {
console.error('FCM Error:', error);
});
} else {
console.log('알람 설정을 거부했습니다.');
}
});
}
};

// 푸시 알림 메시지 수신 처리
const setupPushNotificationListener = () => {
return onMessage(firebaseMessaging, (payload) => {
if (payload.notification) {
console.log(`푸시 알림: ${payload.notification.body}`);
}
});
};

// 푸시 알림 설정 및 메시지 리스너 설정을 하나로 묶은 함수
export const setupPushNotifications = () => {
handlePushNotificationPermission(); // 푸시 알림 권한 설정
return setupPushNotificationListener(); // 푸시 알림 메시지 리스너 설정
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useAutoSave, useToastStore } from '@/hooks';
import React, { useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { useLocalStorage } from '@/hooks/useLocalStorage'; // 로컬 스토리지 훅 가져오기
import { getIsuserExist } from '@/service/user/getIsuserExist';

const CreateMapLetterCotainer = () => {
const { addToast } = useToastStore();
Expand Down Expand Up @@ -45,6 +46,7 @@ const CreateMapLetterCotainer = () => {
const [description, setDescription] = useState<string>(
storedDescription || ''
);
const [nickname, setNickname] = useState<string>('');

const navigate = useNavigate();

Expand All @@ -63,13 +65,23 @@ const CreateMapLetterCotainer = () => {

useAutoSave(saveLetterData, 50000);

const checkUserExist = async (nickname: string) => {
if (nickname.trim() === '') return true;

const response = await getIsuserExist(nickname);
if (!response.isSuccess || !response.result.isExists) {
return false;
}
return true;
};

return (
<>
<TopBar
handleBackClick={() => {
navigate(-1);
}}
handleSuccesClick={() => {
handleSuccesClick={async () => {
if (
!title.trim() ||
!letterContent.trim() ||
Expand All @@ -81,6 +93,16 @@ const CreateMapLetterCotainer = () => {
);
return;
}

if (nickname.trim() !== '') {
const isUserExist = await checkUserExist(nickname);
if (!isUserExist) {
addToast('사용자를 찾을 수 없습니다.', 'warning');
return;
}
localStorage.setItem('mapnickname', nickname);
}

saveLetterData();
navigate('/letter/map/select');
}}
Expand All @@ -90,6 +112,17 @@ const CreateMapLetterCotainer = () => {
<>
<Margin top={20} />
<div className="relative flex flex-col justify-center w-9/12 m-auto py-9">
<input
value={nickname}
onChange={(e) => setNickname(e.target.value)}
placeholder="보낼 유저의 닉네임을 입력하세요"
className={`z-30 w-full bg-transparent border-none focus:border-none focus:outline-none text-wrap ${font ? font : 'font-sans'}`}
/>
<img
src={'/to_line.f4c129e6.svg'}
className="z-30 h-[7px] w-[200px] object-cover"
/>
<Margin bottom={30} />
<input
onChange={handleChange}
value={title}
Expand All @@ -112,9 +145,9 @@ const CreateMapLetterCotainer = () => {
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'}`}
className={`z-30 w-full bg-transparent border-none focus:border-none focus:outline-none text-wrap ${font ? font : 'font-sans'}`}
/>
<img src={'/to_line.f4c129e6.svg'} />
<img src={'/to_line.f4c129e6.svg'} className="z-30" />
</div>

<SelectSlider
Expand Down
30 changes: 30 additions & 0 deletions src/hooks/useCreateTargetMapLetter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useMutation } from '@tanstack/react-query';
import { useToastStore } from './useToastStore';
import { useNavigate } from 'react-router-dom';
import { createMapTargerLetter } from '@/service/letter/create/createMapTargerLetter';

export const useCreateTargetMapLetter = () => {
const { addToast } = useToastStore();
const navigate = useNavigate();
const mutation = useMutation({
mutationFn: createMapTargerLetter,
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');
localStorage.removeItem('mapnickname');
},
onError: (error) => {
console.error(error);

addToast('지도 편지를 실패했습니다.', 'error');
}
});
return mutation;
};
52 changes: 0 additions & 52 deletions src/hooks/usePushNotification .ts

This file was deleted.

50 changes: 50 additions & 0 deletions src/pages/Home/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import { useEffect, useState } from 'react';
import { useGetRecommendLetter } from '@/hooks/useGetRecommendLetter';
import { useGetRecentRelyLetter } from '@/hooks/useGetRecentRelyLetter';
import { TopButtonContainer } from '@/components/HomePage/TopButtonContainer/TopButtonContainer';
import { getToken, firebaseMessaging, onMessage } from '@/util/firebase';
import { postToken } from '@/service/nofication/postToken';
import { useToastStore } from '@/hooks';

export type ReplyLetter = {
type: 'MAP' | 'KEYWORD';
Expand All @@ -24,6 +27,53 @@ export type RecommendLetter = {

export const HomePage = () => {
const { user } = useUserStore();
const { addToast } = useToastStore();

const isToken = localStorage.getItem('isToken');

useEffect(() => {
if (isToken === 'true') {
return;
}
const handlePushNotifications = async () => {
try {
const permission = await Notification.requestPermission();

if (permission === 'granted') {
const token = await getToken(firebaseMessaging, {
vapidKey: import.meta.env.VITE_FCM_VAPID_KEY
});

try {
const data = await postToken({ token });
addToast(`${data.message}`, 'success');
localStorage.setItem('isToken', 'true');
} catch (error) {
console.error(error);
}
} else {
console.log('Notification permission denied');
}
} catch (error) {
console.error('FCM Error : ', error);
}
};

// 푸시 알림 설정 및 수신 처리
handlePushNotifications();

// 푸시 알림 메시지 수신 시 처리
const unsubscribe = onMessage(firebaseMessaging, (payload) => {
if (payload.notification) {
alert(payload.notification.body);
}
});

// 컴포넌트가 언마운트 될 때 리스너 해제
return () => {
unsubscribe();
};
}, []); // 빈 배열을 의존성으로 사용해 처음 마운트될 때만 실행되게 함

const [open, setOpen] = useState(false);
const [toggle, setToggle] = useState(true);
Expand Down
41 changes: 29 additions & 12 deletions src/pages/Map/Select/MapSelectItemPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { LabelProps } from '@/types/label';
import { LabelList } from '@/components/SelectItemPage/LabelList/LabelList';
import { useCreateMapLetter } from '@/hooks/useCreateMapLetter';
import { useLocalStorage } from '@/hooks';
import { useCreateTargetMapLetter } from '@/hooks/useCreateTargetMapLetter';

const testLable: LabelProps[] = [
{
Expand Down Expand Up @@ -38,23 +39,39 @@ export const MapSelectItemPage = () => {
const [selectedLabel, setSelectedLabel] = useState<number | null>(null);
const [isActive, setIsActive] = useState(false);

const { mutate } = useCreateMapLetter();
const { mutate: createPulicLetter } = useCreateMapLetter();
const { mutate: createTargeLetter } = useCreateTargetMapLetter();
const nickname = localStorage.getItem('mapnickname');

const handleClick = () => {
console.log('보내기 버튼 클릭됨!');
if (selectedLabel === null) {
return;
}
mutate({
title,
content,
description,
latitude: latitudeValue,
longitude: longitudeValue,
font,
paper: letter,
label: testLable[selectedLabel].imgSrc
});

if (nickname) {
createTargeLetter({
title,
content,
description,
latitude: latitudeValue,
longitude: longitudeValue,
font,
paper: letter,
label: testLable[selectedLabel].imgSrc,
target: nickname
});
} else {
createPulicLetter({
title,
content,
description,
latitude: latitudeValue,
longitude: longitudeValue,
font,
paper: letter,
label: testLable[selectedLabel].imgSrc
});
}
};

useEffect(() => {
Expand Down
Loading

0 comments on commit 8eb0caa

Please sign in to comment.