diff --git a/apps/audius-client/packages/mobile/src/app/Drawers.tsx b/apps/audius-client/packages/mobile/src/app/Drawers.tsx index f4b33bb40c6..e5203ae1559 100644 --- a/apps/audius-client/packages/mobile/src/app/Drawers.tsx +++ b/apps/audius-client/packages/mobile/src/app/Drawers.tsx @@ -18,6 +18,7 @@ import { EnablePushNotificationsDrawer } from 'app/components/enable-push-notifi import { FeedFilterDrawer } from 'app/components/feed-filter-drawer' import { ForgotPasswordDrawer } from 'app/components/forgot-password-drawer' import { GatedContentUploadPromptDrawer } from 'app/components/gated-content-upload-prompt-drawer/GatedContentUploadPromptDrawer' +import { InboxUnavailableDrawer } from 'app/components/inbox-unavailable-drawer/InboxUnavailableDrawer' import { LockedContentDrawer } from 'app/components/locked-content-drawer' import { OverflowMenuDrawer } from 'app/components/overflow-menu-drawer' import { PlaybackRateDrawer } from 'app/components/playback-rate-drawer' @@ -118,7 +119,8 @@ const nativeDrawersMap: { [DrawerName in Drawer]?: ComponentType } = { GatedContentUploadPrompt: GatedContentUploadPromptDrawer, ChatActions: ChatActionsDrawer, BlockMessages: BlockMessagesDrawer, - SupportersInfo: SupportersInfoDrawer + SupportersInfo: SupportersInfoDrawer, + InboxUnavailable: InboxUnavailableDrawer } const commonDrawers = Object.entries(commonDrawersMap) as [ diff --git a/apps/audius-client/packages/mobile/src/assets/images/iconMessageLocked.svg b/apps/audius-client/packages/mobile/src/assets/images/iconMessageLocked.svg new file mode 100644 index 00000000000..c8a3b303043 --- /dev/null +++ b/apps/audius-client/packages/mobile/src/assets/images/iconMessageLocked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/audius-client/packages/mobile/src/components/inbox-unavailable-drawer/InboxUnavailableDrawer.tsx b/apps/audius-client/packages/mobile/src/components/inbox-unavailable-drawer/InboxUnavailableDrawer.tsx new file mode 100644 index 00000000000..d1d8890043f --- /dev/null +++ b/apps/audius-client/packages/mobile/src/components/inbox-unavailable-drawer/InboxUnavailableDrawer.tsx @@ -0,0 +1,260 @@ +import type { ReactNode } from 'react' +import { useCallback, useMemo } from 'react' + +import type { User } from '@audius/common' +import { + chatSelectors, + chatActions, + tippingActions, + cacheUsersSelectors, + ChatPermissionAction +} from '@audius/common' +import { View } from 'react-native' +import { useDispatch, useSelector } from 'react-redux' + +import IconMessageLocked from 'app/assets/images/iconMessageLocked.svg' +import IconTip from 'app/assets/images/iconTip.svg' +import { Text, Button } from 'app/components/core' +import { NativeDrawer } from 'app/components/drawer' +import { useNavigation } from 'app/hooks/useNavigation' +import { getData } from 'app/store/drawers/selectors' +import { setVisibility } from 'app/store/drawers/slice' +import { makeStyles, flexRowCentered } from 'app/styles' +import { useColor } from 'app/utils/theme' + +import { UserBadges } from '../user-badges' + +const { unblockUser } = chatActions +const { getCanChat } = chatSelectors +const { getUser } = cacheUsersSelectors +const { beginTip } = tippingActions + +const INBOX_UNAVAILABLE_MODAL_NAME = 'InboxUnavailable' + +const messages = { + title: 'Inbox Unavailable', + blockee: 'You cannot send messages to users you have blocked.', + tipGated: (displayName: ReactNode) => ( + <> + {'You must send '} + {displayName} + {' a tip before you can send them messages.'} + + ), + noAction: 'You can no longer send messages to ', + info: 'This will not affect their ability to view your profile or interact with your content.', + unblockUser: 'Unblock User', + learnMore: 'Learn More', + sendAudio: 'Send $AUDIO', + cancel: 'Cancel' +} + +const useStyles = makeStyles(({ spacing, typography, palette }) => ({ + drawer: { + marginTop: spacing(2), + marginBottom: spacing(5), + padding: spacing(3.5), + gap: spacing(4) + }, + titleContainer: { + ...flexRowCentered(), + gap: spacing(3.5), + alignSelf: 'center' + }, + title: { + fontSize: typography.fontSize.xl, + fontFamily: typography.fontByWeight.heavy, + color: palette.neutralLight2, + textTransform: 'uppercase', + lineHeight: typography.fontSize.xl * 1.25 + }, + infoContainer: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + gap: spacing(4.5), + paddingVertical: spacing(2), + paddingHorizontal: spacing(4), + backgroundColor: palette.neutralLight9, + borderWidth: 1, + borderColor: palette.neutralLight7, + borderRadius: spacing(2) + }, + callToActionText: { + color: palette.neutral, + fontSize: typography.fontSize.large, + fontFamily: typography.fontByWeight.medium, + lineHeight: typography.fontSize.large * 1.3, + textAlign: 'center' + }, + button: { + padding: spacing(4), + height: spacing(12) + }, + buttonText: { + color: palette.neutral, + fontSize: typography.fontSize.large, + fontFamily: typography.fontByWeight.bold + }, + buttonTextWhite: { + color: palette.white + }, + border: { + borderBottomWidth: 1, + borderBottomColor: palette.neutralLight8 + } +})) + +const mapActionToContent = ( + callToAction: ChatPermissionAction, + styles: ReturnType, + user: User | null +) => { + switch (callToAction) { + case ChatPermissionAction.NONE: + return ( + <> + {messages.noAction} + {user ? ( + + ) : null} + + ) + case ChatPermissionAction.TIP: + return ( + <> + {messages.tipGated( + user ? ( + + ) : null + )} + + ) + case ChatPermissionAction.UNBLOCK: + return <>{messages.blockee} + default: + return null + } +} + +export const InboxUnavailableDrawer = () => { + const dispatch = useDispatch() + const navigation = useNavigation() + const styles = useStyles() + const neutralLight2 = useColor('neutralLight2') + + const { userId } = useSelector((state) => getData<'InboxUnavailable'>(state)) + const user = useSelector((state) => getUser(state, { id: userId })) + const { callToAction } = useSelector((state) => getCanChat(state, userId)) + + const closeDrawer = useCallback(() => { + dispatch( + setVisibility({ + drawer: 'InboxUnavailable', + visible: false + }) + ) + }, [dispatch]) + + const handleUnblockPress = useCallback(() => { + dispatch(unblockUser({ userId })) + closeDrawer() + }, [dispatch, userId, closeDrawer]) + + const handleLearnMorePress = useCallback(() => { + // TODO: Link to blog + closeDrawer() + }, [closeDrawer]) + + const handleTipPress = useCallback(() => { + dispatch(beginTip({ user, source: 'profile' })) + navigation.navigate('TipArtist') + closeDrawer() + }, [closeDrawer, dispatch, navigation, user]) + + const actionToButtonsMap = useMemo(() => { + return { + [ChatPermissionAction.NONE]: [ + { + buttonTitle: messages.learnMore, + buttonPress: handleLearnMorePress, + buttonVariant: 'common' + } + ], + [ChatPermissionAction.TIP]: [ + { + buttonTitle: messages.sendAudio, + buttonPress: handleTipPress, + buttonVariant: 'primary', + buttonIcon: IconTip, + buttonTextStyle: styles.buttonTextWhite + } + ], + [ChatPermissionAction.UNBLOCK]: [ + { + buttonTitle: messages.unblockUser, + buttonPress: handleUnblockPress, + buttonVariant: 'primary', + buttonTextStyle: styles.buttonTextWhite + }, + { + buttonTitle: messages.cancel, + buttonPress: closeDrawer, + buttonVariant: 'common' + } + ] + } + }, [ + handleLearnMorePress, + handleTipPress, + styles.buttonTextWhite, + handleUnblockPress, + closeDrawer + ]) + + const content = mapActionToContent(callToAction, styles, user) + + return ( + + + + + {messages.title} + + + {content} + {actionToButtonsMap[callToAction].map( + ({ + buttonTitle, + buttonPress, + buttonVariant, + buttonIcon, + buttonTextStyle + }) => ( +