@planningcenter/chat-react-native 3.41.0-rc.0 → 3.41.1-qa-799.0

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 (88) hide show
  1. package/build/components/page/page_error_boundary.d.ts.map +1 -1
  2. package/build/components/page/page_error_boundary.js +29 -1
  3. package/build/components/page/page_error_boundary.js.map +1 -1
  4. package/build/contexts/api_provider.d.ts.map +1 -1
  5. package/build/contexts/api_provider.js +12 -2
  6. package/build/contexts/api_provider.js.map +1 -1
  7. package/build/contexts/conversations_context.d.ts +3 -1
  8. package/build/contexts/conversations_context.d.ts.map +1 -1
  9. package/build/contexts/conversations_context.js +2 -2
  10. package/build/contexts/conversations_context.js.map +1 -1
  11. package/build/hooks/services/use_team_members_for_new_conversation.d.ts +14 -14
  12. package/build/hooks/use_api.d.ts +48 -47
  13. package/build/hooks/use_api.d.ts.map +1 -1
  14. package/build/hooks/use_api.js +8 -10
  15. package/build/hooks/use_api.js.map +1 -1
  16. package/build/hooks/use_auto_dismiss_keyboard.d.ts +2 -0
  17. package/build/hooks/use_auto_dismiss_keyboard.d.ts.map +1 -0
  18. package/build/hooks/use_auto_dismiss_keyboard.js +13 -0
  19. package/build/hooks/use_auto_dismiss_keyboard.js.map +1 -0
  20. package/build/hooks/use_chat_permissions.d.ts +14 -14
  21. package/build/hooks/use_conversation_message.d.ts.map +1 -1
  22. package/build/hooks/use_conversation_message.js +1 -4
  23. package/build/hooks/use_conversation_message.js.map +1 -1
  24. package/build/hooks/use_conversations.d.ts +4 -2
  25. package/build/hooks/use_conversations.d.ts.map +1 -1
  26. package/build/hooks/use_conversations.js +2 -2
  27. package/build/hooks/use_conversations.js.map +1 -1
  28. package/build/hooks/use_groups.d.ts +26 -26
  29. package/build/hooks/use_groups_groups.d.ts +26 -26
  30. package/build/hooks/use_my_gender.d.ts.map +1 -1
  31. package/build/hooks/use_my_gender.js +1 -2
  32. package/build/hooks/use_my_gender.js.map +1 -1
  33. package/build/hooks/use_new_conversation_entry.d.ts +6 -1
  34. package/build/hooks/use_new_conversation_entry.d.ts.map +1 -1
  35. package/build/hooks/use_new_conversation_entry.js +19 -5
  36. package/build/hooks/use_new_conversation_entry.js.map +1 -1
  37. package/build/hooks/use_new_conversation_from_filter.js +2 -8
  38. package/build/hooks/use_new_conversation_from_filter.js.map +1 -1
  39. package/build/hooks/use_people_person.d.ts.map +1 -1
  40. package/build/hooks/use_people_person.js +1 -2
  41. package/build/hooks/use_people_person.js.map +1 -1
  42. package/build/hooks/use_teams.d.ts +26 -26
  43. package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.d.ts +14 -14
  44. package/build/screens/conversation_filters/hooks/filters.d.ts +40 -40
  45. package/build/screens/conversation_screen.d.ts.map +1 -1
  46. package/build/screens/conversation_screen.js +2 -0
  47. package/build/screens/conversation_screen.js.map +1 -1
  48. package/build/screens/conversation_select_recipients/conversation_new_entry_screen.d.ts.map +1 -1
  49. package/build/screens/conversation_select_recipients/conversation_new_entry_screen.js +4 -0
  50. package/build/screens/conversation_select_recipients/conversation_new_entry_screen.js.map +1 -1
  51. package/build/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.d.ts.map +1 -1
  52. package/build/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.js +2 -0
  53. package/build/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.js.map +1 -1
  54. package/build/screens/conversation_select_type_screen.d.ts.map +1 -1
  55. package/build/screens/conversation_select_type_screen.js +4 -6
  56. package/build/screens/conversation_select_type_screen.js.map +1 -1
  57. package/build/screens/conversations/components/chat_group_badge.d.ts.map +1 -1
  58. package/build/screens/conversations/components/chat_group_badge.js +1 -10
  59. package/build/screens/conversations/components/chat_group_badge.js.map +1 -1
  60. package/build/screens/conversations/components/list_header_component.d.ts.map +1 -1
  61. package/build/screens/conversations/components/list_header_component.js +6 -0
  62. package/build/screens/conversations/components/list_header_component.js.map +1 -1
  63. package/build/screens/conversations/conversations_screen.js +1 -1
  64. package/build/screens/conversations/conversations_screen.js.map +1 -1
  65. package/build/utils/performance_tracking.js +2 -0
  66. package/build/utils/performance_tracking.js.map +1 -1
  67. package/build/utils/uri.d.ts +1 -1
  68. package/package.json +3 -3
  69. package/src/__tests__/hooks/use_api.test.tsx +11 -0
  70. package/src/components/page/page_error_boundary.tsx +46 -1
  71. package/src/contexts/api_provider.tsx +16 -2
  72. package/src/contexts/conversations_context.tsx +19 -7
  73. package/src/hooks/use_api.ts +18 -27
  74. package/src/hooks/use_auto_dismiss_keyboard.ts +11 -0
  75. package/src/hooks/use_conversation_message.ts +1 -4
  76. package/src/hooks/use_conversations.ts +8 -4
  77. package/src/hooks/use_my_gender.ts +12 -10
  78. package/src/hooks/use_new_conversation_entry.ts +39 -11
  79. package/src/hooks/use_new_conversation_from_filter.ts +12 -12
  80. package/src/hooks/use_people_person.ts +12 -10
  81. package/src/screens/conversation_screen.tsx +3 -0
  82. package/src/screens/conversation_select_recipients/conversation_new_entry_screen.tsx +6 -0
  83. package/src/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.tsx +3 -0
  84. package/src/screens/conversation_select_type_screen.tsx +5 -6
  85. package/src/screens/conversations/components/chat_group_badge.tsx +4 -10
  86. package/src/screens/conversations/components/list_header_component.tsx +7 -0
  87. package/src/screens/conversations/conversations_screen.tsx +1 -1
  88. package/src/utils/performance_tracking.ts +2 -0
@@ -12,6 +12,7 @@ import { useAppState } from '../hooks/use_app_state'
12
12
  import { appGrantsRequestArgs } from '../hooks/use_chat_permissions'
13
13
  import { getRequestQueryKey, RequestQueryKey } from '../hooks/use_suspense_api'
14
14
  import { emitAuthRefresh } from '../utils/auth_events'
15
+ import { ResponseError } from '../utils/response_error'
15
16
  import { ChatContext, ChatContextValue } from './chat_context'
16
17
 
17
18
  let apiClient: ApiClient | undefined
@@ -26,11 +27,21 @@ const defaultQueryFn = ({ queryKey }: { queryKey: QueryKey }) => {
26
27
  return apiClient[app].get({ url, data, headers })
27
28
  }
28
29
 
30
+ const configuredRetry = (failureCount: number, error: any) => {
31
+ if (is429Error(error)) return false
32
+
33
+ return failureCount < 3
34
+ }
35
+
36
+ const is429Error = (error: any): boolean => {
37
+ return error instanceof ResponseError && error.status === 429
38
+ }
39
+
29
40
  export const chatQueryClient = new QueryClient({
30
41
  defaultOptions: {
31
42
  queries: {
32
43
  queryFn: defaultQueryFn,
33
- retry: 3,
44
+ retry: configuredRetry,
34
45
  },
35
46
  },
36
47
  })
@@ -98,7 +109,10 @@ function useSessionChanged(value: Pick<ChatContextValue, 'token' | 'env'>): bool
98
109
  const tokenChanged = Boolean(
99
110
  prevToken?.access_token && newToken?.access_token !== prevToken?.access_token
100
111
  )
101
- return tokenChanged || Boolean(prevEnv && newEnv !== prevEnv)
112
+
113
+ const envChanged = Boolean(prevEnv && newEnv !== prevEnv)
114
+
115
+ return tokenChanged || envChanged
102
116
  }
103
117
 
104
118
  function usePrevious<T>(value: T): T {
@@ -4,9 +4,11 @@ import {
4
4
  InfiniteData,
5
5
  } from '@tanstack/react-query'
6
6
  import React, { createContext, PropsWithChildren, useContext, useMemo, useState } from 'react'
7
+ import { ApiGetQueryOptions } from '../hooks'
7
8
  import { useConversations, UseConversationsValue } from '../hooks/use_conversations'
8
9
  import { ConversationFiltersParams } from '../screens/conversation_filters/screen_props'
9
10
  import { ApiCollection, ConversationResource } from '../types'
11
+ import { ResponseError } from '../utils/response_error'
10
12
 
11
13
  interface ConversationsContextValue extends UseConversationsValue {
12
14
  activeConversationId?: number
@@ -37,15 +39,22 @@ const ConversationsContext = createContext<ConversationsContextValue>({
37
39
  export const ConversationsContextProvider = ({
38
40
  children,
39
41
  args = {},
40
- }: PropsWithChildren<{ args: ConversationFiltersParams }>) => {
42
+ opts: paginatorOptions,
43
+ }: PropsWithChildren<{
44
+ args: ConversationFiltersParams
45
+ opts?: ApiGetQueryOptions
46
+ }>) => {
41
47
  const [activeConversationId, setActiveConversationId] = useState<number | undefined>()
42
48
  const { chat_group_graph_id, group_source_app_name, show_direct_messages } = args
43
49
 
44
- const query = useConversations({
45
- chat_group_graph_id,
46
- group_source_app_name,
47
- show_direct_messages,
48
- })
50
+ const query = useConversations(
51
+ {
52
+ chat_group_graph_id,
53
+ group_source_app_name,
54
+ show_direct_messages,
55
+ },
56
+ paginatorOptions
57
+ )
49
58
 
50
59
  const value = useMemo(
51
60
  () => ({
@@ -63,7 +72,10 @@ export const ConversationsContextProvider = ({
63
72
  function fetchNextPage(
64
73
  _options?: FetchNextPageOptions
65
74
  ): Promise<
66
- InfiniteQueryObserverResult<InfiniteData<ApiCollection<ConversationResource>, unknown>, Response>
75
+ InfiniteQueryObserverResult<
76
+ InfiniteData<ApiCollection<ConversationResource>, unknown>,
77
+ ResponseError
78
+ >
67
79
  > {
68
80
  throw new Error('Function not implemented.')
69
81
  }
@@ -1,66 +1,60 @@
1
- import {
2
- AnyUseSuspenseInfiniteQueryOptions,
3
- InfiniteData,
4
- useInfiniteQuery,
5
- useQuery,
6
- } from '@tanstack/react-query'
1
+ import { InfiniteData, useInfiniteQuery, useQuery } from '@tanstack/react-query'
7
2
  import { useContext } from 'react'
8
3
  import { ChatContext } from '../contexts/chat_context'
9
- import { ApiCollection, ApiResource, FailedResponse, ResourceObject } from '../types'
4
+ import { ApiCollection, ApiResource, ResourceObject } from '../types'
10
5
  import { GetRequest, RequestData } from '../utils/client'
6
+ import { ResponseError, throwResponseError } from '../utils/response_error'
11
7
  import { getNextPageParamFromMeta } from './paginator_meta'
12
8
  import { App, useApiClient } from './use_api_client'
13
9
  import { getRequestQueryKey, RequestQueryKey } from './use_suspense_api'
14
10
 
15
11
  interface ApiGetOptions extends GetRequest {
16
12
  app?: App
17
- enabled?: boolean
18
13
  }
19
14
 
20
- type UseApiGetOptions = {
15
+ export type ApiGetQueryOptions = {
21
16
  staleTime?: number
22
17
  gcTime?: number
18
+ enabled?: boolean
19
+ throwOnError?: boolean | ((error: ResponseError) => boolean)
23
20
  }
24
21
 
25
22
  export const useApiGet = <T extends ResourceObject | ResourceObject[]>(
26
23
  args: ApiGetOptions,
27
- opts?: UseApiGetOptions
24
+ opts?: ApiGetQueryOptions
28
25
  ) => {
29
26
  type Resource = ApiResource<T>
30
27
  const { session } = useContext(ChatContext)
31
28
  const apiClient = useApiClient()
32
29
 
33
- const enabled = !!session.token?.access_token && (args.enabled ?? true)
30
+ const enabled = !!session.token?.access_token && (opts?.enabled ?? true)
34
31
 
35
- const { data, ...query } = useQuery<Resource, FailedResponse>({
32
+ const { data, ...query } = useQuery<Resource, ResponseError>({
36
33
  queryKey: getRequestQueryKey(args),
37
34
  queryFn: ({ queryKey }) => {
38
35
  const [url, d, headers, app = 'chat'] = queryKey as RequestQueryKey
39
36
 
40
- return apiClient[app].get({ url, data: d, headers }) as Promise<Resource>
37
+ return (apiClient[app].get({ url, data: d, headers }) as Promise<Resource>).catch(
38
+ throwResponseError
39
+ )
41
40
  },
42
- enabled,
43
41
  ...opts,
42
+ enabled,
44
43
  })
45
44
 
46
45
  return { ...data, ...query }
47
46
  }
48
47
 
49
- export type PaginatorOptions = Omit<
50
- AnyUseSuspenseInfiniteQueryOptions,
51
- 'getNextPageParam' | 'initialPageParam' | 'queryFn' | 'queryKey'
52
- >
53
-
54
48
  export const useApiPaginator = <T extends ResourceObject>(
55
49
  args: ApiGetOptions,
56
- opts?: PaginatorOptions
50
+ opts?: ApiGetQueryOptions
57
51
  ) => {
58
52
  const { session } = useContext(ChatContext)
59
53
  const apiClient = useApiClient()
60
- const enabled = !!session.token?.access_token && (args.enabled ?? true)
54
+ const enabled = !!session.token?.access_token && (opts?.enabled ?? true)
61
55
  const query = useInfiniteQuery<
62
56
  ApiCollection<T>,
63
- FailedResponse,
57
+ ResponseError,
64
58
  InfiniteData<ApiCollection<T>>,
65
59
  any,
66
60
  Partial<RequestData> | undefined
@@ -74,15 +68,12 @@ export const useApiPaginator = <T extends ResourceObject>(
74
68
  const offset = pageParam?.offset || args.data.offset
75
69
  const data = { ...args.data, where, offset }
76
70
 
77
- return apiClient[app].get<ApiCollection<T>>({
78
- url: args.url,
79
- data,
80
- })
71
+ return apiClient[app].get<ApiCollection<T>>({ url: args.url, data }).catch(throwResponseError)
81
72
  },
82
73
  initialPageParam: {} as Partial<RequestData>,
83
74
  getNextPageParam: lastPage => getNextPageParamFromMeta(lastPage.meta?.next),
84
- enabled,
85
75
  ...(opts || {}),
76
+ enabled,
86
77
  })
87
78
 
88
79
  const data: T[] = query.data?.pages?.flatMap(page => page.data) || []
@@ -0,0 +1,11 @@
1
+ import { useIsFocused } from '@react-navigation/native'
2
+ import { useEffect } from 'react'
3
+ import { Keyboard, Platform } from 'react-native'
4
+
5
+ export function useAutoDismissKeyboard() {
6
+ const isFocused = useIsFocused()
7
+ useEffect(() => {
8
+ if (Platform.OS !== 'android') return
9
+ if (!isFocused) Keyboard.dismiss()
10
+ }, [isFocused])
11
+ }
@@ -15,10 +15,7 @@ export const useConversationMessage = ({
15
15
  data: message,
16
16
  isError,
17
17
  isLoading,
18
- } = useApiGet<MessageResource>({
19
- ...getMessageRequestArgs({ conversation_id, messageId }),
20
- enabled,
21
- })
18
+ } = useApiGet<MessageResource>(getMessageRequestArgs({ conversation_id, messageId }), { enabled })
22
19
  const queryKey = getMessageQueryKey({ conversation_id, messageId })
23
20
 
24
21
  return { message, isError, isLoading, queryKey }
@@ -2,11 +2,12 @@ import { InfiniteData, UseInfiniteQueryResult } from '@tanstack/react-query'
2
2
  import { useMemo } from 'react'
3
3
  import { ApiCollection, ConversationResource } from '../types'
4
4
  import { ConversationRequestArgs, getConversationsRequestArgs } from '../utils/request/conversation'
5
- import { useApiPaginator } from './use_api'
5
+ import { ResponseError } from '../utils/response_error'
6
+ import { ApiGetQueryOptions, useApiPaginator } from './use_api'
6
7
 
7
8
  export type ConversationPaginationResult = UseInfiniteQueryResult<
8
9
  InfiniteData<ApiCollection<ConversationResource>, unknown>,
9
- Response
10
+ ResponseError
10
11
  >
11
12
 
12
13
  export type UseConversationsValue = Pick<
@@ -24,9 +25,12 @@ export type UseConversationsValue = Pick<
24
25
  refetch: () => Promise<any>
25
26
  }
26
27
 
27
- export function useConversations(args?: Partial<ConversationRequestArgs>): UseConversationsValue {
28
+ export function useConversations(
29
+ args?: Partial<ConversationRequestArgs>,
30
+ opts?: ApiGetQueryOptions
31
+ ): UseConversationsValue {
28
32
  const requestArgs = getConversationsRequestArgs(args)
29
- const { data, ...rest } = useApiPaginator<ConversationResource>(requestArgs)
33
+ const { data, ...rest } = useApiPaginator<ConversationResource>(requestArgs, opts)
30
34
 
31
35
  const conversations = useMemo(() => data.sort(sortByLastMessage), [data])
32
36
 
@@ -16,18 +16,20 @@ export function useMyGender() {
16
16
  const { featureEnabled } = useFeatures()
17
17
  const isFeatureEnabled = featureEnabled(availableFeatures.gender_specific_conversations)
18
18
 
19
- const { data, isFetching } = useApiGet<PersonWithGender>({
20
- url: '/me',
21
- enabled: isFeatureEnabled,
22
- data: {
23
- include: ['gender'],
24
- fields: {
25
- Person: ['gender'],
26
- Gender: ['value'],
19
+ const { data, isFetching } = useApiGet<PersonWithGender>(
20
+ {
21
+ url: '/me',
22
+ data: {
23
+ include: ['gender'],
24
+ fields: {
25
+ Person: ['gender'],
26
+ Gender: ['value'],
27
+ },
27
28
  },
29
+ app: 'people',
28
30
  },
29
- app: 'people',
30
- })
31
+ { enabled: isFeatureEnabled }
32
+ )
31
33
 
32
34
  const gender = data?.gender
33
35
  const genderValue = gender?.value ?? null
@@ -1,20 +1,44 @@
1
1
  import { useMemo } from 'react'
2
2
  import { useAppGrants } from './use_chat_permissions'
3
3
 
4
- export type NewConversationEntryMode = 'select_type' | 'groups' | 'teams' | 'none'
4
+ export type NewConversationEntryMode =
5
+ | 'select_type'
6
+ | 'groups'
7
+ | 'teams'
8
+ | 'direct_message'
9
+ | 'none'
5
10
 
6
- export function useNewConversationEntry(): NewConversationEntryMode {
11
+ export function useConversationTypeAccess() {
7
12
  const appGrants = useAppGrants()
8
13
 
14
+ return useMemo(
15
+ () => ({
16
+ canCreateGroupsConversations: appGrants.data?.some(
17
+ g => g.createConversations && g.appName === 'Groups'
18
+ ),
19
+ canCreateServicesConversations: appGrants.data?.some(
20
+ g => g.createConversations && g.appName === 'Services'
21
+ ),
22
+ canCreateDirectMessages: appGrants.data?.some(
23
+ g => g.createConversations && g.appName === 'Chat'
24
+ ),
25
+ }),
26
+ [appGrants.data]
27
+ )
28
+ }
29
+
30
+ export function useNewConversationEntry(): NewConversationEntryMode {
31
+ const { canCreateGroupsConversations, canCreateServicesConversations, canCreateDirectMessages } =
32
+ useConversationTypeAccess()
33
+
9
34
  return useMemo(() => {
10
- const canCreateGroupsConversations = appGrants.data?.some(
11
- g => g.createConversations && g.appName === 'Groups'
12
- )
13
- const canCreateServicesConversations = appGrants.data?.some(
14
- g => g.createConversations && g.appName === 'Services'
15
- )
16
-
17
- if (canCreateGroupsConversations && canCreateServicesConversations) {
35
+ const availableCount = [
36
+ canCreateGroupsConversations,
37
+ canCreateServicesConversations,
38
+ canCreateDirectMessages,
39
+ ].filter(Boolean).length
40
+
41
+ if (availableCount > 1) {
18
42
  return 'select_type'
19
43
  }
20
44
 
@@ -26,6 +50,10 @@ export function useNewConversationEntry(): NewConversationEntryMode {
26
50
  return 'teams'
27
51
  }
28
52
 
53
+ if (canCreateDirectMessages) {
54
+ return 'direct_message'
55
+ }
56
+
29
57
  return 'none'
30
- }, [appGrants.data])
58
+ }, [canCreateGroupsConversations, canCreateServicesConversations, canCreateDirectMessages])
31
59
  }
@@ -25,19 +25,19 @@ export function useNewConversationFromFilter(
25
25
  const isGroupsGroup = sourceAppName === 'Groups' && sourceType === 'Group'
26
26
  const isServicesTeam = sourceAppName === 'Services' && sourceType === 'Team'
27
27
 
28
- const { data: group } = useApiGet<GroupResource>({
29
- url: `/me/groups/${chatGroupGraphId}`,
30
- data: { fields: { Group: ['name'] } },
31
- enabled: !!chatGroupGraphId,
32
- app: 'chat',
33
- })
28
+ const { data: group } = useApiGet<GroupResource>(
29
+ { url: `/me/groups/${chatGroupGraphId}`, data: { fields: { Group: ['name'] } }, app: 'chat' },
30
+ { enabled: !!chatGroupGraphId }
31
+ )
34
32
 
35
- const { data: groupsGroup } = useApiGet<GroupsGroupResource>({
36
- url: `/me/groups/${sourceId}`,
37
- data: { fields: { Group: ['can_create_conversation'] } },
38
- enabled: isGroupsGroup && !!sourceId,
39
- app: 'groups',
40
- })
33
+ const { data: groupsGroup } = useApiGet<GroupsGroupResource>(
34
+ {
35
+ url: `/me/groups/${sourceId}`,
36
+ data: { fields: { Group: ['can_create_conversation'] } },
37
+ app: 'groups',
38
+ },
39
+ { enabled: isGroupsGroup && !!sourceId }
40
+ )
41
41
 
42
42
  return useMemo(() => {
43
43
  if (isGroupsGroup && group?.name && sourceId) {
@@ -16,18 +16,20 @@ interface PeoplePerson extends ResourceObject {
16
16
  }
17
17
 
18
18
  export const usePeoplePerson = (person?: { id: number }) => {
19
- const { data: peoplePerson } = useApiGet<PeoplePerson>({
20
- url: `/people/${person?.id}`,
21
- enabled: Boolean(person?.id),
22
- data: {
23
- include: ['emails'],
24
- fields: {
25
- Person: ['id', 'name', 'avatar', 'emails'],
26
- Email: ['address', 'primary'],
19
+ const { data: peoplePerson } = useApiGet<PeoplePerson>(
20
+ {
21
+ url: `/people/${person?.id}`,
22
+ data: {
23
+ include: ['emails'],
24
+ fields: {
25
+ Person: ['id', 'name', 'avatar', 'emails'],
26
+ Email: ['address', 'primary'],
27
+ },
27
28
  },
29
+ app: 'people',
28
30
  },
29
- app: 'people',
30
- })
31
+ { enabled: Boolean(person?.id) }
32
+ )
31
33
 
32
34
  return peoplePerson
33
35
  }
@@ -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 { useAutoDismissKeyboard } from '../hooks/use_auto_dismiss_keyboard'
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'
@@ -130,6 +131,8 @@ function ConversationScreenContent({ route }: ConversationScreenProps) {
130
131
  })
131
132
  }, [])
132
133
 
134
+ useAutoDismissKeyboard()
135
+
133
136
  useEffect(() => {
134
137
  if (reply_root_id) {
135
138
  navigation.setParams({
@@ -16,6 +16,7 @@ type NewConversationStackParamList = {
16
16
  ConversationNewEntry: ConversationSelectRecipientsParams | undefined
17
17
  ConversationSelectGroupRecipients: ConversationSelectRecipientsParams | undefined
18
18
  ConversationSelectTeamsILeadRecipients: ConversationSelectRecipientsParams | undefined
19
+ ConversationSelectDirectMessageRecipients: ConversationSelectRecipientsParams | undefined
19
20
  }
20
21
 
21
22
  export const ConversationNewEntryScreen = ({ route }: ConversationSelectRecipientsScreenProps) => {
@@ -34,6 +35,11 @@ export const ConversationNewEntryScreen = ({ route }: ConversationSelectRecipien
34
35
  return
35
36
  }
36
37
 
38
+ if (entryMode === 'direct_message') {
39
+ navigation.replace('ConversationSelectDirectMessageRecipients', route.params)
40
+ return
41
+ }
42
+
37
43
  if (entryMode === 'none') {
38
44
  navigation.goBack()
39
45
  return
@@ -10,6 +10,7 @@ import { useDirectMessageAffiliations } from '../../hooks/direct_messages/use_di
10
10
  import { useDirectMessageCandidates } from '../../hooks/direct_messages/use_direct_message_candidates'
11
11
  import { useDirectMessageConversationValidate } from '../../hooks/direct_messages/use_direct_message_conversation_validate'
12
12
  import { useFindOrCreateDirectMessageConversation } from '../../hooks/direct_messages/use_find_or_create_direct_message_conversation'
13
+ import { useAutoDismissKeyboard } from '../../hooks/use_auto_dismiss_keyboard'
13
14
  import { DirectMessageCandidateResource } from '../../types/resources/direct_message_candidate'
14
15
  import { ConversationSelectRecipientsScreenProps } from './types/screen_props'
15
16
 
@@ -26,6 +27,8 @@ export function ConversationSelectDirectMessageRecipientsScreen(
26
27
  new Map()
27
28
  )
28
29
 
30
+ useAutoDismissKeyboard()
31
+
29
32
  useEffect(() => {
30
33
  const timer = setTimeout(() => setDebouncedTerm(searchTerm), 200)
31
34
  return () => clearTimeout(timer)
@@ -3,7 +3,7 @@ import React from 'react'
3
3
  import FormSheet, { getFormSheetScreenOptions } from '../components/primitive/form_sheet'
4
4
  import { useCurrentPerson } from '../hooks/use_current_person'
5
5
  import { availableFeatures, useFeatures } from '../hooks/use_features'
6
- import { useNewConversationEntry } from '../hooks/use_new_conversation_entry'
6
+ import { useConversationTypeAccess } from '../hooks/use_new_conversation_entry'
7
7
  import type { NewConversationFromFilter } from '../hooks/use_new_conversation_from_filter'
8
8
  import { AppName } from '../types/resources/app_name'
9
9
  import { GraphId } from '../types/resources/group_resource'
@@ -26,15 +26,14 @@ type NewConversationTypeScreen =
26
26
 
27
27
  export function ConversationSelectTypeScreen({ route }: ConversationSelectTypeScreenProps) {
28
28
  const navigation = useNavigation()
29
- const entryMode = useNewConversationEntry()
29
+ const { canCreateGroupsConversations, canCreateServicesConversations } =
30
+ useConversationTypeAccess()
30
31
  const { featureEnabled } = useFeatures()
31
32
  const { directMessagesDisabled, under_18 } = useCurrentPerson()
32
33
  const { newConversationFromFilter, chat_group_graph_id, group_source_app_name } =
33
34
  route.params || {}
34
35
 
35
36
  const filterParams = { chat_group_graph_id, group_source_app_name }
36
- const canCreateGroups = entryMode === 'select_type' || entryMode === 'groups'
37
- const canCreateTeams = entryMode === 'select_type' || entryMode === 'teams'
38
37
  const dmFeatureEnabled = featureEnabled(availableFeatures.direct_messages_eap)
39
38
 
40
39
  const handleFilterPress = () => {
@@ -89,14 +88,14 @@ export function ConversationSelectTypeScreen({ route }: ConversationSelectTypeSc
89
88
  accessibilityHint="Creates a conversation pre-scoped to the current filter"
90
89
  />
91
90
  )}
92
- {canCreateGroups && (
91
+ {canCreateGroupsConversations && (
93
92
  <FormSheet.Action
94
93
  title="Group"
95
94
  onPress={handleGroupPress}
96
95
  accessibilityHint="Opens group selection screen"
97
96
  />
98
97
  )}
99
- {canCreateTeams && (
98
+ {canCreateServicesConversations && (
100
99
  <FormSheet.Action
101
100
  title="Team"
102
101
  onPress={handleTeamPress}
@@ -11,16 +11,10 @@ export const ChatGroupBadge = ({ rowPaddingHorizontal }: { rowPaddingHorizontal:
11
11
  const navigation = useNavigation()
12
12
  const route = useRoute<RouteProp<ConversationsScreenProps['route']>>()
13
13
  const { chat_group_graph_id } = route.params || {}
14
- const { data: group } = useApiGet<GroupResource>({
15
- url: `/me/groups/${chat_group_graph_id}`,
16
- data: {
17
- fields: {
18
- Group: [],
19
- },
20
- },
21
- enabled: !!chat_group_graph_id,
22
- app: 'chat',
23
- })
14
+ const { data: group } = useApiGet<GroupResource>(
15
+ { url: `/me/groups/${chat_group_graph_id}`, data: { fields: { Group: [] } }, app: 'chat' },
16
+ { enabled: !!chat_group_graph_id }
17
+ )
24
18
 
25
19
  const handleBadgePress = useCallback(() => {
26
20
  navigation.setParams({
@@ -87,6 +87,13 @@ export const ListHeaderComponent = () => {
87
87
  params: { ...route.params },
88
88
  })
89
89
  }
90
+
91
+ if (entryMode === 'direct_message') {
92
+ return navigation.navigate('New', {
93
+ screen: 'ConversationSelectDirectMessageRecipients',
94
+ params: { ...route.params },
95
+ })
96
+ }
90
97
  }, [navigation, route.params, entryMode, newConversationFromFilter])
91
98
 
92
99
  const handleChatSettings = useCallback(() => {
@@ -22,7 +22,7 @@ export function ConversationsScreen({ route }: ConversationsScreenProps) {
22
22
 
23
23
  return (
24
24
  <View style={styles.container}>
25
- <ConversationsContextProvider args={route.params}>
25
+ <ConversationsContextProvider args={route.params} opts={{ throwOnError: true }}>
26
26
  <Conversations ListHeaderComponent={ListHeaderComponent} />
27
27
  </ConversationsContextProvider>
28
28
  </View>
@@ -89,6 +89,8 @@ async function sendPerformanceMetric(
89
89
  apiClient: ApiClient,
90
90
  metric: PerformanceMetric
91
91
  ): Promise<void> {
92
+ if (__DEV__) return
93
+
92
94
  try {
93
95
  await apiClient.chat.post({
94
96
  url: `/me/metrics`,