Skip to content

Commit

Permalink
[friends-native] 카카오톡 친구 추가 기능 반영 (#159)
Browse files Browse the repository at this point in the history
Co-authored-by: woohm402 <[email protected]>
  • Loading branch information
ars-ki-00 and woohm402 authored Jul 6, 2024
1 parent 8024bc1 commit 8309693
Show file tree
Hide file tree
Showing 21 changed files with 373 additions and 106 deletions.
4 changes: 4 additions & 0 deletions apps/friends-react-native/src/app/assets/icons/kakaotalk.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions apps/friends-react-native/src/app/assets/icons/user-hashtag.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Icon from '../../assets/icons/kakaotalk.svg';
import { createIconComponent } from './_createIconComponent';

export const KakaotalkIcon = createIconComponent(Icon);
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Icon from '../../assets/icons/user-hashtag.svg';
import { createIconComponent } from './_createIconComponent';

export const UserHashtagIcon = createIconComponent(Icon);
3 changes: 3 additions & 0 deletions apps/friends-react-native/src/app/contexts/ServiceContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ import { ColorService } from '../../usecases/colorService';
import { CourseBookService } from '../../usecases/courseBookService';
import { FriendService } from '../../usecases/friendService';
import { TimetableViewService } from '../../usecases/timetableViewService';
import { NativeEventService } from '../../usecases/nativeEventService';

type ServiceContext = {
timetableViewService: TimetableViewService;
colorService: ColorService;
friendService: FriendService;
courseBookService: CourseBookService;
assetService: AssetService;
nativeEventService: NativeEventService;
};

export const serviceContext = createContext<ServiceContext | null>(null);
export const useServiceContext = () => {
const context = useContext(serviceContext);
Expand Down
22 changes: 21 additions & 1 deletion apps/friends-react-native/src/app/queries/useFriends.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useQuery } from '@tanstack/react-query';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';

import { useServiceContext } from '../contexts/ServiceContext';
import { FriendId } from '../../entities/friend';

export const useFriends = (req: { state: 'REQUESTED' | 'REQUESTING' | 'ACTIVE' }) => {
const { friendService } = useServiceContext();
Expand All @@ -9,3 +10,22 @@ export const useFriends = (req: { state: 'REQUESTED' | 'REQUESTING' | 'ACTIVE' }
queryFn: ({ queryKey: [, params] }) => friendService.listFriends(params),
});
};

export const useAcceptFriend = () => {
const queryClient = useQueryClient();
const { friendService } = useServiceContext();
return useMutation({
mutationFn: (req: { type: 'NICKNAME'; friendId: FriendId } | { type: 'KAKAO'; requestToken: string }) =>
friendService.acceptFriend(req),
onSuccess: () => queryClient.invalidateQueries(),
});
};

export const useDeclineFriend = () => {
const queryClient = useQueryClient();
const { friendService } = useServiceContext();
return useMutation({
mutationFn: (friendId: FriendId) => friendService.declineFriend({ friendId }),
onSuccess: () => queryClient.invalidateQueries(),
});
};
12 changes: 12 additions & 0 deletions apps/friends-react-native/src/app/queries/useRequestFriendToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useQuery } from '@tanstack/react-query';

import { useServiceContext } from '../contexts/ServiceContext';

export const useRequestFriendToken = () => {
const { friendService } = useServiceContext();

return useQuery({
queryKey: ['requestFriendToken'] as const,
queryFn: () => friendService.generateToken(),
});
};
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { Alert, FlatList, StyleSheet, View } from 'react-native';

import { FriendId } from '../../../../../entities/friend';
import { Button } from '../../../../components/Button';
import { EmptyView } from '../../../../components/EmptyView';
import { Typography } from '../../../../components/Typography';
import { useServiceContext } from '../../../../contexts/ServiceContext';
import { useFriends } from '../../../../queries/useFriends';
import { useAcceptFriend, useDeclineFriend, useFriends } from '../../../../queries/useFriends';

export const ManageFriendsDrawerContentRequestedList = () => {
const { friendService } = useServiceContext();
Expand Down Expand Up @@ -36,7 +34,12 @@ export const ManageFriendsDrawerContentRequestedList = () => {
</Button>
<Button
color="primary"
onPress={() => acceptFriend(item.friendId, { onSuccess: () => Alert.alert('친구 요청을 수락했습니다.') })}
onPress={() =>
acceptFriend(
{ type: 'NICKNAME', friendId: item.friendId },
{ onSuccess: () => Alert.alert('친구 요청을 수락했습니다.') },
)
}
>
수락
</Button>
Expand All @@ -55,24 +58,6 @@ const Empty = () => {
);
};

const useAcceptFriend = () => {
const queryClient = useQueryClient();
const { friendService } = useServiceContext();
return useMutation({
mutationFn: (friendId: FriendId) => friendService.acceptFriend({ friendId }),
onSuccess: () => queryClient.invalidateQueries(),
});
};

const useDeclineFriend = () => {
const queryClient = useQueryClient();
const { friendService } = useServiceContext();
return useMutation({
mutationFn: (friendId: FriendId) => friendService.declineFriend({ friendId }),
onSuccess: () => queryClient.invalidateQueries(),
});
};

const styles = StyleSheet.create({
item: {
height: 40,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export const ManageFriendsDrawerContent = ({ onClose }: Props) => {
<View style={styles.tabContent}>
<TouchableOpacity
style={{ ...styles.addFriend, borderBottomColor: dividerColor }}
onPress={() => dispatch({ type: 'setAddFriendModalOpen', isOpen: true })}
onPress={() => dispatch({ type: 'setRequestFriendModalOpen', isOpen: true })}
>
<Typography style={{ ...styles.addFriendText, color: addFriendButtonColor }}>친구 추가하기</Typography>
<UserPlusIcon style={{ color: addFriendButtonColor }} width={16} height={16} />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { StyleSheet, View } from 'react-native';

import { Typography } from '../../../../components/Typography';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { UserHashtagIcon } from '../../../../components/Icons/UserHashtagIcon';
import { useThemeContext } from '../../../../contexts/ThemeContext';
import { KakaotalkIcon } from '../../../../components/Icons/Kakaotalk';
import { RequestFriendModalStep, useMainScreenContext } from '../..';
import { useRequestFriendToken } from '../../../../queries/useRequestFriendToken';
import { useServiceContext } from '../../../../contexts/ServiceContext';

export const RequestFriendsMethodList = () => {
const { dispatch } = useMainScreenContext();
const { nativeEventService } = useServiceContext();
const { data } = useRequestFriendToken();
const iconColor = useThemeContext((theme) => theme.color.text.default);

const setRequestFriendModalStep = (step: RequestFriendModalStep) =>
dispatch({
type: 'setRequestFriendModalStep',
requestFriendModalStep: step,
});

const requestFriendWithKakao = () => {
const parameters = {
requestToken: data!.requestToken,
};

nativeEventService.sendEventToNative({
type: 'add-friend-kakao',
parameters,
});

dispatch({
type: 'setRequestFriendModalOpen',
isOpen: false,
});
};

return (
<>
<View style={styles.sheetContent}>
<TouchableOpacity style={styles.sheetItem} onPress={requestFriendWithKakao}>
<KakaotalkIcon
width={30}
height={30}
style={{
color: iconColor,
}}
/>
<Typography>카카오톡으로 친구 초대</Typography>
</TouchableOpacity>
</View>
<View style={styles.sheetContent}>
<TouchableOpacity style={styles.sheetItem} onPress={() => setRequestFriendModalStep('REQUEST_WITH_NICKNAME')}>
<UserHashtagIcon
width={30}
height={30}
style={{
color: iconColor,
}}
/>
<Typography>닉네임으로 친구 초대</Typography>
</TouchableOpacity>
</View>
</>
);
};

const styles = StyleSheet.create({
sheetContent: { paddingBottom: 20 },
sheetItem: {
height: 50,
paddingVertical: 10,
display: 'flex',
flexDirection: 'row',
gap: 25,
alignItems: 'center',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { Alert } from 'react-native';
import { StyleSheet, View } from 'react-native';

import { get } from '../../../../../utils/get';
import { BottomSheet } from '../../../../components/BottomSheet';
import { WarningIcon } from '../../../../components/Icons/WarningIcon';
import { Input } from '../../../../components/Input';
import { Typography } from '../../../../components/Typography';
import { COLORS } from '../../../../styles/colors';
import { useMainScreenContext, useRequestFriend } from '../..';
import { useServiceContext } from '../../../../contexts/ServiceContext';
import { useThemeContext } from '../../../../contexts/ThemeContext';

export const RequestFriendsWithNickname = () => {
const { requestFriendModalNickname, dispatch } = useMainScreenContext();
const { friendService } = useServiceContext();
const guideEnabledColor = useThemeContext((data) => data.color.text.guide);
const { mutate: request } = useRequestFriend();

const isValid = friendService.isValidNicknameTag(requestFriendModalNickname);
const guideMessageState = requestFriendModalNickname === '' ? 'disabled' : isValid ? 'hidden' : 'enabled';

const closeAddFriendModal = () => dispatch({ type: 'setRequestFriendModalOpen', isOpen: false });

return (
<View style={styles.modalContent}>
<BottomSheet.Header
left={{ text: '취소', onPress: closeAddFriendModal }}
right={{
text: '요청 보내기',
onPress: () =>
request(requestFriendModalNickname, {
onSuccess: () => {
Alert.alert('친구에게 요청을 보냈습니다.');
closeAddFriendModal();
},
onError: (err) => {
const displayMessage = get(err, ['displayMessage']);
Alert.alert(displayMessage ? `${displayMessage}` : '오류가 발생했습니다.');
},
}),
disabled: !isValid,
}}
/>
<Typography variant="description" style={styles.inputDescription}>
추가하고 싶은 친구의 닉네임
</Typography>
<Input
style={styles.input}
autoFocus
value={requestFriendModalNickname}
onChange={(e) => dispatch({ type: 'setRequestFriendModalNickname', nickname: e })}
placeholder="예) 홍길동#1234"
/>
<View style={styles.guide}>
{guideMessageState !== 'hidden' &&
(() => {
const color = { enabled: guideEnabledColor, disabled: COLORS.gray40 }[guideMessageState];
return (
<>
<WarningIcon width={18} height={18} style={{ color }} />
<Typography variant="description" style={{ ...styles.guideText, color }}>
닉네임 전체를 입력하세요
</Typography>
</>
);
})()}
</View>
</View>
);
};

const styles = StyleSheet.create({
questionIconButton: { flexDirection: 'row', alignItems: 'center', gap: 6 },
questionIcon: { color: COLORS.gray30 },
modalContent: { paddingBottom: 30 },
inputDescription: { marginTop: 30, fontSize: 14 },
input: { marginTop: 15 },
guide: {
marginTop: 7,
display: 'flex',
flexDirection: 'row',
gap: 1,
alignItems: 'center',
height: 12,
},
guideText: { fontSize: 12 },
hamburgerWrapper: { position: 'relative' },
hamburgerNotificationDot: { position: 'absolute', top: 5, right: -1 },
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useMainScreenContext } from '..';
import { BottomSheet } from '../../../components/BottomSheet';
import { RequestFriendsMethodList } from './RequestFriendsMethodList';
import { RequestFriendsWithNickname } from './RequestFriendsWithNickname';

export const RequestFriendsBottomSheetContent = () => {
const { isRequestFriendModalOpen, requestFriendModalStep, dispatch } = useMainScreenContext();

const closeAddFriendModal = () => dispatch({ type: 'setRequestFriendModalOpen', isOpen: false });

return (
<BottomSheet isOpen={isRequestFriendModalOpen} onClose={closeAddFriendModal}>
{requestFriendModalStep === 'METHOD_LIST' ? <RequestFriendsMethodList /> : <RequestFriendsWithNickname />}
</BottomSheet>
);
};
Loading

0 comments on commit 8309693

Please sign in to comment.