Skip to content

Commit

Permalink
feat(uikit): added reaction user list bottom sheet
Browse files Browse the repository at this point in the history
  • Loading branch information
bang9 committed Dec 6, 2022
1 parent 238bfa4 commit e9ef7e9
Show file tree
Hide file tree
Showing 12 changed files with 373 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ import createStyleSheet from '../../styles/createStyleSheet';
import useHeaderStyle from '../../styles/useHeaderStyle';
import useUIKitTheme from '../../theme/useUIKitTheme';

type ModalAnimationType = 'slide' | 'slide-no-gesture' | 'fade';
type Props = {
type?: 'slide' | 'fade';
type?: ModalAnimationType;
onClose: () => void;
backgroundStyle?: StyleProp<ViewStyle>;
disableBackgroundClose?: boolean;
Expand Down Expand Up @@ -117,12 +118,12 @@ const isHideGesture = (distanceY: number, velocityY: number) => {
return distanceY > 125 || (distanceY > 0 && velocityY > 0.1);
};
const useModalPanResponder = (
type: 'slide' | 'fade',
type: ModalAnimationType,
translateY: Animated.Value,
show: () => void,
hide: () => void,
) => {
if (type === 'fade') return { panHandlers: {} };
if (type === 'fade' || type === 'slide-no-gesture') return { panHandlers: {} };
return React.useRef(
PanResponder.create({
onMoveShouldSetPanResponderCapture: (_, { dy }) => dy > 8,
Expand All @@ -137,15 +138,15 @@ const useModalPanResponder = (
).current;
};

const useModalAnimation = (type: 'slide' | 'fade') => {
const initialY = type === 'slide' ? Dimensions.get('window').height : 0;
const useModalAnimation = (type: ModalAnimationType) => {
const initialY = type === 'fade' ? 0 : Dimensions.get('window').height;
const baseAnimBackground = useRef(new Animated.Value(0)).current;
const baseAnimContent = useRef(new Animated.Value(initialY)).current;

const content = {
opacity: baseAnimBackground.interpolate({
inputRange: [0, 1],
outputRange: [type === 'slide' ? 1 : 0, 1],
outputRange: [type === 'fade' ? 0 : 1, 1],
}),
translateY: baseAnimContent,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ const DarkUIKitTheme = createTheme({
highlight: palette.onBackgroundDark03,
},
selected: {
background: palette.primary400,
background: palette.primary500,
highlight: palette.primary200,
},
},
Expand All @@ -196,7 +196,7 @@ const DarkUIKitTheme = createTheme({
highlight: palette.transparent,
},
selected: {
background: palette.primary400,
background: palette.primary500,
highlight: palette.transparent,
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import { DEFAULT_LONG_PRESS_DELAY } from '../../constants';
import type { GroupChannelProps } from '../../domain/groupChannel/types';
import { useSendbirdChat } from '../../hooks/useContext';
import { MessageReactionAddon } from '../ReactionAddons';
import { ReactionAddons } from '../ReactionAddons';
import AdminMessage from './AdminMessage';
import FileMessage from './FileMessage';
import MessageContainer from './MessageContainer';
Expand Down Expand Up @@ -62,7 +62,7 @@ const MessageRenderer: GroupChannelProps['Fragment']['renderMessage'] = ({

const reactionChildren = useIIFE(() => {
if (shouldRenderReaction(channel, features.reactionEnabled) && message.reactions && message.reactions.length > 0) {
return <MessageReactionAddon channel={channel} message={message} />;
return <ReactionAddons.Message channel={channel} message={message} />;
}
return null;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,20 @@ const BottomSheetReactionAddon = ({ onClose, message, channel }: Props) => {
return (
<View style={styles.container}>
{emojiAll.map(({ key, url }) => {
const reactedUserIds = message?.reactions?.find((it) => it.key === key)?.userIds ?? [];
const reactionUserIds = message?.reactions?.find((it) => it.key === key)?.userIds ?? [];
const currentUserIdx = reactionUserIds.indexOf(currentUser?.userId ?? UNKNOWN_USER_ID);
const reacted = currentUserIdx > -1;

const idx = reactedUserIds.indexOf(currentUser?.userId ?? UNKNOWN_USER_ID);
const reacted = idx > -1;
const onPress = () => {
if (reacted) channel.deleteReaction(message, key);
else channel.addReaction(message, key);
onClose();
};

return (
<Pressable
key={key}
onPress={() => {
if (reacted) channel.deleteReaction(message, key);
else channel.addReaction(message, key);
onClose();
}}
onPress={onPress}
style={({ pressed }) => [
styles.button,
{ backgroundColor: reacted || pressed ? color.selected.background : color.enabled.background },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import { Pressable } from 'react-native';
import type { Emoji } from '@sendbird/chat';
import { createStyleSheet, useUIKitTheme } from '@sendbird/uikit-react-native-foundation';
import type { SendbirdBaseChannel, SendbirdBaseMessage, SendbirdReaction } from '@sendbird/uikit-utils';
import { getReactionCount } from '@sendbird/uikit-utils';

import { UNKNOWN_USER_ID } from '../../constants';
import { DEFAULT_LONG_PRESS_DELAY, UNKNOWN_USER_ID } from '../../constants';
import { useReaction, useSendbirdChat } from '../../hooks/useContext';
import ReactionRoundedButton from './ReactionRoundedButton';

const NUM_COL = 4;
const REACTION_MORE_KEY = 'reaction-more-button';

const getUserReacted = (reaction: SendbirdReaction, userId?: string) => {
return reaction.userIds.indexOf(userId ?? UNKNOWN_USER_ID) > -1;
const getUserReacted = (reaction: SendbirdReaction, userId = UNKNOWN_USER_ID) => {
return reaction.userIds.indexOf(userId) > -1;
};

const createOnPressReaction = (
Expand All @@ -36,7 +37,8 @@ const createReactionButtons = (
message: SendbirdBaseMessage,
getEmoji: (key: string) => Emoji,
emojiLimit: number,
onPressMore: () => void,
onOpenReactionList: () => void,
onOpenReactionUserList: (focusIndex: number) => void,
currentUserId?: string,
) => {
const reactions = message.reactions ?? [];
Expand All @@ -47,11 +49,13 @@ const createReactionButtons = (
<Pressable
key={reaction.key}
onPress={createOnPressReaction(reaction, channel, message, getUserReacted(reaction, currentUserId))}
onLongPress={() => onOpenReactionUserList(index)}
delayLongPress={DEFAULT_LONG_PRESS_DELAY}
>
{({ pressed }) => (
<ReactionRoundedButton
url={getEmoji(reaction.key).url}
count={reaction.userIds.length}
count={getReactionCount(reaction)}
reacted={pressed || getUserReacted(reaction, currentUserId)}
style={[isNotLastOfRow && styles.marginRight, isNotLastOfCol && styles.marginBottom]}
/>
Expand All @@ -61,7 +65,7 @@ const createReactionButtons = (
});
if (buttons.length < emojiLimit) {
buttons.push(
<Pressable key={REACTION_MORE_KEY} onPress={onPressMore}>
<Pressable key={REACTION_MORE_KEY} onPress={onOpenReactionList}>
{({ pressed }) => <ReactionRoundedButton.More pressed={pressed} />}
</Pressable>,
);
Expand All @@ -73,7 +77,7 @@ const createReactionButtons = (
const MessageReactionAddon = ({ channel, message }: { channel: SendbirdBaseChannel; message: SendbirdBaseMessage }) => {
const { colors } = useUIKitTheme();
const { emojiManager, currentUser } = useSendbirdChat();
const { openReactionList } = useReaction();
const { openReactionList, openReactionUserList } = useReaction();

if (!message.reactions?.length) return null;

Expand All @@ -83,6 +87,7 @@ const MessageReactionAddon = ({ channel, message }: { channel: SendbirdBaseChann
(key) => emojiManager.allEmojiMap[key],
emojiManager.allEmoji.length,
() => openReactionList({ channel, message }),
(focusIndex) => openReactionUserList({ channel, message, focusIndex }),
currentUser?.userId,
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
export { default as BottomSheetReactionAddon } from './BottomSheetReactionAddon';
export { default as MessageReactionAddon } from './MessageReactionAddon';
import BottomSheet from './BottomSheetReactionAddon';
import Message from './MessageReactionAddon';

export const ReactionAddons = {
BottomSheet,
Message,
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,8 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';

import { Image, Modal, createStyleSheet, useUIKitTheme } from '@sendbird/uikit-react-native-foundation';

import { UNKNOWN_USER_ID } from '../constants';
import type { ReactionContext } from '../contexts/ReactionCtx';
import type { SendbirdChatContext } from '../contexts/SendbirdChatCtx';

type GetFromContext<T> = T extends React.Context<infer Value> ? NonNullable<Value> : never;
type ReactionBottomSheetProps = {
visible: boolean;
onDismiss: () => void;
onClose: () => void;
chatCtx: GetFromContext<typeof SendbirdChatContext>;
reactionCtx: GetFromContext<typeof ReactionContext>;
};

const ReactionUserListBottomSheet = ({ visible, onClose, onDismiss, reactionCtx }: ReactionBottomSheetProps) => {
const { width } = useWindowDimensions();
const { bottom, left, right } = useSafeAreaInsets();
const { colors } = useUIKitTheme();

// const { currentUser, emojiManager } = chatCtx;
const { channel, message } = reactionCtx;
// const color = colors.ui.reaction.default;

return (
<Modal
type={'slide'}
visible={Boolean(visible && channel && message)}
onClose={onClose}
onDismiss={onDismiss}
backgroundStyle={styles.modal}
>
<View
style={[
styles.container,
{
width,
paddingBottom: bottom,
backgroundColor: colors.ui.dialog.default.none.background,
paddingLeft: left + styles.container.paddingHorizontal,
paddingRight: right + styles.container.paddingHorizontal,
},
]}
></View>
</Modal>
);
};
import { UNKNOWN_USER_ID } from '../../constants';
import type { ReactionBottomSheetProps } from './index';

const NUM_COLUMN = 6;
const ReactionListBottomSheet = ({ visible, onClose, onDismiss, reactionCtx, chatCtx }: ReactionBottomSheetProps) => {
Expand Down Expand Up @@ -153,7 +110,4 @@ const styles = createStyleSheet({
},
});

export const ReactionBottomSheets = {
ReactionList: ReactionListBottomSheet,
UserList: ReactionUserListBottomSheet,
};
export default ReactionListBottomSheet;
Loading

0 comments on commit e9ef7e9

Please sign in to comment.