@planningcenter/chat-react-native 3.15.0-rc.0 → 3.15.0-rc.10

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 (204) hide show
  1. package/build/components/conversations/conversation_actions.js +1 -1
  2. package/build/components/conversations/conversation_actions.js.map +1 -1
  3. package/build/components/conversations/conversation_preview.d.ts.map +1 -1
  4. package/build/components/conversations/conversation_preview.js +9 -5
  5. package/build/components/conversations/conversation_preview.js.map +1 -1
  6. package/build/components/conversations/mute_indicator.d.ts.map +1 -1
  7. package/build/components/conversations/mute_indicator.js +3 -1
  8. package/build/components/conversations/mute_indicator.js.map +1 -1
  9. package/build/components/conversations/swipeable_toggle_button.d.ts.map +1 -1
  10. package/build/components/conversations/swipeable_toggle_button.js +6 -3
  11. package/build/components/conversations/swipeable_toggle_button.js.map +1 -1
  12. package/build/components/conversations/unread_count_badge.js +9 -6
  13. package/build/components/conversations/unread_count_badge.js.map +1 -1
  14. package/build/components/display/action_button.d.ts.map +1 -1
  15. package/build/components/display/action_button.js +2 -4
  16. package/build/components/display/action_button.js.map +1 -1
  17. package/build/components/display/avatar.d.ts +3 -1
  18. package/build/components/display/avatar.d.ts.map +1 -1
  19. package/build/components/display/avatar.js +2 -2
  20. package/build/components/display/avatar.js.map +1 -1
  21. package/build/components/display/avatar_group.d.ts +3 -1
  22. package/build/components/display/avatar_group.d.ts.map +1 -1
  23. package/build/components/display/avatar_group.js +2 -2
  24. package/build/components/display/avatar_group.js.map +1 -1
  25. package/build/components/display/badge.d.ts +5 -1
  26. package/build/components/display/badge.d.ts.map +1 -1
  27. package/build/components/display/badge.js +2 -2
  28. package/build/components/display/badge.js.map +1 -1
  29. package/build/components/display/icon.d.ts +26 -13
  30. package/build/components/display/icon.d.ts.map +1 -1
  31. package/build/components/display/icon.js +0 -12
  32. package/build/components/display/icon.js.map +1 -1
  33. package/build/components/display/index.d.ts +1 -0
  34. package/build/components/display/index.d.ts.map +1 -1
  35. package/build/components/display/index.js +1 -0
  36. package/build/components/display/index.js.map +1 -1
  37. package/build/components/display/pressable_row.d.ts +14 -0
  38. package/build/components/display/pressable_row.d.ts.map +1 -0
  39. package/build/components/display/pressable_row.js +65 -0
  40. package/build/components/display/pressable_row.js.map +1 -0
  41. package/build/components/display/toggle_button.d.ts +3 -1
  42. package/build/components/display/toggle_button.d.ts.map +1 -1
  43. package/build/components/display/toggle_button.js +1 -1
  44. package/build/components/display/toggle_button.js.map +1 -1
  45. package/build/components/primitive/avatar_primitive.d.ts +2 -0
  46. package/build/components/primitive/avatar_primitive.d.ts.map +1 -1
  47. package/build/components/primitive/avatar_primitive.js +20 -19
  48. package/build/components/primitive/avatar_primitive.js.map +1 -1
  49. package/build/components/primitive/form_sheet.d.ts +3 -2
  50. package/build/components/primitive/form_sheet.d.ts.map +1 -1
  51. package/build/components/primitive/form_sheet.js +5 -3
  52. package/build/components/primitive/form_sheet.js.map +1 -1
  53. package/build/hooks/index.d.ts +1 -0
  54. package/build/hooks/index.d.ts.map +1 -1
  55. package/build/hooks/index.js +1 -0
  56. package/build/hooks/index.js.map +1 -1
  57. package/build/hooks/use_api.d.ts +1 -1
  58. package/build/hooks/use_api.d.ts.map +1 -1
  59. package/build/hooks/use_api.js.map +1 -1
  60. package/build/hooks/use_api_client.d.ts +1 -1
  61. package/build/hooks/use_api_client.d.ts.map +1 -1
  62. package/build/hooks/use_api_client.js +1 -1
  63. package/build/hooks/use_api_client.js.map +1 -1
  64. package/build/hooks/use_app_name.d.ts +3 -0
  65. package/build/hooks/use_app_name.d.ts.map +1 -0
  66. package/build/hooks/use_app_name.js +12 -0
  67. package/build/hooks/use_app_name.js.map +1 -0
  68. package/build/hooks/use_async_storage.d.ts +1 -1
  69. package/build/hooks/use_async_storage.d.ts.map +1 -1
  70. package/build/hooks/use_async_storage.js +6 -5
  71. package/build/hooks/use_async_storage.js.map +1 -1
  72. package/build/hooks/use_report_bug_action.d.ts +1 -1
  73. package/build/hooks/use_report_bug_action.d.ts.map +1 -1
  74. package/build/hooks/use_report_bug_action.js +1 -9
  75. package/build/hooks/use_report_bug_action.js.map +1 -1
  76. package/build/hooks/use_scalable_number_of_lines.d.ts +2 -0
  77. package/build/hooks/use_scalable_number_of_lines.d.ts.map +1 -0
  78. package/build/hooks/use_scalable_number_of_lines.js +9 -0
  79. package/build/hooks/use_scalable_number_of_lines.js.map +1 -0
  80. package/build/hooks/use_suspense_api.d.ts +1 -1
  81. package/build/hooks/use_suspense_api.d.ts.map +1 -1
  82. package/build/hooks/use_suspense_api.js.map +1 -1
  83. package/build/index.d.ts +2 -0
  84. package/build/index.d.ts.map +1 -1
  85. package/build/index.js +2 -0
  86. package/build/index.js.map +1 -1
  87. package/build/navigation/index.d.ts +20 -5
  88. package/build/navigation/index.d.ts.map +1 -1
  89. package/build/navigation/index.js +23 -15
  90. package/build/navigation/index.js.map +1 -1
  91. package/build/polyfills/events/CustomEvent.d.ts +21 -0
  92. package/build/polyfills/events/CustomEvent.d.ts.map +1 -0
  93. package/build/polyfills/events/CustomEvent.js +22 -0
  94. package/build/polyfills/events/CustomEvent.js.map +1 -0
  95. package/build/polyfills/events/Event.d.ts +49 -0
  96. package/build/polyfills/events/Event.d.ts.map +1 -0
  97. package/build/polyfills/events/Event.js +125 -0
  98. package/build/polyfills/events/Event.js.map +1 -0
  99. package/build/polyfills/events/EventHandlerAttributes.d.ts +8 -0
  100. package/build/polyfills/events/EventHandlerAttributes.d.ts.map +1 -0
  101. package/build/polyfills/events/EventHandlerAttributes.js +46 -0
  102. package/build/polyfills/events/EventHandlerAttributes.js.map +1 -0
  103. package/build/polyfills/events/EventTarget.d.ts +33 -0
  104. package/build/polyfills/events/EventTarget.d.ts.map +1 -0
  105. package/build/polyfills/events/EventTarget.js +238 -0
  106. package/build/polyfills/events/EventTarget.js.map +1 -0
  107. package/build/polyfills/events/internals/EventInternals.d.ts +30 -0
  108. package/build/polyfills/events/internals/EventInternals.d.ts.map +1 -0
  109. package/build/polyfills/events/internals/EventInternals.js +76 -0
  110. package/build/polyfills/events/internals/EventInternals.js.map +1 -0
  111. package/build/polyfills/events/internals/EventTargetInternals.d.ts +9 -0
  112. package/build/polyfills/events/internals/EventTargetInternals.d.ts.map +1 -0
  113. package/build/polyfills/events/internals/EventTargetInternals.js +11 -0
  114. package/build/polyfills/events/internals/EventTargetInternals.js.map +1 -0
  115. package/build/polyfills/webidl/PlatformObjects.d.ts +31 -0
  116. package/build/polyfills/webidl/PlatformObjects.d.ts.map +1 -0
  117. package/build/polyfills/webidl/PlatformObjects.js +39 -0
  118. package/build/polyfills/webidl/PlatformObjects.js.map +1 -0
  119. package/build/screens/bug_report_screen.d.ts.map +1 -1
  120. package/build/screens/bug_report_screen.js +62 -57
  121. package/build/screens/bug_report_screen.js.map +1 -1
  122. package/build/screens/conversation_filters/components/conversation_filters.js +9 -7
  123. package/build/screens/conversation_filters/components/conversation_filters.js.map +1 -1
  124. package/build/screens/conversation_filters/components/rows.d.ts.map +1 -1
  125. package/build/screens/conversation_filters/components/rows.js +50 -31
  126. package/build/screens/conversation_filters/components/rows.js.map +1 -1
  127. package/build/screens/conversations/components/list_header_component.d.ts.map +1 -1
  128. package/build/screens/conversations/components/list_header_component.js +2 -2
  129. package/build/screens/conversations/components/list_header_component.js.map +1 -1
  130. package/build/screens/conversations/conversations_screen.d.ts.map +1 -1
  131. package/build/screens/conversations/conversations_screen.js +6 -6
  132. package/build/screens/conversations/conversations_screen.js.map +1 -1
  133. package/build/screens/design_system_screen.js +1 -1
  134. package/build/screens/design_system_screen.js.map +1 -1
  135. package/build/screens/get_help_screen.d.ts +5 -0
  136. package/build/screens/get_help_screen.d.ts.map +1 -0
  137. package/build/screens/get_help_screen.js +94 -0
  138. package/build/screens/get_help_screen.js.map +1 -0
  139. package/build/screens/message_actions_screen.d.ts +1 -1
  140. package/build/screens/message_actions_screen.d.ts.map +1 -1
  141. package/build/screens/message_actions_screen.js +14 -11
  142. package/build/screens/message_actions_screen.js.map +1 -1
  143. package/build/utils/client/index.d.ts +1 -0
  144. package/build/utils/client/index.d.ts.map +1 -1
  145. package/build/utils/client/index.js +1 -0
  146. package/build/utils/client/index.js.map +1 -1
  147. package/build/utils/client/types.d.ts +61 -0
  148. package/build/utils/client/types.d.ts.map +1 -0
  149. package/build/utils/client/types.js +2 -0
  150. package/build/utils/client/types.js.map +1 -0
  151. package/build/utils/styles.d.ts +1 -1
  152. package/build/utils/styles.js +1 -1
  153. package/build/utils/styles.js.map +1 -1
  154. package/build/utils/theme.d.ts +1 -0
  155. package/build/utils/theme.d.ts.map +1 -1
  156. package/build/utils/theme.js +2 -0
  157. package/build/utils/theme.js.map +1 -1
  158. package/package.json +5 -5
  159. package/src/__tests__/event-polyfill.test.ts +314 -0
  160. package/src/components/conversations/conversation_actions.tsx +1 -1
  161. package/src/components/conversations/conversation_preview.tsx +15 -4
  162. package/src/components/conversations/mute_indicator.tsx +9 -1
  163. package/src/components/conversations/swipeable_toggle_button.tsx +18 -3
  164. package/src/components/conversations/unread_count_badge.tsx +9 -6
  165. package/src/components/display/action_button.tsx +3 -4
  166. package/src/components/display/avatar.tsx +5 -1
  167. package/src/components/display/avatar_group.tsx +5 -1
  168. package/src/components/display/badge.tsx +6 -1
  169. package/src/components/display/icon.tsx +17 -14
  170. package/src/components/display/index.ts +1 -0
  171. package/src/components/display/pressable_row.tsx +103 -0
  172. package/src/components/display/toggle_button.tsx +12 -1
  173. package/src/components/primitive/avatar_primitive.tsx +35 -19
  174. package/src/components/primitive/form_sheet.tsx +33 -5
  175. package/src/hooks/index.ts +1 -0
  176. package/src/hooks/use_api.ts +1 -1
  177. package/src/hooks/use_api_client.ts +2 -2
  178. package/src/hooks/use_app_name.ts +17 -0
  179. package/src/hooks/use_async_storage.ts +8 -5
  180. package/src/hooks/use_report_bug_action.ts +2 -10
  181. package/src/hooks/use_scalable_number_of_lines.ts +10 -0
  182. package/src/hooks/use_suspense_api.ts +1 -1
  183. package/src/index.tsx +2 -0
  184. package/src/navigation/index.tsx +38 -25
  185. package/src/polyfills/events/CustomEvent.ts +32 -0
  186. package/src/polyfills/events/Event.ts +186 -0
  187. package/src/polyfills/events/EventHandlerAttributes.ts +67 -0
  188. package/src/polyfills/events/EventTarget.ts +360 -0
  189. package/src/polyfills/events/README.md +1 -0
  190. package/src/polyfills/events/internals/EventInternals.ts +95 -0
  191. package/src/polyfills/events/internals/EventTargetInternals.ts +16 -0
  192. package/src/polyfills/webidl/PlatformObjects.ts +50 -0
  193. package/src/screens/bug_report_screen.tsx +79 -67
  194. package/src/screens/conversation_filters/components/conversation_filters.tsx +10 -7
  195. package/src/screens/conversation_filters/components/rows.tsx +63 -50
  196. package/src/screens/conversations/components/list_header_component.tsx +3 -1
  197. package/src/screens/conversations/conversations_screen.tsx +8 -6
  198. package/src/screens/design_system_screen.tsx +1 -1
  199. package/src/screens/get_help_screen.tsx +131 -0
  200. package/src/screens/message_actions_screen.tsx +34 -12
  201. package/src/utils/client/index.ts +1 -0
  202. package/src/utils/styles.ts +1 -1
  203. package/src/utils/theme.ts +3 -0
  204. /package/src/utils/client/{types.d.ts → types.ts} +0 -0
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Internal implementation details for the `EventTarget` module.
3
+ */
4
+
5
+ import type Event from '../Event'
6
+ import type EventTarget from '../EventTarget'
7
+ import { setIsTrusted } from './EventInternals'
8
+
9
+ export const EVENT_TARGET_GET_THE_PARENT_KEY: unique symbol = Symbol('EventTarget[get the parent]')
10
+
11
+ export const INTERNAL_DISPATCH_METHOD_KEY: unique symbol = Symbol('EventTarget[dispatch]')
12
+
13
+ export function dispatchTrustedEvent(eventTarget: EventTarget, event: Event): void {
14
+ setIsTrusted(event, true)
15
+ return eventTarget[INTERNAL_DISPATCH_METHOD_KEY](event)
16
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ const IS_PLATFORM_OBJECT_KEY = Symbol('isPlatformObject')
9
+ const CLONE_PLATFORM_OBJECT_KEY = Symbol('clonePlatformObject')
10
+
11
+ type CloneFn<T extends object> = (value: T) => T
12
+
13
+ /**
14
+ * Marks the given object or instances of the given class as platform objects.
15
+ *
16
+ * Optionally, it sets the clone function for that platform object, which is a
17
+ * simplification of the serializable attribute of the Web interface.
18
+ */
19
+ export function setPlatformObject<T extends object>(
20
+ obj: { prototype: unknown },
21
+ options?: { clone: CloneFn<T> }
22
+ ): void
23
+ export function setPlatformObject<T extends object>(obj: T, options?: { clone: CloneFn<T> }): void
24
+ export function setPlatformObject(obj: any, options?: { clone: (value: any) => any }): void {
25
+ if (typeof obj === 'function') {
26
+ ;(obj.prototype as any)[IS_PLATFORM_OBJECT_KEY] = true
27
+ if (options) {
28
+ ;(obj.prototype as any)[CLONE_PLATFORM_OBJECT_KEY] = options.clone
29
+ }
30
+ } else {
31
+ ;(obj as any)[IS_PLATFORM_OBJECT_KEY] = true
32
+ if (options) {
33
+ ;(obj as any)[CLONE_PLATFORM_OBJECT_KEY] = options.clone
34
+ }
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Indicates if the given object is a platform object.
40
+ */
41
+ export function isPlatformObject<T extends object>(obj: T): boolean {
42
+ return IS_PLATFORM_OBJECT_KEY in (obj as any)
43
+ }
44
+
45
+ /**
46
+ * Returns the clone function for the given platform object, if it was set.
47
+ */
48
+ export function getPlatformObjectClone<T extends object>(obj: T): ((value: T) => T) | undefined {
49
+ return (obj as any)[CLONE_PLATFORM_OBJECT_KEY]
50
+ }
@@ -1,5 +1,13 @@
1
1
  import React, { useCallback, useLayoutEffect, useState } from 'react'
2
- import { View, StyleSheet, TextInput, Linking, ScrollView, TouchableOpacity } from 'react-native'
2
+ import {
3
+ View,
4
+ StyleSheet,
5
+ TextInput,
6
+ Linking,
7
+ ScrollView,
8
+ TouchableOpacity,
9
+ Platform,
10
+ } from 'react-native'
3
11
  import type {
4
12
  NativeStackNavigationOptions,
5
13
  NativeStackScreenProps,
@@ -27,19 +35,23 @@ import { DefaultLoading } from '../components/page/loading'
27
35
  import { startsWith } from 'lodash'
28
36
  import { VideoAttachmentPreview } from '../components/display/video_attachment_preview'
29
37
  import { SafeAreaModal } from '../components/safe_area_modal'
38
+ import { useSafeAreaInsets } from 'react-native-safe-area-context'
39
+ import { useAppName } from '../hooks/use_app_name'
30
40
 
31
41
  const MAX_DESCRIPTION_LENGTH = 2000
32
42
 
33
- const BUG_TYPE_OPTIONS = [
34
- 'Issues sending or receiving messages',
35
- 'Trouble starting a new conversation',
36
- "Notifications aren't working",
37
- 'Incorrect read receipts or unread counts',
38
- 'Problems with file attachments',
39
- "Something isn't displaying properly",
40
- 'App is slow or crashes',
41
- 'Other',
42
- ]
43
+ enum BUG_TYPE_OPTIONS {
44
+ Chat = 'Chat',
45
+ FindMyChurch = 'Find my church',
46
+ LoggingIn = 'Logging in',
47
+ UsingGroups = 'Using groups (events, resources, members)',
48
+ CheckingIn = 'Checking in',
49
+ MakingADonation = 'Making a donation',
50
+ RegisteringForAnEvent = 'Registering for an event',
51
+ MyProfileAndSchedule = 'My profile and schedule',
52
+ Directory = 'Directory',
53
+ Other = 'Other',
54
+ }
43
55
 
44
56
  export const BugReportScreenOptions = ({
45
57
  navigation,
@@ -55,57 +67,49 @@ interface Attachment {
55
67
  type: string
56
68
  }
57
69
 
70
+ enum QualifiedMobileAppName {
71
+ chat = 'Chat', // There is no app name for the mobile app
72
+ churchcenter = 'Church Center App',
73
+ services = 'Services Mobile',
74
+ }
75
+
58
76
  export function BugReportScreen() {
59
77
  const styles = useStyles()
60
78
  const navigation = useNavigation()
61
- const [bugType, setBugType] = useState('')
79
+ const name = useAppName()
80
+ const appName = QualifiedMobileAppName[name]
81
+ const [bugType, setBugType] = useState<BUG_TYPE_OPTIONS>(BUG_TYPE_OPTIONS.Chat)
62
82
  const [showBugTypePicker, setShowBugTypePicker] = useState(false)
63
83
  const [whatWereDoing, setWhatWereDoing] = useState('')
64
84
  const [whatExpected, setWhatExpected] = useState('')
65
- const [stepsToResolve, setStepsToResolve] = useState('')
66
85
  const uploadApi = useUploadClient()
67
86
  const [uploading, setUploading] = useState(false)
68
87
  const [attachment, setAttachment] = useState<Attachment | null>(null)
69
88
  const [uploadError, setUploadError] = useState<string | null>(null)
70
- const mutation = useReportBugAction()
71
- const { mutate, status } = mutation
89
+ const { mutate: createBugReport, status } = useReportBugAction()
72
90
  const formValid =
73
91
  bugType.trim().length > 0 &&
74
92
  whatWereDoing.trim().length > 0 &&
75
93
  whatExpected.trim().length > 0 &&
76
- stepsToResolve.trim().length > 0 &&
77
94
  status === 'idle' &&
78
95
  !uploading
79
96
  const [imagePreviewURI, setImagePreviewURI] = useState<string>('')
80
-
81
97
  const isImageAttachment = startsWith(attachment?.type, 'image/')
82
98
 
83
99
  const handleSubmit = useCallback(() => {
84
- const description = `${whatWereDoing.substring(0, 100)}
85
-
86
- ## What kind of bug did you experience?
87
- ${bugType}
88
-
89
- ## What were you trying to do when you encountered the bug?
90
- ${whatWereDoing}
91
-
92
- ## What did you expect to happen? What actually happened?
93
- ${whatExpected}
100
+ const description = generateBugReportDescription(bugType, whatWereDoing, whatExpected)
94
101
 
95
- ## What steps have you tried to resolve the issue?
96
- ${stepsToResolve}`
97
-
98
- mutate({
102
+ createBugReport({
99
103
  description,
100
- description_json: JSON.stringify({
104
+ description_json: {
101
105
  bugType,
102
106
  whatWereDoing,
103
107
  whatExpected,
104
- stepsToResolve,
105
- }),
108
+ appName: appName,
109
+ },
106
110
  attachmentIds: attachment ? [attachment.id] : [],
107
111
  })
108
- }, [attachment, bugType, whatWereDoing, whatExpected, stepsToResolve, mutate])
112
+ }, [attachment, bugType, whatWereDoing, whatExpected, createBugReport, appName])
109
113
 
110
114
  const handleRemoveAttachment = useCallback(() => {
111
115
  setAttachment(null)
@@ -215,12 +219,10 @@ ${stepsToResolve}`
215
219
 
216
220
  <View style={styles.textInputContainer}>
217
221
  <Text style={styles.fieldLabel}>
218
- What kind of bug did you experience? <Text style={styles.required}>*</Text>
222
+ Where did you experience the issue? <Text style={styles.required}>*</Text>
219
223
  </Text>
220
224
  <TouchableOpacity style={styles.pickerButton} onPress={() => setShowBugTypePicker(true)}>
221
- <Text style={[styles.pickerText, !bugType && styles.pickerPlaceholder]}>
222
- {bugType || 'Select the bug type'}
223
- </Text>
225
+ <Text style={[styles.pickerText, !bugType && styles.pickerPlaceholder]}>{bugType}</Text>
224
226
  <Icon
225
227
  name="general.downChevron"
226
228
  style={styles.pickerArrow}
@@ -242,7 +244,7 @@ ${stepsToResolve}`
242
244
  <View style={styles.modalHeaderSpacer} />
243
245
  </View>
244
246
  <ScrollView style={styles.modalContent}>
245
- {BUG_TYPE_OPTIONS.map(option => (
247
+ {Object.values(BUG_TYPE_OPTIONS).map(option => (
246
248
  <TouchableOpacity
247
249
  key={option}
248
250
  style={styles.modalOption}
@@ -287,7 +289,7 @@ ${stepsToResolve}`
287
289
 
288
290
  <View style={styles.textInputContainer}>
289
291
  <Text style={styles.fieldLabel}>
290
- What did you expect to happen? What actually happened?{' '}
292
+ What did you expect to happen? Please describe what actually happened.{' '}
291
293
  <Text style={styles.required}>*</Text>
292
294
  </Text>
293
295
  <TextInput
@@ -305,25 +307,6 @@ ${stepsToResolve}`
305
307
  )}
306
308
  </View>
307
309
 
308
- <View style={styles.textInputContainer}>
309
- <Text style={styles.fieldLabel}>
310
- What steps have you tried to resolve the issue? <Text style={styles.required}>*</Text>
311
- </Text>
312
- <TextInput
313
- style={styles.textInput}
314
- multiline
315
- placeholder="Description"
316
- value={stepsToResolve}
317
- onChangeText={setStepsToResolve}
318
- maxLength={MAX_DESCRIPTION_LENGTH}
319
- />
320
- {stepsToResolve.length >= MAX_DESCRIPTION_LENGTH - 100 && (
321
- <Text variant="footnote">
322
- {stepsToResolve.length}/{MAX_DESCRIPTION_LENGTH}
323
- </Text>
324
- )}
325
- </View>
326
-
327
310
  <View style={styles.attachmentSection}>
328
311
  <View style={styles.attachmentHeader}>
329
312
  <Text style={styles.attachmentLabel}>
@@ -349,7 +332,7 @@ ${stepsToResolve}`
349
332
  </View>
350
333
  ) : (
351
334
  <Button
352
- title="Attach a screenshot"
335
+ title="Attach a screenshot or recording"
353
336
  accessibilityHint="Opens your device's image gallery"
354
337
  iconNameLeft="general.paperclip"
355
338
  onPress={pickImage}
@@ -359,14 +342,19 @@ ${stepsToResolve}`
359
342
  disabled={uploading || Boolean(attachment)}
360
343
  />
361
344
  )}
362
- </View>
363
-
364
- <View style={styles.footer}>
365
345
  <Text variant="footnote">
366
- We can’t respond to every submission, but we may reach out if we have additional
367
- questions.
346
+ Screenshots and screen recordings help us reproduce and fix issues faster.{' '}
347
+ <TextInlineButton
348
+ accessibilityRole="link"
349
+ variant="footnote"
350
+ onPress={() => Linking.openURL(VIDEO_RECORDING_HELP_URL)}
351
+ >
352
+ How to record your screen
353
+ </TextInlineButton>
368
354
  </Text>
355
+ </View>
369
356
 
357
+ <View style={styles.footer}>
370
358
  <Text variant="footnote">
371
359
  For details on how we process your data and ensure its security, please refer to our{' '}
372
360
  <TextInlineButton
@@ -384,12 +372,36 @@ ${stepsToResolve}`
384
372
  )
385
373
  }
386
374
 
375
+ const VIDEO_RECORDING_HELP_URL = Platform.select({
376
+ android: 'https://support.google.com/android/answer/6241341?hl=en',
377
+ default: 'https://support.apple.com/en-us/HT208721',
378
+ })
379
+
380
+ const generateBugReportDescription = (
381
+ bugType: BUG_TYPE_OPTIONS,
382
+ whatWereDoing: string,
383
+ whatExpected: string
384
+ ) => {
385
+ return `${whatWereDoing.substring(0, 100)}
386
+
387
+ ## What kind of bug did you experience?
388
+ ${bugType}
389
+
390
+ ## What were you trying to do when you encountered the bug?
391
+ ${whatWereDoing}
392
+
393
+ ## What did you expect to happen? What actually happened?
394
+ ${whatExpected}
395
+ `
396
+ }
397
+
387
398
  const useStyles = () => {
399
+ const { bottom } = useSafeAreaInsets()
388
400
  const { colors } = useTheme()
389
401
  return StyleSheet.create({
390
402
  container: {
391
403
  padding: 16,
392
- paddingBottom: 16,
404
+ paddingBottom: 16 + bottom,
393
405
  gap: 24,
394
406
  },
395
407
  fullHeight: {
@@ -2,7 +2,7 @@ import React, { useContext, useMemo } from 'react'
2
2
  import { StyleSheet, View, ViewStyle } from 'react-native'
3
3
  import { FlatList } from 'react-native-gesture-handler'
4
4
  import { Heading } from '../../../components'
5
- import { useTheme } from '../../../hooks'
5
+ import { useAtFontScaleBreakpoint, useTheme } from '../../../hooks'
6
6
  import { FilterContext, useFilterContext } from '../context/conversation_filter_context'
7
7
  import { FilterTypes } from '../filter_types'
8
8
  import { useGroupsToFilter, useTeamsToFilter } from '../hooks/filters'
@@ -119,7 +119,7 @@ export const ConversationFilters = () => {
119
119
  },
120
120
  {
121
121
  type: groupItems.length ? SectionTypes.header : SectionTypes.hidden,
122
- data: { title: 'Groups' },
122
+ data: { title: 'Groups', style: styles.extraHeaderScalableSpace },
123
123
  },
124
124
  ...groupItemData,
125
125
  {
@@ -128,7 +128,7 @@ export const ConversationFilters = () => {
128
128
  },
129
129
  {
130
130
  type: teamItems.length ? SectionTypes.header : SectionTypes.hidden,
131
- data: { title: 'Teams' },
131
+ data: { title: 'Teams', style: styles.extraHeaderScalableSpace },
132
132
  },
133
133
  ...teamItemData,
134
134
  {
@@ -166,12 +166,12 @@ export const ConversationFilters = () => {
166
166
  const useStyles = () => {
167
167
  const { platformListPaddingBottom } = useFilterContext()
168
168
  const theme = useTheme()
169
+ const atFontScaleBreakpoint = useAtFontScaleBreakpoint()
169
170
 
170
171
  return StyleSheet.create({
171
172
  flatlistContainer: {
172
173
  paddingBottom: platformListPaddingBottom,
173
174
  },
174
- section: {},
175
175
  sectionHeader: {
176
176
  flexDirection: 'row',
177
177
  justifyContent: 'space-between',
@@ -179,7 +179,9 @@ const useStyles = () => {
179
179
  paddingBottom: 8,
180
180
  paddingHorizontal: 16,
181
181
  },
182
- selectTeamsButton: {},
182
+ extraHeaderScalableSpace: {
183
+ marginTop: atFontScaleBreakpoint ? 24 : 0,
184
+ },
183
185
  filterBar: {
184
186
  backgroundColor: theme.colors.fillColorNeutral100Inverted,
185
187
  flexDirection: 'row',
@@ -203,13 +205,14 @@ const useStyles = () => {
203
205
 
204
206
  type HeaderProps = {
205
207
  title: string
208
+ style?: ViewStyle
206
209
  }
207
210
 
208
- const Header = ({ title }: HeaderProps) => {
211
+ const Header = ({ title, style = {} }: HeaderProps) => {
209
212
  const styles = useStyles()
210
213
 
211
214
  return (
212
- <View style={styles.sectionHeader}>
215
+ <View style={[styles.sectionHeader, style]}>
213
216
  <Heading variant="h3">{title}</Heading>
214
217
  </View>
215
218
  )
@@ -3,33 +3,28 @@ import { NavigationProp, useNavigation } from '@react-navigation/native'
3
3
  import React, { PropsWithChildren, useContext } from 'react'
4
4
  import { StyleSheet, View, ViewStyle } from 'react-native'
5
5
  import { Heading, Icon, Image, Text, TextButton } from '../../../components'
6
- import { useTheme } from '../../../hooks'
6
+ import {
7
+ useAtFontScaleBreakpoint,
8
+ useFontScale,
9
+ useScalableNumberOfLines,
10
+ useTheme,
11
+ } from '../../../hooks'
7
12
  import { useServicesTeamsMap } from '../../../hooks/services/use_services_team'
8
13
  import { GroupResource } from '../../../types/resources/group_resource'
9
14
  import { FilterContext } from '../context/conversation_filter_context'
10
15
  import { FilterTypes } from '../filter_types'
11
16
  import { FilteredGroup } from '../hooks/filters'
12
17
  import { ConversationFilterStackParamList } from '../screen_props'
18
+ import { MAX_FONT_SIZE_MULTIPLIER, platformFontWeightBold } from '../../../utils/styles'
13
19
 
14
20
  export type FilterProps = { group_source_app_name?: string; isActive: boolean; filter: FilterTypes }
15
21
 
16
22
  export const FilterRow = ({ group_source_app_name, isActive, filter }: FilterProps) => {
17
- const styles = useRowStyles({ isActive })
18
23
  const { setAppFilter } = useContext(FilterContext)
19
24
 
20
25
  return (
21
- <PressableRow
22
- style={styles.row}
23
- onPress={() => setAppFilter({ group_source_app_name })}
24
- isActive={isActive}
25
- >
26
+ <PressableRow onPress={() => setAppFilter({ group_source_app_name })} isActive={isActive}>
26
27
  <Text>{filter}</Text>
27
- <Icon
28
- name="general.check"
29
- size={16}
30
- style={styles.rowIconRight}
31
- accessibilityElementsHidden
32
- />
33
28
  </PressableRow>
34
29
  )
35
30
  }
@@ -40,7 +35,7 @@ export type GroupRowProps = {
40
35
  }
41
36
 
42
37
  export const GroupRow = ({ group, isActive }: GroupRowProps) => {
43
- const styles = useRowStyles({ isActive })
38
+ const styles = useRowStyles()
44
39
  const { setGroupFilter } = useContext(FilterContext)
45
40
 
46
41
  const handleFilterByGroup = () => {
@@ -50,27 +45,23 @@ export const GroupRow = ({ group, isActive }: GroupRowProps) => {
50
45
  const { headerImage, membershipsCount } = group
51
46
 
52
47
  return (
53
- <PressableRow style={styles.row} onPress={handleFilterByGroup} isActive={isActive}>
48
+ <PressableRow onPress={handleFilterByGroup} isActive={isActive}>
54
49
  {headerImage?.thumbnail && (
55
50
  <Image
56
51
  source={{ uri: headerImage?.thumbnail }}
57
52
  resizeMode="cover"
58
53
  style={styles.rowImage}
59
- alt={`Image for ${group.name}`}
54
+ alt=""
60
55
  />
61
56
  )}
62
- <View style={styles.rowContent}>
57
+ <View style={styles.rowTextContainer}>
63
58
  <Heading variant="h3" style={styles.rowTitle}>
64
59
  {group.name}
65
60
  </Heading>
66
- {membershipsCount && <Text>{group.membershipsCount} members</Text>}
61
+ {membershipsCount && (
62
+ <Text style={styles.rowSubtitle}>{group.membershipsCount} members</Text>
63
+ )}
67
64
  </View>
68
- <Icon
69
- name="general.check"
70
- size={16}
71
- style={styles.rowIconRight}
72
- accessibilityElementsHidden
73
- />
74
65
  </PressableRow>
75
66
  )
76
67
  }
@@ -86,7 +77,8 @@ export const TeamRow = ({ team }: TeamRowProps) => {
86
77
  const { params } = useContext(FilterContext)
87
78
  const { chat_group_graph_id } = params
88
79
  const isActive = chat_group_graph_id === team.id.toString()
89
- const styles = useRowStyles({ isActive })
80
+ const styles = useRowStyles()
81
+ const scalableNumberOfLines = useScalableNumberOfLines()
90
82
 
91
83
  const handleFilterByGroup = () => {
92
84
  setGroupFilter({ chat_group_graph_id: team.id })
@@ -97,19 +89,15 @@ export const TeamRow = ({ team }: TeamRowProps) => {
97
89
  const serviceTypeName = concatedServiceTypeNames || servicesTeam?.group
98
90
 
99
91
  return (
100
- <PressableRow style={styles.row} onPress={handleFilterByGroup} isActive={isActive}>
101
- <View style={styles.rowContent}>
92
+ <PressableRow onPress={handleFilterByGroup} isActive={isActive}>
93
+ <View style={styles.rowTextContainer}>
102
94
  <Heading variant="h3" style={styles.rowTitle}>
103
95
  {team.name}
104
96
  </Heading>
105
- <Text numberOfLines={1}>{serviceTypeName}</Text>
97
+ <Text numberOfLines={scalableNumberOfLines} style={styles.rowSubtitle}>
98
+ {serviceTypeName}
99
+ </Text>
106
100
  </View>
107
- <Icon
108
- name="general.check"
109
- size={16}
110
- style={styles.rowIconRight}
111
- accessibilityElementsHidden
112
- />
113
101
  </PressableRow>
114
102
  )
115
103
  }
@@ -121,7 +109,7 @@ export const ViewMore = ({ routeName }: ViewMoreRowProps) => {
121
109
  const navigation = useNavigation<NavigationProp<ConversationFilterStackParamList, 'Filters'>>()
122
110
 
123
111
  return (
124
- <View style={[styles.borderLessRow]}>
112
+ <View style={[styles.rowWithoutBorder]}>
125
113
  <TextButton onPress={() => navigation.navigate(routeName, {})} style={styles.viewMoreButton}>
126
114
  View more
127
115
  </TextButton>
@@ -135,15 +123,23 @@ export const PressableRow = ({
135
123
  onPress,
136
124
  style,
137
125
  }: PropsWithChildren<{ isActive: boolean; onPress: () => void; style?: ViewStyle }>) => {
138
- const styles = useRowStyles()
126
+ const styles = useRowStyles({ isActive })
139
127
  return (
140
128
  <PlatformPressable
141
- style={styles.container}
129
+ style={styles.pressable}
142
130
  onPress={onPress}
143
131
  accessibilityRole="radio"
144
132
  accessibilityState={{ selected: isActive }}
145
133
  >
146
- <View style={[styles.innerContainer, style]}>{children}</View>
134
+ <View style={styles.row}>
135
+ <View style={[styles.rowInner, style]}>{children}</View>
136
+ <Icon
137
+ name="general.check"
138
+ style={styles.rowIconRight}
139
+ accessibilityElementsHidden
140
+ maxFontSizeMultiplier={2.5}
141
+ />
142
+ </View>
147
143
  </PlatformPressable>
148
144
  )
149
145
  }
@@ -154,20 +150,35 @@ const THUMBNAIL_HEIGHT = THUMBNAIL_WIDTH / ASPECT_RATIO
154
150
 
155
151
  const useRowStyles = ({ isActive = false }: { isActive?: boolean } = {}) => {
156
152
  const theme = useTheme()
153
+ const fontScale = useFontScale({ maxFontSizeMultiplier: MAX_FONT_SIZE_MULTIPLIER })
154
+ const atFontScaleBreakpoint = useAtFontScaleBreakpoint()
155
+
157
156
  return StyleSheet.create({
158
- container: {
157
+ pressable: {
159
158
  paddingLeft: 16,
160
159
  },
161
- innerContainer: {
160
+ row: {
162
161
  flexDirection: 'row',
163
162
  alignItems: 'center',
163
+ justifyContent: 'space-between',
164
164
  gap: 12,
165
165
  borderBottomWidth: 1,
166
- borderBottomColor: theme.colors.fillColorNeutral050Base,
166
+ borderBottomColor: atFontScaleBreakpoint
167
+ ? theme.colors.borderColorDefaultDark
168
+ : theme.colors.borderColorDefaultBase,
167
169
  paddingVertical: 12,
168
170
  paddingRight: 16,
169
171
  },
170
- borderLessRow: {
172
+ rowInner: {
173
+ flexDirection: atFontScaleBreakpoint ? 'column' : 'row',
174
+ alignItems: atFontScaleBreakpoint ? 'flex-start' : 'center',
175
+ gap: 12,
176
+ flexShrink: 1,
177
+ },
178
+ rowTextContainer: {
179
+ flexShrink: 1,
180
+ },
181
+ rowWithoutBorder: {
171
182
  flexDirection: 'row',
172
183
  alignItems: 'center',
173
184
  gap: 12,
@@ -178,23 +189,25 @@ const useRowStyles = ({ isActive = false }: { isActive?: boolean } = {}) => {
178
189
  viewMoreButton: {
179
190
  flex: 1,
180
191
  },
181
- row: {},
182
192
  rowImage: {
183
- width: THUMBNAIL_WIDTH,
184
- height: THUMBNAIL_HEIGHT,
185
- borderRadius: 4,
186
- },
187
- rowContent: {
188
- flexShrink: 1,
193
+ width: THUMBNAIL_WIDTH * fontScale,
194
+ height: THUMBNAIL_HEIGHT * fontScale,
195
+ borderRadius: 4 * fontScale,
189
196
  },
190
197
  rowIconRight: {
191
- marginLeft: 'auto',
198
+ fontSize: 16,
192
199
  color: theme.colors.statusSuccessIcon,
193
200
  opacity: isActive ? 1 : 0,
194
201
  },
195
202
  rowTitle: {
196
203
  fontSize: 16,
197
204
  flexShrink: 1,
205
+ fontWeight: atFontScaleBreakpoint ? 400 : platformFontWeightBold,
206
+ },
207
+ rowSubtitle: {
208
+ color: atFontScaleBreakpoint
209
+ ? theme.colors.textColorDefaultSecondary
210
+ : theme.colors.textColorDefaultPrimary,
198
211
  },
199
212
  })
200
213
  }
@@ -68,6 +68,8 @@ export const ListHeaderComponent = () => {
68
68
  onPress={alertMarkAllRead}
69
69
  disabled={isPending}
70
70
  maxFontSizeMultiplier={MAX_FONT_SIZE_MULTIPLIER_LANDMARK}
71
+ accessibilityShowsLargeContentViewer
72
+ accessibilityLargeContentTitle="Mark all read"
71
73
  >
72
74
  Mark all read
73
75
  </TextButton>
@@ -153,7 +155,7 @@ const useStyles = () => {
153
155
  rowGap: 8,
154
156
  columnGap: 16,
155
157
  paddingTop: 8,
156
- paddingBottom: atFontScaleBreakpoint ? 8 : 0,
158
+ paddingBottom: atFontScaleBreakpoint ? 4 : 0,
157
159
  paddingHorizontal: ROW_PADDING_HORIZONTAL,
158
160
  },
159
161
  filterRow: {
@@ -1,5 +1,5 @@
1
1
  import { StaticScreenProps, useNavigation } from '@react-navigation/native'
2
- import React from 'react'
2
+ import React, { useCallback } from 'react'
3
3
  import { StyleSheet, View } from 'react-native'
4
4
  import { Conversations, TextButton } from '../../components'
5
5
  import { ActionButton } from '../../components/display/action_button'
@@ -54,9 +54,9 @@ export function ConversationsScreen({ route }: ConversationsScreenProps) {
54
54
  })
55
55
  }
56
56
 
57
- const reportABug = () => {
58
- return navigation.navigate('BugReport')
59
- }
57
+ const handleGetHelp = useCallback(() => {
58
+ navigation.navigate('GetHelp')
59
+ }, [navigation])
60
60
 
61
61
  return (
62
62
  <View style={styles.container}>
@@ -71,10 +71,12 @@ export function ConversationsScreen({ route }: ConversationsScreenProps) {
71
71
  secondaryButton={
72
72
  <TextButton
73
73
  variant="tertiary"
74
- onPress={reportABug}
74
+ onPress={handleGetHelp}
75
75
  maxFontSizeMultiplier={MAX_FONT_SIZE_MULTIPLIER_LANDMARK}
76
+ accessibilityShowsLargeContentViewer
77
+ accessibilityLargeContentTitle="Get help"
76
78
  >
77
- Report a bug
79
+ Get help
78
80
  </TextButton>
79
81
  }
80
82
  />
@@ -863,7 +863,7 @@ function ImageIconsSection({ isLast }: SectionProps) {
863
863
  description="Displays any icon from @planningcenter/icons. Missing icons will fallback to a grey circle. Styling with `fontSize` will allow it to scale with the device's text a11y size."
864
864
  >
865
865
  <Row>
866
- {/* @ts-expect-error - Icon name is not a string */}
866
+ {/* @ts-expect-error - Testing missing icon fallback */}
867
867
  <Icon name="missingIcon" size={20} />
868
868
  <Icon name="general.textMessage" size={20} />
869
869
  <Icon name="general.bell" size={20} color={colors.needsDesignPass} />