Skip to content

Commit

Permalink
[PAY-907] Mobile chat reactions (#3020)
Browse files Browse the repository at this point in the history
  • Loading branch information
dharit-tan authored Mar 22, 2023
1 parent 4678b89 commit 00f27e8
Show file tree
Hide file tree
Showing 7 changed files with 558 additions and 126 deletions.
3 changes: 2 additions & 1 deletion packages/mobile/src/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PortalProvider } from '@gorhom/portal'
import { PortalProvider, PortalHost } from '@gorhom/portal'
import * as Sentry from '@sentry/react-native'
import { Platform, UIManager } from 'react-native'
import Config from 'react-native-config'
Expand Down Expand Up @@ -82,6 +82,7 @@ const App = () => {
<WalletConnectProvider>
<PortalProvider>
<ErrorBoundary>
<PortalHost name='ChatReactionsPortal' />
<NavigationContainer>
<Toasts />
<Airplay />
Expand Down
200 changes: 156 additions & 44 deletions packages/mobile/src/screens/chat-screen/ChatMessageListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import { forwardRef } from 'react'

import type { ReactionTypes } from '@audius/common'
import {
accountSelectors,
decodeHashId,
formatMessageDate
} from '@audius/common'
import type { ChatMessage } from '@audius/sdk'
import type { ChatMessage, ChatMessageReaction } from '@audius/sdk'
import type { ViewStyle, StyleProp } from 'react-native'
import { View } from 'react-native'
import { TouchableWithoutFeedback } from 'react-native-gesture-handler'
import { useSelector } from 'react-redux'

import ChatTail from 'app/assets/images/ChatTail.svg'
import { Text } from 'app/components/core'
import { makeStyles } from 'app/styles'
import { useThemePalette } from 'app/utils/theme'

import { reactionMap } from '../notifications-screen/Reaction'

const { getUserId } = accountSelectors

const useStyles = makeStyles(({ spacing, palette, typography }) => ({
Expand All @@ -29,8 +36,8 @@ const useStyles = makeStyles(({ spacing, palette, typography }) => ({
marginTop: spacing(2),
backgroundColor: palette.white,
borderRadius: spacing(3),
shadowColor: '#000000',
shadowOffset: { width: 0, height: 3 },
shadowColor: 'black',
shadowOffset: { width: -2, height: 3 },
shadowOpacity: 0.2,
shadowRadius: 5
},
Expand All @@ -39,7 +46,8 @@ const useStyles = makeStyles(({ spacing, palette, typography }) => ({
},
message: {
fontSize: typography.fontSize.medium,
lineHeight: spacing(6)
lineHeight: spacing(6),
color: palette.neutral
},
messageIsAuthor: {
color: palette.white
Expand All @@ -55,7 +63,6 @@ const useStyles = makeStyles(({ spacing, palette, typography }) => ({
tail: {
display: 'flex',
position: 'absolute',
zIndex: -1,
bottom: 47
},
tailIsAuthor: {
Expand All @@ -64,53 +71,158 @@ const useStyles = makeStyles(({ spacing, palette, typography }) => ({
tailOtherUser: {
left: -spacing(3),
transform: [{ scaleX: -1 }]
},
tailShadow: {
position: 'absolute',
bottom: 0,
left: spacing(3),
backgroundColor: palette.background,
height: 0.2,
width: spacing(3),
shadowColor: 'black',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 1,
shadowRadius: 2
},
reaction: {
height: spacing(8),
width: spacing(8),
shadowColor: 'black',
shadowOffset: { width: 0, height: 3 },
shadowOpacity: 0.2,
shadowRadius: 5
},
reactionContainer: {
position: 'relative',
display: 'flex',
flexDirection: 'row-reverse',
justifyContent: 'flex-end',
bottom: spacing(4),
gap: -spacing(4),
height: 0
},
reactionContainerIsAuthor: {
right: spacing(4)
},
reactionContainerOtherUser: {
left: spacing(4),
flexDirection: 'row'
},
reactionMarginBottom: {
marginBottom: spacing(2)
}
}))

type ChatReactionProps = {
reaction: ChatMessageReaction
}

const ChatReaction = ({ reaction }: ChatReactionProps) => {
const styles = useStyles()

if (!reaction || !reaction.reaction || !(reaction.reaction in reactionMap)) {
return null
}
const Reaction = reactionMap[reaction.reaction as ReactionTypes]
return <Reaction style={styles.reaction} key={reaction.user_id} isVisible />
}

type ChatMessageListItemProps = {
message: ChatMessage
hasTail: boolean
unreadCount: number
shouldShowReaction?: boolean
shouldShowDate?: boolean
style?: StyleProp<ViewStyle>
onLongPress?: () => void
}

export const ChatMessageListItem = ({
message,
hasTail,
unreadCount
}: ChatMessageListItemProps) => {
const styles = useStyles()
const palette = useThemePalette()
export const ChatMessageListItem = forwardRef<View, ChatMessageListItemProps>(
(props: ChatMessageListItemProps, refProp) => {
const {
message,
hasTail,
shouldShowReaction = true,
shouldShowDate = true,
style: styleProp,
onLongPress
} = props
const styles = useStyles()
const palette = useThemePalette()

const userId = useSelector(getUserId)
const senderUserId = decodeHashId(message.sender_user_id)
const isAuthor = senderUserId === userId
const userId = useSelector(getUserId)
const senderUserId = decodeHashId(message.sender_user_id)
const isAuthor = senderUserId === userId

return (
<>
<View style={isAuthor ? styles.rootIsAuthor : styles.rootOtherUser}>
<View style={[styles.bubble, isAuthor && styles.isAuthor]}>
<Text style={[styles.message, isAuthor && styles.messageIsAuthor]}>
{message.message}
</Text>
return (
<>
<View
style={[
isAuthor ? styles.rootIsAuthor : styles.rootOtherUser,
!hasTail && message.reactions.length > 0
? styles.reactionMarginBottom
: null,
styleProp
]}
>
<View>
<TouchableWithoutFeedback onPress={onLongPress}>
<View>
<View
style={[styles.bubble, isAuthor && styles.isAuthor]}
ref={refProp}
>
<Text
style={[styles.message, isAuthor && styles.messageIsAuthor]}
>
{message.message}
</Text>
</View>
{message.reactions?.length > 0 ? (
<>
{shouldShowReaction ? (
<View
style={[
styles.reactionContainer,
isAuthor
? styles.reactionContainerIsAuthor
: styles.reactionContainerOtherUser
]}
>
{message.reactions.map((reaction, index) => {
return (
<ChatReaction key={index} reaction={reaction} />
)
})}
</View>
) : null}
</>
) : null}
</View>
</TouchableWithoutFeedback>
</View>
{hasTail ? (
<>
<View
style={[
styles.tail,
isAuthor ? styles.tailIsAuthor : styles.tailOtherUser,
!shouldShowDate && { bottom: 0 }
]}
>
<View style={styles.tailShadow} />
<ChatTail fill={isAuthor ? palette.secondary : palette.white} />
</View>
{shouldShowDate ? (
<View style={styles.dateContainer}>
<Text style={styles.date}>
{formatMessageDate(message.created_at)}
</Text>
</View>
) : null}
</>
) : null}
</View>
{hasTail ? (
<>
<View
style={[
styles.tail,
isAuthor ? styles.tailIsAuthor : styles.tailOtherUser
]}
>
<ChatTail fill={isAuthor ? palette.secondary : palette.white} />
</View>
<View style={styles.dateContainer}>
<Text style={styles.date}>
{formatMessageDate(message.created_at)}
</Text>
</View>
</>
) : null}
</View>
</>
)
}
</>
)
}
)
Loading

0 comments on commit 00f27e8

Please sign in to comment.