Skip to content

Commit 1c7ec6f

Browse files
authored
[PAY-1702] Use existing chats as default user list when sharing to DMs (#3877)
1 parent bf09344 commit 1c7ec6f

File tree

7 files changed

+82
-22
lines changed

7 files changed

+82
-22
lines changed

packages/common/src/store/pages/chat/selectors.ts

+40-3
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import { User } from 'models/User'
77
import { accountSelectors } from 'store/account'
88
import { cacheUsersSelectors } from 'store/cache'
99
import { CommonState } from 'store/reducers'
10-
import { decodeHashId } from 'utils/hashIds'
11-
import { Maybe } from 'utils/typeUtils'
10+
import { decodeHashId, encodeHashId } from 'utils/hashIds'
11+
import { Maybe, removeNullable } from 'utils/typeUtils'
1212

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

171+
/**
172+
* Gets a list of the users the current user has chats with.
173+
* Note that this only takes the first user of each chat that doesn't match the current one,
174+
* so this will need to be adjusted when we do group chats.
175+
*/
176+
export const getUserList = createSelector(
177+
[getUserId, getChats, getHasMoreChats, getChatsStatus],
178+
(currentUserId, chats, hasMore, chatsStatus) => {
179+
const chatUserListIds = chats
180+
.map(
181+
(c) =>
182+
c.chat_members
183+
.filter((u) => decodeHashId(u.user_id) !== currentUserId)
184+
.map((u) => decodeHashId(u.user_id))[0]
185+
)
186+
.filter(removeNullable)
187+
return {
188+
userIds: chatUserListIds,
189+
hasMore,
190+
loading: chatsStatus === Status.LOADING
191+
}
192+
}
193+
)
194+
171195
export const getChatMessageByIndex = (
172196
state: CommonState,
173197
chatId: string,
@@ -228,6 +252,7 @@ export const getCanCreateChat = createSelector(
228252
getBlockees,
229253
getBlockers,
230254
getChatPermissions,
255+
getChats,
231256
(state: CommonState, { userId }: { userId: Maybe<ID> }) => {
232257
if (!userId) return null
233258
const usersMap = getUsers(state, { ids: [userId] })
@@ -239,6 +264,7 @@ export const getCanCreateChat = createSelector(
239264
blockees,
240265
blockers,
241266
chatPermissions,
267+
chats,
242268
user
243269
): { canCreateChat: boolean; callToAction: ChatPermissionAction } => {
244270
if (!currentUserId) {
@@ -254,13 +280,24 @@ export const getCanCreateChat = createSelector(
254280
}
255281
}
256282

283+
// Check for existing chat, since unblocked users with existing chats
284+
// don't need permission to continue chatting.
285+
// Use a callback fn to prevent iteration until necessary to improve perf
286+
// Note: this only works if the respective chat has been fetched already, like in chatsUserList
287+
const encodedUserId = encodeHashId(user.user_id)
288+
const hasExistingChat = () =>
289+
!!chats.find((c) =>
290+
c.chat_members.find((u) => u.user_id === encodedUserId)
291+
)
292+
257293
const userPermissions = chatPermissions[user.user_id]
258294
const isBlockee = blockees.includes(user.user_id)
259295
const isBlocker = blockers.includes(user.user_id)
260296
const canCreateChat =
261297
!isBlockee &&
262298
!isBlocker &&
263-
(userPermissions?.current_user_has_permission ?? true)
299+
((userPermissions?.current_user_has_permission ?? true) ||
300+
hasExistingChat())
264301

265302
let action = ChatPermissionAction.NOT_APPLICABLE
266303
if (!canCreateChat) {

packages/common/src/store/ui/create-chat-modal/index.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@ import { Action } from '@reduxjs/toolkit'
33
import { createModal } from '../modals/createModal'
44

55
export type CreateChatModalState = {
6+
defaultUserList?: 'followers' | 'chats'
67
presetMessage?: string
78
onCancelAction?: Action
89
}
910

1011
const createChatModal = createModal<CreateChatModalState>({
1112
reducerPath: 'createChatModal',
1213
initialState: {
13-
isOpen: false
14+
isOpen: false,
15+
defaultUserList: 'followers'
1416
},
1517
sliceSelector: (state) => state.ui.modalsWithState
1618
})

packages/mobile/src/components/share-drawer/ShareDrawer.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ export const ShareDrawer = () => {
109109
const handleShareToDirectMessage = useCallback(async () => {
110110
if (!content) return
111111
navigation.navigate('ChatUserList', {
112-
presetMessage: getContentUrl(content)
112+
presetMessage: getContentUrl(content),
113+
defaultUserList: 'chats'
113114
})
114115
track(make({ eventName: Name.CHAT_ENTRY_POINT, source: 'share' }))
115116
}, [content, navigation])

packages/mobile/src/screens/app-screen/AppTabScreen.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import type {
77
NotificationType,
88
RepostType,
99
SearchPlaylist,
10-
SearchTrack
10+
SearchTrack,
11+
CreateChatModalState
1112
} from '@audius/common'
1213
import { FeatureFlags } from '@audius/common'
1314
import type { EventArg, NavigationState } from '@react-navigation/native'
@@ -110,6 +111,7 @@ export type AppTabScreenParamList = {
110111
ChatUserList:
111112
| {
112113
presetMessage?: string
114+
defaultUserList?: CreateChatModalState['defaultUserList']
113115
}
114116
| undefined
115117
Chat: {

packages/mobile/src/screens/chat-screen/ChatUserListScreen.tsx

+15-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useCallback, useEffect, useState } from 'react'
22

3-
import type { User } from '@audius/common'
3+
import type { CreateChatModalState, User } from '@audius/common'
44
import {
55
FOLLOWERS_USER_LIST_TAG,
66
Status,
@@ -12,6 +12,7 @@ import {
1212
searchUsersModalActions,
1313
searchUsersModalSelectors,
1414
statusIsNotFinalized,
15+
chatSelectors,
1516
useProxySelector,
1617
userListActions
1718
} from '@audius/common'
@@ -36,6 +37,8 @@ const { searchUsers } = searchUsersModalActions
3637
const { getUserList } = searchUsersModalSelectors
3738
const { getUsers } = cacheUsersSelectors
3839
const { fetchBlockees, fetchBlockers, fetchPermissions } = chatActions
40+
const { getUserList: getChatsUserList } = chatSelectors
41+
const { getUserList: getFollowersUserList } = followersUserListSelectors
3942

4043
const DEBOUNCE_MS = 150
4144

@@ -141,12 +144,17 @@ const ListEmpty = () => {
141144
)
142145
}
143146

144-
const useDefaultUserList = () => {
147+
const useDefaultUserList = (
148+
defaultUserList: CreateChatModalState['defaultUserList']
149+
) => {
145150
const dispatch = useDispatch()
146151
const currentUser = useSelector(getAccountUser)
147-
const { hasMore, loading, userIds } = useSelector(
148-
followersUserListSelectors.getUserList
149-
)
152+
const followersUserList = useSelector(getFollowersUserList)
153+
const chatsUserList = useSelector(getChatsUserList)
154+
155+
const { hasMore, loading, userIds } =
156+
defaultUserList === 'chats' ? chatsUserList : followersUserList
157+
150158
const loadMore = useCallback(() => {
151159
if (currentUser) {
152160
dispatch(followersUserListActions.setFollowers(currentUser?.user_id))
@@ -190,7 +198,8 @@ export const ChatUserListScreen = () => {
190198
const dispatch = useDispatch()
191199
const { params } = useRoute<'ChatUserList'>()
192200
const presetMessage = params?.presetMessage
193-
const defaultUserList = useDefaultUserList()
201+
const defaultUserListType = params?.defaultUserList
202+
const defaultUserList = useDefaultUserList(defaultUserListType)
194203
const queryUserList = useQueryUserList(query)
195204

196205
const hasQuery = query.length > 0

packages/web/src/components/share-modal/ShareModal.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ export const ShareModal = () => {
6060
openCreateChatModal({
6161
// Just care about the link
6262
presetMessage: (await getTwitterShareText(content, false)).link,
63-
onCancelAction: setVisibility({ modal: 'Share', visible: true })
63+
onCancelAction: setVisibility({ modal: 'Share', visible: true }),
64+
defaultUserList: 'chats'
6465
})
6566
dispatch(make(Name.CHAT_ENTRY_POINT, { source: 'share' }))
6667
}, [openCreateChatModal, dispatch, onClose, content])

packages/web/src/pages/chat-page/components/CreateChatModal.tsx

+17-9
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import {
1111
useCreateChatModal,
1212
useInboxUnavailableModal,
1313
createChatModalActions,
14-
searchUsersModalActions
14+
searchUsersModalActions,
15+
chatSelectors
1516
} from '@audius/common'
1617
import { IconCompose } from '@audius/stems'
1718
import { useDispatch } from 'react-redux'
@@ -27,18 +28,21 @@ const messages = {
2728
}
2829

2930
const { getAccountUser } = accountSelectors
30-
const { fetchBlockers } = chatActions
31+
const { getUserList: getFollowersUserList } = followersUserListSelectors
32+
const { getUserList: getChatsUserList } = chatSelectors
33+
const { fetchBlockers, fetchMoreChats } = chatActions
3134

3235
export const CreateChatModal = () => {
3336
const dispatch = useDispatch()
3437
const currentUser = useSelector(getAccountUser)
3538
const { isOpen, onClose, onClosed, data } = useCreateChatModal()
3639
const { onOpen: openInboxUnavailableModal } = useInboxUnavailableModal()
37-
const { onCancelAction, presetMessage } = data
40+
const { onCancelAction, presetMessage, defaultUserList } = data
3841

39-
const { userIds, loading, hasMore } = useSelector(
40-
followersUserListSelectors.getUserList
41-
)
42+
const followersUserList = useSelector(getFollowersUserList)
43+
const chatsUserList = useSelector(getChatsUserList)
44+
const { userIds, hasMore, loading } =
45+
defaultUserList === 'chats' ? chatsUserList : followersUserList
4246

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

4953
const loadMore = useCallback(() => {
5054
if (currentUser) {
51-
dispatch(followersUserListActions.setFollowers(currentUser?.user_id))
52-
dispatch(userListActions.loadMore(FOLLOWERS_USER_LIST_TAG))
55+
if (defaultUserList === 'chats') {
56+
dispatch(fetchMoreChats())
57+
} else {
58+
dispatch(followersUserListActions.setFollowers(currentUser?.user_id))
59+
dispatch(userListActions.loadMore(FOLLOWERS_USER_LIST_TAG))
60+
}
5361
}
54-
}, [dispatch, currentUser])
62+
}, [dispatch, defaultUserList, currentUser])
5563

5664
const handleOpenInboxUnavailableModal = useCallback(
5765
(user: User) => {

0 commit comments

Comments
 (0)