Skip to content

Commit 4168362

Browse files
authored
Merge pull request #232 from DevKor-github/dev
[๋ฐฐํฌ] ์ธ์•ฑ ์ฑ„ํŒ… ์•Œ๋ฆผ ๊ตฌํ˜„
2 parents 0a46c53 + 13e74e9 commit 4168362

File tree

13 files changed

+186
-15
lines changed

13 files changed

+186
-15
lines changed

โ€Žsrc/App.tsxโ€Ž

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import SocketProvider from '@/common/utils/SocketProvider';
88
import NotificationProvider from '@/common/utils/NotificationProvider';
99
import { OverlayProvider } from '@/common/utils/OverlayProvider';
1010
import useVersionName from '@/common/hooks/useVersionName';
11+
import InAppNotification from './common/components/InAppNotification';
1112

1213
const queryClient = new QueryClient({
1314
defaultOptions: {
@@ -29,12 +30,12 @@ function App() {
2930

3031
return (
3132
<QueryClientProvider client={queryClient}>
32-
<SocketProvider>
33-
<OverlayProvider>
33+
<OverlayProvider>
34+
<SocketProvider>
3435
<AmplitudeProvider />
3536
<NotificationProvider>{router}</NotificationProvider>
36-
</OverlayProvider>
37-
</SocketProvider>
37+
</SocketProvider>
38+
</OverlayProvider>
3839
</QueryClientProvider>
3940
);
4041
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { UserProfileImage } from '../UserProfileImage';
2+
import { motion } from 'motion/react';
3+
import * as s from './style.css';
4+
import getImageUrl from '@/common/utils/getImageUrl';
5+
import { useNavigate } from 'react-router';
6+
7+
interface Props {
8+
isOpen: boolean;
9+
nickname: string | null;
10+
profileImage: string | null;
11+
content: string | null;
12+
chatRoomId: number;
13+
}
14+
15+
const InAppNotification = ({ isOpen, nickname, profileImage, content, chatRoomId }: Props) => {
16+
const navigate = useNavigate();
17+
18+
return (
19+
<motion.div
20+
className={s.Container}
21+
onClick={() => {
22+
navigate(`/chat/${chatRoomId}`);
23+
}}
24+
initial={{ opacity: 0, translateX: '-50%' }}
25+
animate={isOpen ? { opacity: 0.84, translateX: '-50%', translateY: '50%' } : { opacity: 0, translateX: '-50%' }}
26+
>
27+
<UserProfileImage nickname={nickname ?? ''} src={getImageUrl(profileImage ?? '')} />
28+
<div className={s.Content}>
29+
<div className={s.Title}>{nickname}</div>
30+
<div className={s.Disc}>{content}</div>
31+
</div>
32+
</motion.div>
33+
);
34+
};
35+
36+
export default InAppNotification;
37+
38+
// import { motion } from 'motion/react';
39+
40+
// import * as s from './style.css';
41+
42+
// import { TOAST_ANIMATION_DURATION } from '@/common/hooks/useToast';
43+
44+
// interface ToastProps {
45+
// isOpen: boolean;
46+
// message: string;
47+
// }
48+
// const Toast = ({ isOpen, message }: ToastProps) => {
49+
// return (
50+
// <div className={s.Layout}>
51+
// <motion.div
52+
// className={s.Container}
53+
// initial={{ opacity: 0, translateY: 66 }}
54+
// animate={isOpen ? { opacity: 1, translateY: 0 } : { opacity: 0, translateY: 66 }}
55+
// transition={{ duration: TOAST_ANIMATION_DURATION / 1000, type: 'spring' }}
56+
// >
57+
// {message}
58+
// </motion.div>
59+
// </div>
60+
// );
61+
// };
62+
63+
// export default Toast;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { css } from '@styled-system/css';
2+
3+
export const Container = css({
4+
display: 'flex',
5+
flex: 1,
6+
position: 'absolute',
7+
padding: '0.875rem',
8+
w: '90%',
9+
10+
left: '50%',
11+
alignItems: 'flex-start',
12+
gap: '0.625rem',
13+
14+
borderRadius: '0.625rem',
15+
background: '100',
16+
boxShadow: '0 0 4px 0 rgba(0, 0, 0, 0.10)',
17+
opacity: 0.84,
18+
});
19+
20+
export const Content = css({
21+
display: 'flex',
22+
flexDirection: 'column',
23+
gap: '0.25rem',
24+
flex: '1 0 0',
25+
});
26+
27+
export const Title = css({
28+
color: 'black',
29+
fontFamily: 'Pretendard',
30+
fontSize: '0.75rem',
31+
fontStyle: 'normal',
32+
fontWeight: 500,
33+
lineHeight: 'normal',
34+
});
35+
36+
export const Disc = css({
37+
color: 'black',
38+
fontFamily: 'Pretendard',
39+
fontSize: '0.75rem',
40+
fontStyle: 'normal',
41+
fontWeight: 400,
42+
lineHeight: 'normal',
43+
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { useCallback, useRef } from 'react';
2+
import { useOverlay } from '@/common/hooks/useOverlay';
3+
import InAppNotification from '../components/InAppNotification';
4+
5+
export const TOAST_ANIMATION_DURATION = 300;
6+
export const TOAST_DISPLAY_DURATION = 1000 + TOAST_ANIMATION_DURATION;
7+
8+
interface OpenInAppNotificationOption {
9+
nickname: string | null;
10+
content: string | null;
11+
profileImage: string | null;
12+
chatRoomId: number;
13+
}
14+
export function useInAppNotificaiton() {
15+
const overlay = useOverlay({ exitOnUnmount: false });
16+
const closeTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
17+
const exitTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
18+
19+
const initTimeout = useCallback(() => {
20+
// ๊ฐ์ข… ํƒ€์ด๋จธ๋“ค์„ ์ดˆ๊ธฐํ™” ํ•ฉ๋‹ˆ๋‹ค
21+
if (closeTimeoutRef.current !== null) {
22+
clearTimeout(closeTimeoutRef.current);
23+
}
24+
25+
if (exitTimeoutRef.current !== null) {
26+
clearTimeout(exitTimeoutRef.current);
27+
}
28+
}, []);
29+
30+
const handleClose = useCallback(() => {
31+
overlay.close();
32+
exitTimeoutRef.current = setTimeout(overlay.exit, TOAST_ANIMATION_DURATION);
33+
}, [overlay]);
34+
35+
const openNotification = ({ content, profileImage, nickname, chatRoomId }: OpenInAppNotificationOption) => {
36+
initTimeout();
37+
closeTimeoutRef.current = setTimeout(handleClose, TOAST_DISPLAY_DURATION);
38+
overlay.open(({ isOpen }) => (
39+
<InAppNotification
40+
isOpen={isOpen}
41+
content={content}
42+
nickname={nickname}
43+
profileImage={profileImage}
44+
chatRoomId={chatRoomId}
45+
/>
46+
));
47+
};
48+
49+
return { openNotification };
50+
}

โ€Žsrc/common/utils/SocketProvider/index.tsxโ€Ž

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { connectSocket, subChatListSocket } from '@/common/utils/wsClient';
33
import { useQueryClient } from '@tanstack/react-query';
44
import { QUERY_KEYS } from '@/libs/queryKeys';
55
import { useUnreadChatStore } from '@/common/store/UnreadChatStore';
6+
import { useInAppNotificaiton } from '@/common/hooks/useInAppNotification';
7+
import { useLocation } from 'react-router';
68

79
interface Props {
810
children: React.ReactNode;
@@ -11,6 +13,9 @@ interface Props {
1113
const SocketProvider = ({ children }: Props) => {
1214
const queryClient = useQueryClient();
1315
const setUnreadChatCount = useUnreadChatStore(state => state.setUnreadChatCount);
16+
const { openNotification } = useInAppNotificaiton();
17+
const location = useLocation();
18+
console.log(location.pathname);
1419

1520
useEffect(() => {
1621
let unsubscribe: (() => void) | undefined;
@@ -21,6 +26,18 @@ const SocketProvider = ({ children }: Props) => {
2126
unsubscribe = subChatListSocket(data => {
2227
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.CHAT_LIST] });
2328
if (data.type === 'UNREAD_CHAT_COUNT') setUnreadChatCount(data.message.unreadChatCount);
29+
if (data.type === 'CHAT') {
30+
if (location.pathname.startsWith('/chat')) {
31+
return;
32+
}
33+
34+
const content = data.message.mostRecentChatContent;
35+
const nickname = data.message.mostRecentChatNickname;
36+
const profileImage = data.message.opponentProfileImageUrl;
37+
const chatRoomId = data.message.chatRoomId;
38+
39+
openNotification({ content, nickname, profileImage, chatRoomId });
40+
}
2441
});
2542
})
2643
.catch(err => {
@@ -30,7 +47,7 @@ const SocketProvider = ({ children }: Props) => {
3047
return () => {
3148
unsubscribe?.();
3249
};
33-
}, [queryClient, setUnreadChatCount]);
50+
}, [queryClient, setUnreadChatCount, location.pathname]);
3451

3552
return <>{children}</>;
3653
};

โ€Žsrc/features/chatList/components/ChatList/index.tsxโ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ const ChatList = ({ data }: Props) => {
7373

7474
return (
7575
<>
76-
<Link className={s.List} to={`/chatroom/${data.chatRoomId}`} {...bind()}>
76+
<Link className={s.List} to={`/chat/${data.chatRoomId}`} {...bind()}>
7777
<div className={s.Img}>
7878
<img className={s.Thumbnail} src={thumbnail} />
7979
<UserProfileImage nickname={data.opponentNickname} src={data.opponentProfileImageUrl} />

โ€Žsrc/features/detail/components/BottomActions/index.tsxโ€Ž

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,13 @@ const BottomActions = ({ itemId, itemInfo }: Props) => {
3838
}
3939

4040
if (itemStatusData.chatRoomId) {
41-
navigate(`/chatroom/${itemStatusData.chatRoomId}`);
41+
navigate(`/chat/${itemStatusData.chatRoomId}`);
4242
return;
4343
}
4444

4545
createChatroom(itemId, {
4646
onSuccess: data => {
47-
navigate(`/chatroom/${data.data.chatRoom.chatRoomId}`);
47+
navigate(`/chat/${data.data.chatRoom.chatRoomId}`);
4848
},
4949
});
5050
};

โ€Žsrc/features/detail/components/ImageContainer/index.tsxโ€Ž

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ const ImageContainer = ({ images }: Props) => {
1313
const totalIndex = images.length;
1414
const [currentIndex, setCurrentIndex] = useState(0);
1515

16-
// TODO: ๋ชจ๋ฐ”์ผ์—์„œ ์คŒ ๋˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ
1716
return (
1817
<div className={s.Container}>
1918
<Swiper

โ€Žsrc/features/pick/components/DetailBottom/index.tsxโ€Ž

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ const DetailBottom = ({ id, itemId, isCreator, pickState, chatRoomId }: Props) =
5151
const confirmPick = () => {
5252
confirm(id, {
5353
onSuccess: () => {
54-
// TODO: ์ฑ„ํŒ…๋ฐฉ ์ด๋™
55-
navigate(`/chatroom/${chatRoomId}`, { replace: true });
54+
navigate(`/chat/${chatRoomId}`, { replace: true });
5655
openToast({ message: 'PICK์ด ํ™•์ •๋˜์—ˆ์–ด์š”' });
5756
},
5857
});

โ€Žsrc/features/pick/components/TimeDrawer/index.tsxโ€Ž

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ const TimeDrawer = ({ dateTime, setDateTime, transactionText, next, prev }: Prop
2323
<div className={s.TimePickerWrapper}>
2424
<label>{transactionText} ์‹œ๊ฐ„</label>
2525
<input type="time" value={value} onChange={e => setValue(e.target.value)} />
26-
{/* TODO: TimePicker ์ปดํฌ๋„ŒํŠธ ์‚ฌ์šฉ */}
2726
</div>
2827
<div className={s.ButtonWrapper}>
2928
<button className={s.DateDrawerButton({ type: 'prev' })} onClick={prev}>

0 commit comments

Comments
ย (0)