Skip to content
This repository has been archived by the owner on Oct 4, 2023. It is now read-only.

Commit

Permalink
[PAY-1702] Use existing chats as default user list when sharing to DMs (
Browse files Browse the repository at this point in the history
  • Loading branch information
rickyrombo authored Aug 14, 2023
1 parent bf09344 commit 1c7ec6f
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 22 deletions.
43 changes: 40 additions & 3 deletions packages/common/src/store/pages/chat/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { User } from 'models/User'
import { accountSelectors } from 'store/account'
import { cacheUsersSelectors } from 'store/cache'
import { CommonState } from 'store/reducers'
import { decodeHashId } from 'utils/hashIds'
import { Maybe } from 'utils/typeUtils'
import { decodeHashId, encodeHashId } from 'utils/hashIds'
import { Maybe, removeNullable } from 'utils/typeUtils'

import { chatMessagesAdapter, chatsAdapter } from './slice'
import { ChatPermissionAction } from './types'
Expand Down Expand Up @@ -168,6 +168,30 @@ export const getSingleOtherChatUser = (
return getOtherChatUsers(state, chatId)[0]
}

/**
* Gets a list of the users the current user has chats with.
* Note that this only takes the first user of each chat that doesn't match the current one,
* so this will need to be adjusted when we do group chats.
*/
export const getUserList = createSelector(
[getUserId, getChats, getHasMoreChats, getChatsStatus],
(currentUserId, chats, hasMore, chatsStatus) => {
const chatUserListIds = chats
.map(
(c) =>
c.chat_members
.filter((u) => decodeHashId(u.user_id) !== currentUserId)
.map((u) => decodeHashId(u.user_id))[0]
)
.filter(removeNullable)
return {
userIds: chatUserListIds,
hasMore,
loading: chatsStatus === Status.LOADING
}
}
)

export const getChatMessageByIndex = (
state: CommonState,
chatId: string,
Expand Down Expand Up @@ -228,6 +252,7 @@ export const getCanCreateChat = createSelector(
getBlockees,
getBlockers,
getChatPermissions,
getChats,
(state: CommonState, { userId }: { userId: Maybe<ID> }) => {
if (!userId) return null
const usersMap = getUsers(state, { ids: [userId] })
Expand All @@ -239,6 +264,7 @@ export const getCanCreateChat = createSelector(
blockees,
blockers,
chatPermissions,
chats,
user
): { canCreateChat: boolean; callToAction: ChatPermissionAction } => {
if (!currentUserId) {
Expand All @@ -254,13 +280,24 @@ export const getCanCreateChat = createSelector(
}
}

// Check for existing chat, since unblocked users with existing chats
// don't need permission to continue chatting.
// Use a callback fn to prevent iteration until necessary to improve perf
// Note: this only works if the respective chat has been fetched already, like in chatsUserList
const encodedUserId = encodeHashId(user.user_id)
const hasExistingChat = () =>
!!chats.find((c) =>
c.chat_members.find((u) => u.user_id === encodedUserId)
)

const userPermissions = chatPermissions[user.user_id]
const isBlockee = blockees.includes(user.user_id)
const isBlocker = blockers.includes(user.user_id)
const canCreateChat =
!isBlockee &&
!isBlocker &&
(userPermissions?.current_user_has_permission ?? true)
((userPermissions?.current_user_has_permission ?? true) ||
hasExistingChat())

let action = ChatPermissionAction.NOT_APPLICABLE
if (!canCreateChat) {
Expand Down
4 changes: 3 additions & 1 deletion packages/common/src/store/ui/create-chat-modal/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import { Action } from '@reduxjs/toolkit'
import { createModal } from '../modals/createModal'

export type CreateChatModalState = {
defaultUserList?: 'followers' | 'chats'
presetMessage?: string
onCancelAction?: Action
}

const createChatModal = createModal<CreateChatModalState>({
reducerPath: 'createChatModal',
initialState: {
isOpen: false
isOpen: false,
defaultUserList: 'followers'
},
sliceSelector: (state) => state.ui.modalsWithState
})
Expand Down
3 changes: 2 additions & 1 deletion packages/mobile/src/components/share-drawer/ShareDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ export const ShareDrawer = () => {
const handleShareToDirectMessage = useCallback(async () => {
if (!content) return
navigation.navigate('ChatUserList', {
presetMessage: getContentUrl(content)
presetMessage: getContentUrl(content),
defaultUserList: 'chats'
})
track(make({ eventName: Name.CHAT_ENTRY_POINT, source: 'share' }))
}, [content, navigation])
Expand Down
4 changes: 3 additions & 1 deletion packages/mobile/src/screens/app-screen/AppTabScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import type {
NotificationType,
RepostType,
SearchPlaylist,
SearchTrack
SearchTrack,
CreateChatModalState
} from '@audius/common'
import { FeatureFlags } from '@audius/common'
import type { EventArg, NavigationState } from '@react-navigation/native'
Expand Down Expand Up @@ -110,6 +111,7 @@ export type AppTabScreenParamList = {
ChatUserList:
| {
presetMessage?: string
defaultUserList?: CreateChatModalState['defaultUserList']
}
| undefined
Chat: {
Expand Down
21 changes: 15 additions & 6 deletions packages/mobile/src/screens/chat-screen/ChatUserListScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useState } from 'react'

import type { User } from '@audius/common'
import type { CreateChatModalState, User } from '@audius/common'
import {
FOLLOWERS_USER_LIST_TAG,
Status,
Expand All @@ -12,6 +12,7 @@ import {
searchUsersModalActions,
searchUsersModalSelectors,
statusIsNotFinalized,
chatSelectors,
useProxySelector,
userListActions
} from '@audius/common'
Expand All @@ -36,6 +37,8 @@ const { searchUsers } = searchUsersModalActions
const { getUserList } = searchUsersModalSelectors
const { getUsers } = cacheUsersSelectors
const { fetchBlockees, fetchBlockers, fetchPermissions } = chatActions
const { getUserList: getChatsUserList } = chatSelectors
const { getUserList: getFollowersUserList } = followersUserListSelectors

const DEBOUNCE_MS = 150

Expand Down Expand Up @@ -141,12 +144,17 @@ const ListEmpty = () => {
)
}

const useDefaultUserList = () => {
const useDefaultUserList = (
defaultUserList: CreateChatModalState['defaultUserList']
) => {
const dispatch = useDispatch()
const currentUser = useSelector(getAccountUser)
const { hasMore, loading, userIds } = useSelector(
followersUserListSelectors.getUserList
)
const followersUserList = useSelector(getFollowersUserList)
const chatsUserList = useSelector(getChatsUserList)

const { hasMore, loading, userIds } =
defaultUserList === 'chats' ? chatsUserList : followersUserList

const loadMore = useCallback(() => {
if (currentUser) {
dispatch(followersUserListActions.setFollowers(currentUser?.user_id))
Expand Down Expand Up @@ -190,7 +198,8 @@ export const ChatUserListScreen = () => {
const dispatch = useDispatch()
const { params } = useRoute<'ChatUserList'>()
const presetMessage = params?.presetMessage
const defaultUserList = useDefaultUserList()
const defaultUserListType = params?.defaultUserList
const defaultUserList = useDefaultUserList(defaultUserListType)
const queryUserList = useQueryUserList(query)

const hasQuery = query.length > 0
Expand Down
3 changes: 2 additions & 1 deletion packages/web/src/components/share-modal/ShareModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ export const ShareModal = () => {
openCreateChatModal({
// Just care about the link
presetMessage: (await getTwitterShareText(content, false)).link,
onCancelAction: setVisibility({ modal: 'Share', visible: true })
onCancelAction: setVisibility({ modal: 'Share', visible: true }),
defaultUserList: 'chats'
})
dispatch(make(Name.CHAT_ENTRY_POINT, { source: 'share' }))
}, [openCreateChatModal, dispatch, onClose, content])
Expand Down
26 changes: 17 additions & 9 deletions packages/web/src/pages/chat-page/components/CreateChatModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
useCreateChatModal,
useInboxUnavailableModal,
createChatModalActions,
searchUsersModalActions
searchUsersModalActions,
chatSelectors
} from '@audius/common'
import { IconCompose } from '@audius/stems'
import { useDispatch } from 'react-redux'
Expand All @@ -27,18 +28,21 @@ const messages = {
}

const { getAccountUser } = accountSelectors
const { fetchBlockers } = chatActions
const { getUserList: getFollowersUserList } = followersUserListSelectors
const { getUserList: getChatsUserList } = chatSelectors
const { fetchBlockers, fetchMoreChats } = chatActions

export const CreateChatModal = () => {
const dispatch = useDispatch()
const currentUser = useSelector(getAccountUser)
const { isOpen, onClose, onClosed, data } = useCreateChatModal()
const { onOpen: openInboxUnavailableModal } = useInboxUnavailableModal()
const { onCancelAction, presetMessage } = data
const { onCancelAction, presetMessage, defaultUserList } = data

const { userIds, loading, hasMore } = useSelector(
followersUserListSelectors.getUserList
)
const followersUserList = useSelector(getFollowersUserList)
const chatsUserList = useSelector(getChatsUserList)
const { userIds, hasMore, loading } =
defaultUserList === 'chats' ? chatsUserList : followersUserList

const handleCancel = useCallback(() => {
if (onCancelAction) {
Expand All @@ -48,10 +52,14 @@ export const CreateChatModal = () => {

const loadMore = useCallback(() => {
if (currentUser) {
dispatch(followersUserListActions.setFollowers(currentUser?.user_id))
dispatch(userListActions.loadMore(FOLLOWERS_USER_LIST_TAG))
if (defaultUserList === 'chats') {
dispatch(fetchMoreChats())
} else {
dispatch(followersUserListActions.setFollowers(currentUser?.user_id))
dispatch(userListActions.loadMore(FOLLOWERS_USER_LIST_TAG))
}
}
}, [dispatch, currentUser])
}, [dispatch, defaultUserList, currentUser])

const handleOpenInboxUnavailableModal = useCallback(
(user: User) => {
Expand Down

0 comments on commit 1c7ec6f

Please sign in to comment.