Skip to content

Commit 107cd1c

Browse files
authored
Merge pull request #68 from codeit-maso/feature/chan
✨ feat: 이모지 리액션, 카카오톡 공유, 클립보드 기능 구현
2 parents dc915cd + f2a337b commit 107cd1c

File tree

13 files changed

+626
-52
lines changed

13 files changed

+626
-52
lines changed

src/api/emojiReactions.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { api } from './api';
2+
3+
export const fetchReactions = async (recipientId, limit = 8) => {
4+
try {
5+
const teamId = import.meta.env.VITE_TEAM_ID;
6+
const res = await api.get(
7+
`/${teamId}/recipients/${recipientId}/reactions/?limit=${limit}`,
8+
);
9+
return res.data.results;
10+
} catch (error) {
11+
console.error('리액션 목록 가져오기 실패:', error);
12+
return [];
13+
}
14+
};
15+
16+
export const addReaction = async (recipientId, emoji) => {
17+
try {
18+
const teamId = import.meta.env.VITE_TEAM_ID;
19+
const res = await api.post(
20+
`/${teamId}/recipients/${recipientId}/reactions/`,
21+
{
22+
emoji: emoji,
23+
type: 'increase',
24+
},
25+
);
26+
return res.data;
27+
} catch (error) {
28+
console.error('이모지 리액션 추가 실패:', error);
29+
throw error;
30+
}
31+
};
32+
33+
export const decreaseReaction = async (recipientId, emoji) => {
34+
try {
35+
const teamId = import.meta.env.VITE_TEAM_ID;
36+
const res = await api.post(
37+
`/${teamId}/recipients/${recipientId}/reactions/`,
38+
{
39+
emoji: emoji,
40+
type: 'decrease',
41+
},
42+
);
43+
return res.data;
44+
} catch (error) {
45+
console.error('이모지 리액션 감소 실패:', error);
46+
throw error;
47+
}
48+
};

src/api/fetchReactions.js

Lines changed: 0 additions & 13 deletions
This file was deleted.

src/api/fetchRecipient.js

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/assets/images/close.svg

Lines changed: 3 additions & 0 deletions
Loading

src/assets/images/success.svg

Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { useEffect } from 'react';
2+
import styles from './Toast.module.scss';
3+
import successIcon from '../../../assets/images/success.svg';
4+
import closeIcon from '../../../assets/images/close.svg';
5+
6+
export default function Toast({
7+
isVisible,
8+
message,
9+
onClose,
10+
duration = 5000,
11+
type = 'success',
12+
}) {
13+
useEffect(() => {
14+
if (isVisible) {
15+
const timer = setTimeout(() => {
16+
onClose();
17+
}, duration);
18+
19+
return () => clearTimeout(timer);
20+
}
21+
}, [isVisible, onClose, duration]);
22+
23+
if (!isVisible) return null;
24+
25+
return (
26+
<div className={`${styles.toast} ${styles[`toast--${type}`]}`}>
27+
<div className={styles.iconContainer}>
28+
{type === 'success' && (
29+
<img src={successIcon} alt="성공" className={styles.icon} />
30+
)}
31+
</div>
32+
<div className={styles.content}>{message}</div>
33+
<button
34+
className={styles.closeButton}
35+
onClick={onClose}
36+
aria-label="닫기"
37+
>
38+
<img src={closeIcon} alt="닫기" />
39+
</button>
40+
</div>
41+
);
42+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
@use '../../../assets/styles/variables.scss' as *;
2+
3+
.toast {
4+
position: fixed;
5+
bottom: 20px;
6+
left: 50%;
7+
transform: translateX(-50%);
8+
background-color: #383838;
9+
color: white;
10+
padding: 12px 16px;
11+
border-radius: 8px;
12+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
13+
z-index: 9999;
14+
max-width: 90%;
15+
min-width: 320px;
16+
text-align: left;
17+
animation: fadeIn 0.3s ease-out;
18+
display: flex;
19+
align-items: center;
20+
justify-content: flex-start;
21+
gap: 8px;
22+
}
23+
24+
.iconContainer {
25+
display: flex;
26+
align-items: center;
27+
justify-content: center;
28+
}
29+
30+
.icon {
31+
width: 24px;
32+
height: 24px;
33+
}
34+
35+
.content {
36+
flex: 1;
37+
word-break: break-word;
38+
font-size: 16px;
39+
font-weight: 500;
40+
line-height: 1.5;
41+
margin: 0 4px;
42+
}
43+
44+
.closeButton {
45+
background: none;
46+
border: none;
47+
padding: 0;
48+
cursor: pointer;
49+
display: flex;
50+
align-items: center;
51+
justify-content: center;
52+
}
53+
54+
@keyframes fadeIn {
55+
from {
56+
opacity: 0;
57+
transform: translate(-50%, 10px);
58+
}
59+
to {
60+
opacity: 1;
61+
transform: translate(-50%, 0);
62+
}
63+
}

0 commit comments

Comments
 (0)