@planningcenter/chat-react-native 3.42.2-qa-staging.3 → 3.42.2-qa-staging.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/build/components/conversation/messages_disabled_banners.d.ts +5 -1
  2. package/build/components/conversation/messages_disabled_banners.d.ts.map +1 -1
  3. package/build/components/conversation/messages_disabled_banners.js +8 -4
  4. package/build/components/conversation/messages_disabled_banners.js.map +1 -1
  5. package/build/hooks/direct_messages/find_direct_message_conversation.d.ts +14 -0
  6. package/build/hooks/direct_messages/find_direct_message_conversation.d.ts.map +1 -0
  7. package/build/hooks/direct_messages/find_direct_message_conversation.js +10 -0
  8. package/build/hooks/direct_messages/find_direct_message_conversation.js.map +1 -0
  9. package/build/hooks/direct_messages/resolve_message_available_members_link.d.ts +16 -0
  10. package/build/hooks/direct_messages/resolve_message_available_members_link.d.ts.map +1 -0
  11. package/build/hooks/direct_messages/resolve_message_available_members_link.js +24 -0
  12. package/build/hooks/direct_messages/resolve_message_available_members_link.js.map +1 -0
  13. package/build/hooks/direct_messages/use_find_or_create_direct_message_conversation.d.ts.map +1 -1
  14. package/build/hooks/direct_messages/use_find_or_create_direct_message_conversation.js +1 -8
  15. package/build/hooks/direct_messages/use_find_or_create_direct_message_conversation.js.map +1 -1
  16. package/build/hooks/direct_messages/use_message_available_members.d.ts +6 -0
  17. package/build/hooks/direct_messages/use_message_available_members.d.ts.map +1 -0
  18. package/build/hooks/direct_messages/use_message_available_members.js +40 -0
  19. package/build/hooks/direct_messages/use_message_available_members.js.map +1 -0
  20. package/build/hooks/direct_messages/use_message_available_members_action.d.ts +6 -0
  21. package/build/hooks/direct_messages/use_message_available_members_action.d.ts.map +1 -0
  22. package/build/hooks/direct_messages/use_message_available_members_action.js +30 -0
  23. package/build/hooks/direct_messages/use_message_available_members_action.js.map +1 -0
  24. package/build/hooks/use_conversation.d.ts.map +1 -1
  25. package/build/hooks/use_conversation.js +1 -0
  26. package/build/hooks/use_conversation.js.map +1 -1
  27. package/build/screens/conversation_screen.d.ts.map +1 -1
  28. package/build/screens/conversation_screen.js +3 -1
  29. package/build/screens/conversation_screen.js.map +1 -1
  30. package/build/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.d.ts +1 -1
  31. package/build/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.d.ts.map +1 -1
  32. package/build/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.js +13 -2
  33. package/build/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.js.map +1 -1
  34. package/build/screens/conversation_select_recipients/types/screen_props.d.ts +2 -0
  35. package/build/screens/conversation_select_recipients/types/screen_props.d.ts.map +1 -1
  36. package/build/screens/conversation_select_recipients/types/screen_props.js.map +1 -1
  37. package/build/types/resources/direct_message_candidate.d.ts +1 -0
  38. package/build/types/resources/direct_message_candidate.d.ts.map +1 -1
  39. package/build/types/resources/direct_message_candidate.js.map +1 -1
  40. package/build/types/resources/member.d.ts +1 -0
  41. package/build/types/resources/member.d.ts.map +1 -1
  42. package/build/types/resources/member.js.map +1 -1
  43. package/build/types/resources/member_ability.d.ts +1 -0
  44. package/build/types/resources/member_ability.d.ts.map +1 -1
  45. package/build/types/resources/member_ability.js.map +1 -1
  46. package/package.json +3 -3
  47. package/src/__tests__/hooks/resolve_message_available_members_link.test.ts +72 -0
  48. package/src/components/conversation/messages_disabled_banners.tsx +31 -4
  49. package/src/hooks/direct_messages/find_direct_message_conversation.ts +16 -0
  50. package/src/hooks/direct_messages/resolve_message_available_members_link.ts +42 -0
  51. package/src/hooks/direct_messages/use_find_or_create_direct_message_conversation.ts +2 -11
  52. package/src/hooks/direct_messages/use_message_available_members.ts +65 -0
  53. package/src/hooks/direct_messages/use_message_available_members_action.ts +34 -0
  54. package/src/hooks/use_conversation.ts +1 -0
  55. package/src/screens/conversation_screen.tsx +9 -1
  56. package/src/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.tsx +18 -4
  57. package/src/screens/conversation_select_recipients/types/screen_props.tsx +2 -0
  58. package/src/types/resources/direct_message_candidate.ts +5 -0
  59. package/src/types/resources/member.ts +1 -0
  60. package/src/types/resources/member_ability.ts +1 -0
@@ -0,0 +1,42 @@
1
+ import { ConversationResource } from '../../types'
2
+ import { PreselectedDirectMessagePerson } from '../../types/resources/direct_message_candidate'
3
+ import { MemberResource } from '../../types/resources/member'
4
+
5
+ export type MessageAvailableMembersLink =
6
+ | { label: string; existingConversationId: number }
7
+ | { label: string; recipients: PreselectedDirectMessagePerson[] }
8
+
9
+ export function resolveMessageAvailableMembersLink({
10
+ availableMembers,
11
+ existingConversations,
12
+ canCreateDirectMessages,
13
+ }: {
14
+ availableMembers: Pick<MemberResource, 'id' | 'name' | 'avatar'>[]
15
+ existingConversations: Pick<ConversationResource, 'id' | 'disabled'>[] | undefined
16
+ canCreateDirectMessages: boolean
17
+ }): MessageAvailableMembersLink | undefined {
18
+ if (availableMembers.length === 0 || !existingConversations) return undefined
19
+
20
+ const label =
21
+ availableMembers.length === 1
22
+ ? `Message ${availableMembers[0].name.split(' ')[0]}`
23
+ : `Message the remaining ${availableMembers.length} people`
24
+
25
+ const existingConversation = existingConversations[0]
26
+ if (existingConversation) {
27
+ return existingConversation.disabled
28
+ ? undefined
29
+ : { label, existingConversationId: Number(existingConversation.id) }
30
+ }
31
+
32
+ if (!canCreateDirectMessages) return undefined
33
+
34
+ return {
35
+ label,
36
+ recipients: availableMembers.map(m => ({
37
+ id: m.id.toString(),
38
+ name: m.name,
39
+ avatar: m.avatar,
40
+ })),
41
+ }
42
+ }
@@ -1,17 +1,8 @@
1
1
  import { useQuery } from '@tanstack/react-query'
2
2
  import { useState, useCallback } from 'react'
3
- import { ApiCollection, ApiResource, ConversationResource } from '../../types'
3
+ import { ApiResource, ConversationResource } from '../../types'
4
4
  import { ApiClient, useApiClient } from '../use_api_client'
5
-
6
- const findDirectMessageConversation = (apiClient: ApiClient, participantIds: string[]) =>
7
- apiClient.chat.get({
8
- url: '/me/conversations',
9
- data: {
10
- fields: { Conversation: ['title'] },
11
- filter: 'with_exact_direct_message_participants',
12
- participant_ids: participantIds.join(','),
13
- },
14
- }) as Promise<ApiCollection<ConversationResource>>
5
+ import { findDirectMessageConversation } from './find_direct_message_conversation'
15
6
 
16
7
  const createDirectMessageConversation = (apiClient: ApiClient, ids: string[]) =>
17
8
  apiClient.chat.post({
@@ -0,0 +1,65 @@
1
+ import { useQuery } from '@tanstack/react-query'
2
+ import { useMemo } from 'react'
3
+ import { ConversationResource } from '../../types'
4
+ import { MemberResource } from '../../types/resources/member'
5
+ import { useApiGet } from '../use_api'
6
+ import { useApiClient } from '../use_api_client'
7
+ import { useCurrentPerson } from '../use_current_person'
8
+ import { getRequestQueryKey } from '../use_suspense_api'
9
+ import {
10
+ findDirectMessageConversation,
11
+ getFindDirectMessageConversationRequestArgs,
12
+ } from './find_direct_message_conversation'
13
+ import {
14
+ MessageAvailableMembersLink,
15
+ resolveMessageAvailableMembersLink,
16
+ } from './resolve_message_available_members_link'
17
+
18
+ export function useMessageAvailableMembers(
19
+ conversation: ConversationResource,
20
+ { enabled }: { enabled: boolean }
21
+ ): MessageAvailableMembersLink | undefined {
22
+ const apiClient = useApiClient()
23
+ const currentPerson = useCurrentPerson()
24
+
25
+ const { data: members } = useApiGet<MemberResource[]>(
26
+ {
27
+ url: `/me/conversations/${conversation.id}/members`,
28
+ data: { fields: { Member: ['name', 'avatar', 'direct_messages_disabled'] } },
29
+ },
30
+ { enabled }
31
+ )
32
+
33
+ const availableMembers = useMemo(() => {
34
+ if (!enabled || !members) return []
35
+
36
+ const currentMember = members.find(m => m.id === currentPerson.id)
37
+ if (currentMember?.directMessagesDisabled) return []
38
+
39
+ return members.filter(m => m.id !== currentPerson.id && !m.directMessagesDisabled)
40
+ }, [enabled, members, currentPerson.id])
41
+
42
+ const availablePersonIds = useMemo(() => availableMembers.map(m => m.id), [availableMembers])
43
+
44
+ const { data: existingDirectMessageData } = useQuery({
45
+ queryKey: getRequestQueryKey(
46
+ getFindDirectMessageConversationRequestArgs(availablePersonIds.map(String))
47
+ ),
48
+ queryFn: () => findDirectMessageConversation(apiClient, availablePersonIds.map(String)),
49
+ enabled: availablePersonIds.length > 0,
50
+ })
51
+
52
+ return useMemo(
53
+ () =>
54
+ resolveMessageAvailableMembersLink({
55
+ availableMembers,
56
+ existingConversations: existingDirectMessageData?.data,
57
+ canCreateDirectMessages: !!conversation.memberAbility?.canCreateDirectMessages,
58
+ }),
59
+ [
60
+ availableMembers,
61
+ existingDirectMessageData,
62
+ conversation.memberAbility?.canCreateDirectMessages,
63
+ ]
64
+ )
65
+ }
@@ -0,0 +1,34 @@
1
+ import { StackActions, useNavigation } from '@react-navigation/native'
2
+ import { ConversationResource } from '../../types'
3
+ import { useMessageAvailableMembers } from './use_message_available_members'
4
+
5
+ export function useMessageAvailableMembersAction(conversation: ConversationResource) {
6
+ const navigation = useNavigation()
7
+ const isDmDisabled =
8
+ !!conversation.disabled && conversation.disabledReason === 'direct_messages_disabled'
9
+ const messageAvailableMembersLink = useMessageAvailableMembers(conversation, {
10
+ enabled: isDmDisabled,
11
+ })
12
+
13
+ if (!messageAvailableMembersLink) return undefined
14
+
15
+ return {
16
+ label: messageAvailableMembersLink.label,
17
+ onPress: () => {
18
+ if ('existingConversationId' in messageAvailableMembersLink) {
19
+ // replace instead of navigate/push so the disabled conversation isn't
20
+ // left in the back stack as a dead end
21
+ navigation.dispatch(
22
+ StackActions.replace('Conversation', {
23
+ conversation_id: messageAvailableMembersLink.existingConversationId,
24
+ })
25
+ )
26
+ } else {
27
+ navigation.navigate('New', {
28
+ screen: 'ConversationSelectDirectMessageRecipients',
29
+ params: { preselected_people: messageAvailableMembersLink.recipients },
30
+ })
31
+ }
32
+ },
33
+ }
34
+ }
@@ -47,6 +47,7 @@ export const getConversationRequestArgs = ({ conversation_id }: { conversation_i
47
47
  'leader',
48
48
  'can_reply',
49
49
  'can_delete_non_authored_messages',
50
+ 'can_create_direct_messages',
50
51
  ],
51
52
  ConversationMembership: [
52
53
  'last_read_message_sort_key',
@@ -28,6 +28,7 @@ import { KeyboardView } from '../components/display/keyboard_view'
28
28
  import BlankState from '../components/primitive/blank_state_primitive'
29
29
  import { ConversationContextProvider } from '../contexts/conversation_context'
30
30
  import { useTheme } from '../hooks'
31
+ import { useMessageAvailableMembersAction } from '../hooks/direct_messages/use_message_available_members_action'
31
32
  import { useConversation } from '../hooks/use_conversation'
32
33
  import { useConversationJoltEvents } from '../hooks/use_conversation_jolt_events'
33
34
  import { useConversationMessages } from '../hooks/use_conversation_messages'
@@ -249,8 +250,15 @@ function ConversationBottomBar({
249
250
  currentlyEditingMessage,
250
251
  onFlaggedInMessageForm,
251
252
  }: ConversationBottomBarProps) {
253
+ const messageLink = useMessageAvailableMembersAction(conversation)
254
+
252
255
  if (conversation.disabled)
253
- return <ConversationDisabledBanner disabledReason={conversation.disabledReason} />
256
+ return (
257
+ <ConversationDisabledBanner
258
+ disabledReason={conversation.disabledReason}
259
+ messageLink={messageLink}
260
+ />
261
+ )
254
262
  if (!canReply) return <MemberMessagesDisabledBanner />
255
263
  return (
256
264
  <MessageForm.Root
@@ -17,16 +17,30 @@ import { ConversationSelectRecipientsScreenProps } from './types/screen_props'
17
17
 
18
18
  const MAX_RECIPIENTS = 9
19
19
 
20
- export function ConversationSelectDirectMessageRecipientsScreen(
21
- _props: ConversationSelectRecipientsScreenProps
22
- ) {
20
+ export function ConversationSelectDirectMessageRecipientsScreen({
21
+ route,
22
+ }: ConversationSelectRecipientsScreenProps) {
23
+ const { preselected_people = [] } = route.params ?? {}
23
24
  const styles = useStyles()
24
25
  const navigation = useNavigation()
25
26
  const { featureEnabled } = useFeatures()
26
27
  const [searchTerm, setSearchTerm] = useState('')
27
28
  const [debouncedTerm, setDebouncedTerm] = useState('')
28
29
  const [selectedPeople, setSelectedPeople] = useState<Map<string, DirectMessageCandidateResource>>(
29
- new Map()
30
+ () =>
31
+ new Map(
32
+ preselected_people.map(person => [
33
+ person.id,
34
+ {
35
+ ...person,
36
+ type: 'DirectMessageCandidate' as const,
37
+ groups: [],
38
+ teams: [],
39
+ under_18: false,
40
+ directMessagesDisabled: false,
41
+ },
42
+ ])
43
+ )
30
44
  )
31
45
  const searchInputRef = useRef<TextInput>(null)
32
46
 
@@ -1,10 +1,12 @@
1
1
  import { StaticScreenProps } from '@react-navigation/native'
2
2
  import { AppName } from '../../../types/resources/app_name'
3
+ import { PreselectedDirectMessagePerson } from '../../../types/resources/direct_message_candidate'
3
4
  import { GraphId } from '../../../types/resources/group_resource'
4
5
 
5
6
  export type ConversationSelectRecipientsParams = {
6
7
  chat_group_graph_id?: GraphId
7
8
  group_source_app_name?: AppName
9
+ preselected_people?: PreselectedDirectMessagePerson[]
8
10
  }
9
11
 
10
12
  export type ConversationSelectRecipientsScreenProps =
@@ -10,3 +10,8 @@ export interface DirectMessageCandidateResource extends ResourceObject {
10
10
  under_18: boolean
11
11
  directMessagesDisabled: boolean
12
12
  }
13
+
14
+ export type PreselectedDirectMessagePerson = Pick<
15
+ DirectMessageCandidateResource,
16
+ 'id' | 'name' | 'avatar'
17
+ >
@@ -5,6 +5,7 @@ export interface MemberResource {
5
5
  avatar: string
6
6
  badges: MemberBadge[]
7
7
  child: boolean
8
+ directMessagesDisabled?: boolean
8
9
  gender: string | null
9
10
  role?: string
10
11
  }
@@ -5,4 +5,5 @@ export interface MemberAbilityResource {
5
5
  canDeleteNonAuthoredMessages: boolean
6
6
  leader: boolean
7
7
  canReply: boolean
8
+ canCreateDirectMessages: boolean
8
9
  }