diff --git a/src/components/BottomSheet/ActionListBottomSheet/ActionButton.tsx b/src/components/BottomSheet/ActionListBottomSheet/ActionButton.tsx
new file mode 100644
index 00000000..1cd12224
--- /dev/null
+++ b/src/components/BottomSheet/ActionListBottomSheet/ActionButton.tsx
@@ -0,0 +1,20 @@
+import Text from '@/components/Text';
+import TouchableRipple from '@/components/TouchableRipple';
+
+import { styles } from './index.style';
+
+export type ActionButton = {
+ label: string;
+ onPress: () => void;
+};
+
+export const ActionButton = ({
+ label,
+ onPress,
+}: ActionButton) => (
+
+
+ {label}
+
+
+);
\ No newline at end of file
diff --git a/src/components/BottomSheet/ActionListBottomSheet/index.style.ts b/src/components/BottomSheet/ActionListBottomSheet/index.style.ts
new file mode 100644
index 00000000..c708babc
--- /dev/null
+++ b/src/components/BottomSheet/ActionListBottomSheet/index.style.ts
@@ -0,0 +1,9 @@
+import { StyleSheet } from 'react-native';
+
+export const styles = StyleSheet.create({
+ actionButtonContainer: {
+ justifyContent: 'center',
+ alignItems: 'center',
+ paddingVertical: 12,
+ },
+});
\ No newline at end of file
diff --git a/src/components/BottomSheet/ActionListBottomSheet/index.tsx b/src/components/BottomSheet/ActionListBottomSheet/index.tsx
new file mode 100644
index 00000000..91f3422d
--- /dev/null
+++ b/src/components/BottomSheet/ActionListBottomSheet/index.tsx
@@ -0,0 +1,29 @@
+import { Shapes } from '@components';
+import { View } from 'react-native';
+
+import type { BottomSheetProps } from '..';
+import { BottomSheet } from '../index';
+import { ActionButton } from './ActionButton';
+
+interface ActionListBottomSheetProps extends BottomSheetProps {
+ actionItems: ActionButton[];
+}
+
+const ActionListBottomSheet = ({
+ actionItems,
+ ...props
+}: ActionListBottomSheetProps) => (
+
+ {actionItems.map((item, idx) => (
+
+ {idx > 0 && }
+
+
+ ))}
+
+);
+
+export default ActionListBottomSheet;
diff --git a/src/components/BottomSheet/BottomSheetCommon.tsx b/src/components/BottomSheet/BottomSheetCommon.tsx
index ea27640b..560c046b 100644
--- a/src/components/BottomSheet/BottomSheetCommon.tsx
+++ b/src/components/BottomSheet/BottomSheetCommon.tsx
@@ -2,7 +2,7 @@ import { Backdrop } from '@components';
import type { PropsWithChildren } from 'react';
import { useEffect, useRef, useState } from 'react';
import type { LayoutChangeEvent } from 'react-native';
-import { Animated, Dimensions, View } from 'react-native';
+import { Animated, Dimensions, Keyboard } from 'react-native';
import { useHardwareBack } from '@/hooks/useHardwareBack';
@@ -13,6 +13,7 @@ export interface BottomSheetCommonProps extends PropsWithChildren {
onClose?: () => void;
/** iOS에서는 false로 넘겨도 되며, 기본값 true */
closeOnBackdropPress?: boolean;
+ closeOnHardwareBack?: boolean;
/** SafeAreaInsets.bottom 혹은 0 */
bottomPadding: number;
/** useNativeDriver 플래그 */
@@ -23,6 +24,7 @@ const BottomSheetCommon = ({
enabled,
onClose,
closeOnBackdropPress = true,
+ closeOnHardwareBack = true,
bottomPadding,
useNativeDriver,
children,
@@ -53,7 +55,7 @@ const BottomSheetCommon = ({
};
useHardwareBack(() => {
- if (enabled && onClose) {
+ if (closeOnHardwareBack && enabled && onClose) {
onClose();
return true;
}
@@ -63,6 +65,7 @@ const BottomSheetCommon = ({
useEffect(() => {
if (enabled) {
setHidden(false);
+ Keyboard.dismiss();
}
const anim = Animated.timing(animation, {
toValue: enabled ? 1 : 0,
diff --git a/src/components/BottomSheet/index.android.tsx b/src/components/BottomSheet/index.android.tsx
index a1691bc5..e414d2e3 100644
--- a/src/components/BottomSheet/index.android.tsx
+++ b/src/components/BottomSheet/index.android.tsx
@@ -11,7 +11,7 @@ type BottomSheetProps = Omit<
'bottomPadding' | 'useNativeDriver'
>;
-const BottomSheet = (props: BottomSheetProps) => {
+export const BottomSheet = (props: BottomSheetProps) => {
const insets = useSafeAreaInsets();
return (
@@ -23,4 +23,4 @@ const BottomSheet = (props: BottomSheetProps) => {
);
};
-export default BottomSheet;
+export { default as ActionListBottomSheet } from './ActionListBottomSheet';
diff --git a/src/components/BottomSheet/index.ios.tsx b/src/components/BottomSheet/index.ios.tsx
index be4e2749..1dbd686a 100644
--- a/src/components/BottomSheet/index.ios.tsx
+++ b/src/components/BottomSheet/index.ios.tsx
@@ -13,7 +13,7 @@ type BottomSheetProps = Omit<
'bottomPadding' | 'useNativeDriver'
>;
-const BottomSheet = (props: BottomSheetProps) => {
+export const BottomSheet = (props: BottomSheetProps) => {
const insets = useSafeAreaInsets();
return (
@@ -32,4 +32,4 @@ const BottomSheet = (props: BottomSheetProps) => {
);
};
-export default BottomSheet;
+export { default as ActionListBottomSheet } from './ActionListBottomSheet';
\ No newline at end of file
diff --git a/src/components/BottomSheet/index.tsx b/src/components/BottomSheet/index.tsx
index 2573424b..f558455f 100644
--- a/src/components/BottomSheet/index.tsx
+++ b/src/components/BottomSheet/index.tsx
@@ -6,13 +6,13 @@ import type {
import BottomSheetCommon from './BottomSheetCommon';
/** 공통Props에서 bottomPadding/useNativeDriver만 뺀 타입 */
-type BottomSheetProps = Omit<
+export type BottomSheetProps = Omit<
BottomSheetCommonProps,
'bottomPadding' | 'useNativeDriver'
>;
// TODO: BottomSheet disable 시에, 가상 DOM으로부터 언마운트하도록 구현 변경 (Dialog의 구현처럼)
-const BottomSheet = (props: BottomSheetProps) => (
+export const BottomSheet = (props: BottomSheetProps) => (
(
/>
);
-export default BottomSheet;
+export { default as ActionListBottomSheet } from './ActionListBottomSheet';
diff --git a/src/components/Dialog/Alert/index.style.ts b/src/components/Dialog/Alert/index.style.ts
index 74005162..f9df1489 100644
--- a/src/components/Dialog/Alert/index.style.ts
+++ b/src/components/Dialog/Alert/index.style.ts
@@ -1,6 +1,5 @@
import { StyleSheet } from 'react-native';
-import type { TextProps } from '@/components/Text';
import { theme } from '@/theme';
export const styles = StyleSheet.create({
@@ -19,11 +18,3 @@ export const styles = StyleSheet.create({
alignSelf: 'stretch',
},
});
-
-export const TITLE_TEXT_STYLE: TextProps = {
- font: 't2',
-};
-
-export const MESSAGE_TEXT_STYLE: TextProps = {
- font: 'b3',
-};
\ No newline at end of file
diff --git a/src/components/Dialog/Alert/index.tsx b/src/components/Dialog/Alert/index.tsx
index e4960825..edc0ac55 100644
--- a/src/components/Dialog/Alert/index.tsx
+++ b/src/components/Dialog/Alert/index.tsx
@@ -4,7 +4,8 @@ import { View } from 'react-native';
import { useDialog } from '@/hooks/useDialog';
import { useTranslatedText } from '@/hooks/useTranslatedText';
-import { MESSAGE_TEXT_STYLE, styles, TITLE_TEXT_STYLE } from './index.style';
+import { MESSAGE_TEXT_STYLE, TITLE_TEXT_STYLE } from '../index.style';
+import { styles } from './index.style';
interface AlertDialogProps {
title: string;
diff --git a/src/components/Dialog/Any/index.style.ts b/src/components/Dialog/Any/index.style.ts
index 75c55aaf..84ec8709 100644
--- a/src/components/Dialog/Any/index.style.ts
+++ b/src/components/Dialog/Any/index.style.ts
@@ -1,8 +1,6 @@
import { StyleSheet } from 'react-native';
export const styles = StyleSheet.create({
- container: {
- flex: 1,
- alignItems: 'center',
+ container: {
},
});
diff --git a/src/components/Dialog/Any/index.tsx b/src/components/Dialog/Any/index.tsx
index a223b3a6..0a87de53 100644
--- a/src/components/Dialog/Any/index.tsx
+++ b/src/components/Dialog/Any/index.tsx
@@ -1,8 +1,10 @@
import type { ReactNode } from 'react';
import { View } from 'react-native';
+import Text from '@/components/Text';
import { useDialog } from '@/hooks/useDialog';
+import { TITLE_TEXT_STYLE } from '../index.style';
import { styles } from './index.style';
interface AnyDialogProps {
diff --git a/src/components/Dialog/Confirm/index.style.ts b/src/components/Dialog/Confirm/index.style.ts
index 9c1b35a9..b0f68192 100644
--- a/src/components/Dialog/Confirm/index.style.ts
+++ b/src/components/Dialog/Confirm/index.style.ts
@@ -23,21 +23,3 @@ export const styles = StyleSheet.create({
alignSelf: 'stretch',
},
});
-
-export const TITLE_TEXT_STYLE: TextProps = {
- font: 't2',
-};
-
-export const MESSAGE_TEXT_STYLE: TextProps = {
- font: 'b3',
-};
-
-export const CLOSE_BUTTON_STYLE: ButtonProps = {
- style: {
- alignSelf: 'stretch',
- backgroundColor: 'transparent',
- borderWidth: 1,
- borderColor: theme.color.neutral[500],
- },
- textColor: theme.color.neutral[500],
-};
\ No newline at end of file
diff --git a/src/components/Dialog/Confirm/index.tsx b/src/components/Dialog/Confirm/index.tsx
index 49568373..5d585412 100644
--- a/src/components/Dialog/Confirm/index.tsx
+++ b/src/components/Dialog/Confirm/index.tsx
@@ -4,7 +4,8 @@ import { View } from 'react-native';
import { useDialog } from '@/hooks/useDialog';
import { useTranslatedText } from '@/hooks/useTranslatedText';
-import { CLOSE_BUTTON_STYLE, MESSAGE_TEXT_STYLE, styles, TITLE_TEXT_STYLE } from './index.style';
+import { CLOSE_BUTTON_STYLE, MESSAGE_TEXT_STYLE, TITLE_TEXT_STYLE } from '../index.style';
+import { styles } from './index.style';
interface AlertDialogProps {
title: string;
diff --git a/src/components/Dialog/index.style.ts b/src/components/Dialog/index.style.ts
index db5b138b..637d8b47 100644
--- a/src/components/Dialog/index.style.ts
+++ b/src/components/Dialog/index.style.ts
@@ -2,6 +2,9 @@ import { StyleSheet } from 'react-native';
import { theme } from '@/theme';
+import type { ButtonProps } from '../Button/Basic';
+import type { TextProps } from '../Text';
+
export const styles = StyleSheet.create({
container: {
position: 'absolute',
@@ -19,3 +22,21 @@ export const styles = StyleSheet.create({
padding: theme.spacing[400],
},
});
+
+export const TITLE_TEXT_STYLE: TextProps = {
+ font: 't2',
+};
+
+export const MESSAGE_TEXT_STYLE: TextProps = {
+ font: 'b3',
+};
+
+export const CLOSE_BUTTON_STYLE: ButtonProps = {
+ style: {
+ alignSelf: 'stretch',
+ backgroundColor: 'transparent',
+ borderWidth: 1,
+ borderColor: theme.color.neutral[500],
+ },
+ textColor: theme.color.neutral[500],
+};
\ No newline at end of file
diff --git a/src/components/index.ts b/src/components/index.ts
index 8467105d..e0ec2239 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -2,7 +2,7 @@ export { default as Accordion } from './Accordion';
export { default as Avatar } from './Avatar';
export { default as AvatarGroup } from './AvatarGroup';
export { default as Backdrop } from './Backdrop';
-export { default as BottomSheet } from './BottomSheet';
+export { ActionListBottomSheet, BottomSheet } from './BottomSheet';
export { default as Button } from './Button';
export { default as Checkbox } from './Checkbox';
export { default as DateTimePicker } from './DateTimePicker';
diff --git a/src/locales/ko/groupChat.json b/src/locales/ko/groupChat.json
index 760e014a..6793f189 100644
--- a/src/locales/ko/groupChat.json
+++ b/src/locales/ko/groupChat.json
@@ -1,3 +1,4 @@
{
- "write-message": "메시지 작성"
+ "write-message": "메시지 작성",
+ "group-member-list": "그룹 멤버 목록"
}
\ No newline at end of file
diff --git a/src/screens/group/GroupChatScreen/components/ChatMoreActionsBottomSheet/index.tsx b/src/screens/group/GroupChatScreen/components/ChatMoreActionsBottomSheet/index.tsx
new file mode 100644
index 00000000..fe5af0de
--- /dev/null
+++ b/src/screens/group/GroupChatScreen/components/ChatMoreActionsBottomSheet/index.tsx
@@ -0,0 +1,42 @@
+import { ActionListBottomSheet } from '@/components';
+import type { GetChatRoomInfoResponse } from '@/features/chat/model/getChatRoomInfo';
+import { useTranslatedText } from '@/hooks';
+import { dialogService } from '@/utils/dialog';
+
+import UserInfosDialog from '../UserInfosDialog';
+
+interface ChatMoreActionsBottomSheetProps {
+ userInfos: GetChatRoomInfoResponse['data']['userInfos'];
+ bottomSheetEnabled: boolean;
+ setBottomSheetEnabled: (enabled: boolean) => void;
+}
+
+const ChatMoreActionsBottomSheet = ({
+ userInfos,
+ bottomSheetEnabled,
+ setBottomSheetEnabled,
+}: ChatMoreActionsBottomSheetProps) => {
+ const tViewGroupMembers = useTranslatedText({ tKey: 'groupChat.group-member-list' });
+
+ return (
+ {
+ dialogService.add({
+ content: (
+
+ ),
+ });
+ },
+ },
+ ]}
+ enabled={bottomSheetEnabled}
+ onClose={() => setBottomSheetEnabled(false)}
+ />
+ );
+
+};
+
+export default ChatMoreActionsBottomSheet;
diff --git a/src/screens/group/GroupChatScreen/components/ScreenHeader/index.tsx b/src/screens/group/GroupChatScreen/components/ScreenHeader/index.tsx
index ff9c0fa8..fed9d945 100644
--- a/src/screens/group/GroupChatScreen/components/ScreenHeader/index.tsx
+++ b/src/screens/group/GroupChatScreen/components/ScreenHeader/index.tsx
@@ -11,12 +11,14 @@ interface ScreenHeaderProps {
groupName: string;
isHost: boolean;
color: Color;
+ onMoreButtonPress: () => void;
}
const ScreenHeader = ({
groupName,
isHost,
color,
+ onMoreButtonPress,
}: ScreenHeaderProps) => {
const navigation = useStackNavigation();
const [isNotiEnabled, setIsNotiEnabled] = useState(true);
@@ -46,6 +48,7 @@ const ScreenHeader = ({
diff --git a/src/screens/group/GroupChatScreen/components/UserInfosDialog/index.style.ts b/src/screens/group/GroupChatScreen/components/UserInfosDialog/index.style.ts
new file mode 100644
index 00000000..439ce7d7
--- /dev/null
+++ b/src/screens/group/GroupChatScreen/components/UserInfosDialog/index.style.ts
@@ -0,0 +1,39 @@
+import { StyleSheet } from 'react-native';
+
+import type { TextProps } from '@/components/Text';
+import { theme } from '@/theme';
+
+export const styles = StyleSheet.create({
+ userInfoContainer: {
+ flexDirection: 'row',
+ gap: 12,
+ paddingHorizontal: 16,
+ alignItems: 'center',
+ },
+ scrollView: {
+ flexGrow: 0,
+ maxHeight: 500,
+ borderWidth: 1,
+ borderColor: theme.color.neutral[300],
+ borderRadius: theme.radius[200],
+ paddingVertical: 8,
+ },
+ scrollViewContentContainer: {
+ paddingVertical: 8,
+ gap: 12,
+ },
+});
+
+export const TITLE_TEXT_STYLE: TextProps = {
+ font: 't1',
+ textStyle: {
+ alignSelf: 'center',
+ },
+ containerStyle: {
+ paddingBottom: 12,
+ },
+};
+
+export const USER_INFO_NICKNAME_TEXT_STYLE: TextProps = {
+ font: 'b3',
+};
\ No newline at end of file
diff --git a/src/screens/group/GroupChatScreen/components/UserInfosDialog/index.tsx b/src/screens/group/GroupChatScreen/components/UserInfosDialog/index.tsx
new file mode 100644
index 00000000..f4d965bf
--- /dev/null
+++ b/src/screens/group/GroupChatScreen/components/UserInfosDialog/index.tsx
@@ -0,0 +1,37 @@
+import { ScrollView, View } from 'react-native';
+
+import { Avatar, Dialog, Text, TText } from '@/components';
+import type { GetChatRoomInfoResponse } from '@/features/chat/model/getChatRoomInfo';
+
+import { styles, TITLE_TEXT_STYLE } from './index.style';
+
+interface UserInfosDialogProps {
+ userInfos: GetChatRoomInfoResponse['data']['userInfos'];
+}
+
+const UserInfosDialog = ({ userInfos }: UserInfosDialogProps) => (
+
+
+
+
+ {
+ userInfos.map((userInfo) => (
+
+
+ {userInfo.nickname}
+
+ ))
+ }
+
+
+
+);
+
+export default UserInfosDialog;
\ No newline at end of file
diff --git a/src/screens/group/GroupChatScreen/index.tsx b/src/screens/group/GroupChatScreen/index.tsx
index 1428d006..74ec8788 100644
--- a/src/screens/group/GroupChatScreen/index.tsx
+++ b/src/screens/group/GroupChatScreen/index.tsx
@@ -10,6 +10,7 @@ import { StackableScreenLayout } from '@/layout';
import type { Chat } from './components/ChatItem/types';
import ChatList from './components/ChatList';
+import ChatMoreActionsBottomSheet from './components/ChatMoreActionsBottomSheet';
import MessageInput from './components/MessageInput';
import ScreenHeader from './components/ScreenHeader';
import { useGroupChatBootstrap } from './hooks/useGroupChatBootstrap';
@@ -74,6 +75,8 @@ const GroupChatScreen = () => {
sendChatMessage,
});
+ const [isMoreActionsBottomSheetEnabled, setIsMoreActionsBottomSheetEnabled] = useState(false);
+
if (!chatRoomInfoData || isChatRoomInfoPending || !profileData || isProfilePending) {
return null;
}
@@ -86,6 +89,7 @@ const GroupChatScreen = () => {
color={GROUP_COLORS[groupColorKey].strong}
groupName={groupName}
isHost={false}
+ onMoreButtonPress={() => setIsMoreActionsBottomSheetEnabled(true)}
/>
}
keyboardControllerType='keyboardAvoidingView'
@@ -103,6 +107,11 @@ const GroupChatScreen = () => {
groupColorKey={groupColorKey}
handleSend={handleSend}
/>
+
);
};
diff --git a/src/screens/group/groupDetail/components/GroupColorPickerDialog/ColorPickItem/index.tsx b/src/screens/group/groupDetail/components/GroupColorPickerDialog/ColorPickItem/index.tsx
index 61f138c8..4efe66fb 100644
--- a/src/screens/group/groupDetail/components/GroupColorPickerDialog/ColorPickItem/index.tsx
+++ b/src/screens/group/groupDetail/components/GroupColorPickerDialog/ColorPickItem/index.tsx
@@ -17,7 +17,7 @@ const ColorPickItem = ({
}: ColorPickItemProps) => {
const styles = createStyles(isSelected, colorKey);
- return (
+ return (
onSelect(colorKey)}
style={styles.container}