@planningcenter/chat-react-native 3.15.0 → 3.16.0-rc.1

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 (45) hide show
  1. package/build/components/conversation/message.d.ts.map +1 -1
  2. package/build/components/conversation/message.js +11 -9
  3. package/build/components/conversation/message.js.map +1 -1
  4. package/build/components/conversation/reply_connectors.d.ts +9 -0
  5. package/build/components/conversation/reply_connectors.d.ts.map +1 -0
  6. package/build/components/conversation/reply_connectors.js +147 -0
  7. package/build/components/conversation/reply_connectors.js.map +1 -0
  8. package/build/components/display/avatar.d.ts +2 -1
  9. package/build/components/display/avatar.d.ts.map +1 -1
  10. package/build/components/display/avatar.js +2 -2
  11. package/build/components/display/avatar.js.map +1 -1
  12. package/build/components/display/avatar_group.d.ts +2 -1
  13. package/build/components/display/avatar_group.d.ts.map +1 -1
  14. package/build/components/display/avatar_group.js +2 -2
  15. package/build/components/display/avatar_group.js.map +1 -1
  16. package/build/components/primitive/avatar_primitive.d.ts +1 -0
  17. package/build/components/primitive/avatar_primitive.d.ts.map +1 -1
  18. package/build/components/primitive/avatar_primitive.js +25 -19
  19. package/build/components/primitive/avatar_primitive.js.map +1 -1
  20. package/build/navigation/index.d.ts +6 -3
  21. package/build/navigation/index.d.ts.map +1 -1
  22. package/build/screens/conversation_screen.d.ts +1 -0
  23. package/build/screens/conversation_screen.d.ts.map +1 -1
  24. package/build/screens/conversation_screen.js +1 -0
  25. package/build/screens/conversation_screen.js.map +1 -1
  26. package/build/screens/conversations/conversations_screen.js +1 -1
  27. package/build/screens/conversations/conversations_screen.js.map +1 -1
  28. package/build/screens/get_help_screen.d.ts +6 -3
  29. package/build/screens/get_help_screen.d.ts.map +1 -1
  30. package/build/screens/get_help_screen.js +22 -10
  31. package/build/screens/get_help_screen.js.map +1 -1
  32. package/build/utils/styles.d.ts +1 -0
  33. package/build/utils/styles.d.ts.map +1 -1
  34. package/build/utils/styles.js +1 -0
  35. package/build/utils/styles.js.map +1 -1
  36. package/package.json +2 -2
  37. package/src/components/conversation/message.tsx +22 -8
  38. package/src/components/conversation/reply_connectors.tsx +192 -0
  39. package/src/components/display/avatar.tsx +8 -1
  40. package/src/components/display/avatar_group.tsx +8 -1
  41. package/src/components/primitive/avatar_primitive.tsx +28 -17
  42. package/src/screens/conversation_screen.tsx +2 -0
  43. package/src/screens/conversations/conversations_screen.tsx +1 -1
  44. package/src/screens/get_help_screen.tsx +52 -18
  45. package/src/utils/styles.ts +1 -0
@@ -0,0 +1,192 @@
1
+ import { StyleSheet, View, ViewStyle } from 'react-native'
2
+ import Svg, { Path } from 'react-native-svg'
3
+ import { useTheme } from '../../hooks'
4
+ import { MessageResource } from '../../types'
5
+ import {
6
+ CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL,
7
+ MESSAGE_AUTHOR_AVATAR_COLUMN_WIDTH,
8
+ } from '../../utils/styles'
9
+ import { REPLIES_FEATURE_ENABLED } from '../../screens/conversation_screen'
10
+
11
+ const MY_REPLY_CONNECTOR_WIDTH = 38
12
+ const CONNECTOR_BORDER_WIDTH = 4
13
+
14
+ interface ReplyConnectorProps {
15
+ message: MessageResource
16
+ messageBubbleHeight: number
17
+ }
18
+
19
+ export function TheirReplyConnector({ message, messageBubbleHeight }: ReplyConnectorProps) {
20
+ const styles = useStyles()
21
+
22
+ if (!REPLIES_FEATURE_ENABLED) return null
23
+
24
+ // TODO: Need to remove, just adding this to remove the lint warning about message being unused
25
+ console.log('message', message)
26
+
27
+ const connectorMap: Record<string, React.ReactNode | null> = {
28
+ shortTailCurve: <ShortTailCurveConnector height={messageBubbleHeight / 2} />,
29
+ verticalLine: <VerticalLineConnector />,
30
+ default: null,
31
+ }
32
+
33
+ // TODO: Currently using fake message attributes. Will need to refactor once we have the message reply logic in place.
34
+ const getConnectorKey = () => {
35
+ // TODO: if (message.previousMessageIsTheirReplyMessage && message.nextMessageIsSomeoneElseReplyMessage) return 'shortTailCurve'
36
+ // TODO: if (message.isRootMessage || (message.isTheirReplyMessage && message.nextMessageIsTheirReplyMessage) || (message.isTheirReplyMessage && message.nextMessageIsSomeoneElseReplyMessage && !message.nextMessageIsMyMessage)) return 'verticalLine'
37
+ return 'default'
38
+ }
39
+
40
+ const ConnectorComponent = connectorMap[getConnectorKey()]
41
+
42
+ if (!ConnectorComponent) return null
43
+
44
+ return <View style={styles.theirReplyConnectorContainer}>{ConnectorComponent}</View>
45
+ }
46
+
47
+ // TODO: Refactor how we choose the correct connector once we have the logic in place
48
+ export function MyReplyConnector({ message, messageBubbleHeight }: ReplyConnectorProps) {
49
+ const styles = useStyles()
50
+
51
+ if (!REPLIES_FEATURE_ENABLED) return null
52
+
53
+ // TODO: Need to remove, just adding this to remove the lint warning about message being unused
54
+ console.log('message', message)
55
+
56
+ const connectorMap: Record<string, React.ReactNode | null> = {
57
+ longTailCurve: (
58
+ <LongTailCurveConnector
59
+ height={messageBubbleHeight / 2}
60
+ style={styles.myReplyConnectorPosition}
61
+ />
62
+ ),
63
+ longHeadCurve: (
64
+ <LongHeadCurveConnector
65
+ height={messageBubbleHeight / 2}
66
+ style={styles.myReplyConnectorPosition}
67
+ />
68
+ ),
69
+ verticalLine: <VerticalLineConnector style={styles.myReplyConnectorPosition} />,
70
+ swirl: <SwirlConnector style={styles.myReplyConnectorPosition} />,
71
+ default: null,
72
+ }
73
+
74
+ // TODO: Currently using fake message attributes. Will need to refactor once we have the message reply logic in place.
75
+ const getConnectorKey = () => {
76
+ // TODO: if (message.currentMessageIsReply && (message.nextMessageIsNotReplyMessage || message.nextMessageIsDifferentReplyRootId)) return 'longTailCurve'
77
+ // TODO: if (message.currentMessageIsReplyRoot && message.nextMessageIsMyReply) return 'longHeadCurve'
78
+ // TODO: if (message.currentMessageIsReply && message.nextMessageIsReply) return 'verticalLine'
79
+ // TODO: if (message.currentMessageIsReply && message.previousMessageSomeoneElseReply && (message.nextMessageIsSomeoneElseReply || message.nextMessageIsMyReply)) return 'swirl'
80
+ return 'default'
81
+ }
82
+
83
+ const ConnectorComponent = connectorMap[getConnectorKey()]
84
+
85
+ if (!ConnectorComponent) return null
86
+
87
+ return <View style={styles.myReplyConnectorContainer}>{ConnectorComponent}</View>
88
+ }
89
+
90
+ function VerticalLineConnector({ style }: { style?: ViewStyle }) {
91
+ const styles = useStyles()
92
+ return <View style={[styles.verticalLineConnector, style]} />
93
+ }
94
+
95
+ function LongHeadCurveConnector({ height, style }: { height: number; style?: ViewStyle }) {
96
+ const styles = useStyles()
97
+ return <View style={[styles.longHeadCurveConnector, { height, marginTop: height }, style]} />
98
+ }
99
+
100
+ function LongTailCurveConnector({ height, style }: { height: number; style?: ViewStyle }) {
101
+ const styles = useStyles()
102
+ return <View style={[styles.longTailCurveConnector, { height }, style]} />
103
+ }
104
+
105
+ function ShortTailCurveConnector({ height }: { height: number }) {
106
+ const styles = useStyles()
107
+ return <View style={[styles.shortTailCurveConnector, { height }]} />
108
+ }
109
+
110
+ function SwirlConnector({ style }: { style?: ViewStyle }) {
111
+ const styles = useStyles()
112
+ const { colors } = useTheme()
113
+ const borderColor = colors.borderColorDefaultBase
114
+
115
+ return (
116
+ <View style={[styles.swirlConnectorContainer, style]}>
117
+ <View style={styles.swirlConnectorVerticalLineHead} />
118
+ <Svg width={27} height={34} fill="none">
119
+ <Path
120
+ stroke={borderColor}
121
+ strokeWidth={CONNECTOR_BORDER_WIDTH}
122
+ d="M2.07 34c0-6.142 1.201-12.283 3.343-17m0 0c2.305-5.075 5.699-8.5 9.857-8.5 4.86 0 8.8 3.806 8.8 8.5s-3.94 8.5-8.8 8.5c-4.158 0-7.552-3.425-9.857-8.5zm0 0C3.271 12.283 2.07 6.142 2.07 0"
123
+ />
124
+ </Svg>
125
+ <View style={styles.swirlConnectorVerticalLineTail} />
126
+ </View>
127
+ )
128
+ }
129
+
130
+ const useStyles = () => {
131
+ const { colors } = useTheme()
132
+ const borderColor = colors.borderColorDefaultBase
133
+
134
+ return StyleSheet.create({
135
+ theirReplyConnectorContainer: {
136
+ width: '50%',
137
+ alignSelf: 'flex-end',
138
+ flex: 1,
139
+ },
140
+ myReplyConnectorContainer: {
141
+ width: MESSAGE_AUTHOR_AVATAR_COLUMN_WIDTH,
142
+ position: 'absolute',
143
+ left: CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL,
144
+ top: 0,
145
+ bottom: 0,
146
+ },
147
+ myReplyConnectorPosition: {
148
+ left: '50%',
149
+ },
150
+ verticalLineConnector: {
151
+ flex: 1,
152
+ borderLeftWidth: CONNECTOR_BORDER_WIDTH,
153
+ borderLeftColor: borderColor,
154
+ },
155
+ longHeadCurveConnector: {
156
+ width: MY_REPLY_CONNECTOR_WIDTH,
157
+ borderColor: borderColor,
158
+ borderLeftWidth: CONNECTOR_BORDER_WIDTH,
159
+ borderTopWidth: CONNECTOR_BORDER_WIDTH,
160
+ borderTopLeftRadius: 16,
161
+ flex: 1,
162
+ },
163
+ longTailCurveConnector: {
164
+ width: MY_REPLY_CONNECTOR_WIDTH,
165
+ borderColor: borderColor,
166
+ borderLeftWidth: CONNECTOR_BORDER_WIDTH,
167
+ borderBottomWidth: CONNECTOR_BORDER_WIDTH,
168
+ borderBottomLeftRadius: 16,
169
+ },
170
+ shortTailCurveConnector: {
171
+ borderColor: borderColor,
172
+ borderLeftWidth: CONNECTOR_BORDER_WIDTH,
173
+ borderBottomWidth: CONNECTOR_BORDER_WIDTH,
174
+ borderBottomLeftRadius: 16,
175
+ },
176
+ swirlConnectorContainer: {
177
+ flex: 1,
178
+ },
179
+ swirlConnectorVerticalLineHead: {
180
+ backgroundColor: borderColor,
181
+ width: CONNECTOR_BORDER_WIDTH,
182
+ flex: 1,
183
+ marginBottom: -1, // Ensures there is no gap between the vertical line and the swirl connector
184
+ },
185
+ swirlConnectorVerticalLineTail: {
186
+ backgroundColor: borderColor,
187
+ width: CONNECTOR_BORDER_WIDTH,
188
+ flex: 1,
189
+ marginTop: -1, // Ensures there is no gap between the vertical line and the swirl connector
190
+ },
191
+ })
192
+ }
@@ -14,6 +14,7 @@ interface AvatarProps {
14
14
  fallbackIconName?: IconString
15
15
  style?: AvatarRootProps['style']
16
16
  maxFontSizeMultiplier?: AvatarRootProps['maxFontSizeMultiplier']
17
+ minFontSizeMultiplier?: AvatarRootProps['minFontSizeMultiplier']
17
18
  }
18
19
 
19
20
  export function Avatar({
@@ -24,11 +25,17 @@ export function Avatar({
24
25
  fallbackIconName = 'general.person',
25
26
  style,
26
27
  maxFontSizeMultiplier,
28
+ minFontSizeMultiplier,
27
29
  }: AvatarProps) {
28
30
  const shouldShowFallback = showFallback || !sourceUri
29
31
 
30
32
  return (
31
- <AvatarPrimitive.Root size={size} style={style} maxFontSizeMultiplier={maxFontSizeMultiplier}>
33
+ <AvatarPrimitive.Root
34
+ size={size}
35
+ style={style}
36
+ maxFontSizeMultiplier={maxFontSizeMultiplier}
37
+ minFontSizeMultiplier={minFontSizeMultiplier}
38
+ >
32
39
  <AvatarPrimitive.Mask>
33
40
  {shouldShowFallback ? (
34
41
  <AvatarPrimitive.ImageFallback name={fallbackIconName} />
@@ -12,6 +12,7 @@ interface AvatarGroupDisplayProps {
12
12
  size?: AvatarRootProps['size']
13
13
  style?: AvatarRootProps['style']
14
14
  maxFontSizeMultiplier?: AvatarRootProps['maxFontSizeMultiplier']
15
+ minFontSizeMultiplier?: AvatarRootProps['minFontSizeMultiplier']
15
16
  }
16
17
 
17
18
  export function AvatarGroup({
@@ -21,11 +22,17 @@ export function AvatarGroup({
21
22
  size = 'lg',
22
23
  style,
23
24
  maxFontSizeMultiplier,
25
+ minFontSizeMultiplier,
24
26
  }: AvatarGroupDisplayProps) {
25
27
  const shouldShowFallback = showFallback || !sourceUris || sourceUris.length === 0
26
28
 
27
29
  return (
28
- <AvatarPrimitive.Root size={size} style={style} maxFontSizeMultiplier={maxFontSizeMultiplier}>
30
+ <AvatarPrimitive.Root
31
+ size={size}
32
+ style={style}
33
+ maxFontSizeMultiplier={maxFontSizeMultiplier}
34
+ minFontSizeMultiplier={minFontSizeMultiplier}
35
+ >
29
36
  <AvatarPrimitive.Mask>
30
37
  {shouldShowFallback ? (
31
38
  <AvatarPrimitive.ImageFallback name={fallbackIconName} />
@@ -86,6 +86,7 @@ interface AvatarContextType {
86
86
  allImagesLoaded: boolean
87
87
  setAllImagesLoaded: React.Dispatch<React.SetStateAction<boolean>>
88
88
  maxFontSizeMultiplier: number
89
+ minFontSizeMultiplier?: number
89
90
  }
90
91
 
91
92
  const AvatarContext = createContext<AvatarContextType | null>(null)
@@ -107,6 +108,7 @@ interface AvatarRootProps {
107
108
  size?: AvatarSize
108
109
  style?: ViewProps['style']
109
110
  maxFontSizeMultiplier?: number
111
+ minFontSizeMultiplier?: number
110
112
  }
111
113
 
112
114
  function AvatarRoot({
@@ -114,13 +116,20 @@ function AvatarRoot({
114
116
  size = 'md',
115
117
  style,
116
118
  maxFontSizeMultiplier = MAX_FONT_SIZE_MULTIPLIER,
119
+ minFontSizeMultiplier = undefined,
117
120
  }: AvatarRootProps) {
118
121
  const [allImagesLoaded, setAllImagesLoaded] = useState(false)
119
- const styles = useStyles({ size, maxFontSizeMultiplier })
122
+ const styles = useStyles({ size, maxFontSizeMultiplier, minFontSizeMultiplier })
120
123
 
121
124
  return (
122
125
  <AvatarContext.Provider
123
- value={{ size, allImagesLoaded, setAllImagesLoaded, maxFontSizeMultiplier }}
126
+ value={{
127
+ size,
128
+ allImagesLoaded,
129
+ setAllImagesLoaded,
130
+ maxFontSizeMultiplier,
131
+ minFontSizeMultiplier,
132
+ }}
124
133
  >
125
134
  <View style={[styles.rootContainer, style]}>{children}</View>
126
135
  </AvatarContext.Provider>
@@ -136,8 +145,8 @@ AvatarRoot.displayName = 'Avatar.Root'
136
145
  type AvatarMaskProps = ViewProps
137
146
 
138
147
  function AvatarMask({ children, ...props }: AvatarMaskProps) {
139
- const { maxFontSizeMultiplier } = useAvatarContext()
140
- const styles = useStyles({ maxFontSizeMultiplier })
148
+ const { maxFontSizeMultiplier, minFontSizeMultiplier } = useAvatarContext()
149
+ const styles = useStyles({ maxFontSizeMultiplier, minFontSizeMultiplier })
141
150
 
142
151
  return (
143
152
  <View style={styles.mask} {...props}>
@@ -157,8 +166,8 @@ interface AvatarImageProps extends Omit<ImageProps, 'source' | 'alt'> {
157
166
  }
158
167
 
159
168
  function AvatarImage({ sourceUri, ...props }: AvatarImageProps) {
160
- const { size, maxFontSizeMultiplier } = useAvatarContext()
161
- const fontScale = useFontScale({ maxFontSizeMultiplier })
169
+ const { size, maxFontSizeMultiplier, minFontSizeMultiplier } = useAvatarContext()
170
+ const fontScale = useFontScale({ maxFontSizeMultiplier, minFontSizeMultiplier })
162
171
  const scaledAvatarSize = AVATAR_PX[size] * fontScale
163
172
 
164
173
  return <Image source={{ uri: sourceUri }} loaderSize={scaledAvatarSize} {...props} alt="" />
@@ -183,9 +192,9 @@ interface AvatarImageFallbackProps {
183
192
  }
184
193
 
185
194
  function AvatarImageFallback({ name = 'general.person' }: AvatarImageFallbackProps) {
186
- const { size, maxFontSizeMultiplier } = useAvatarContext()
187
- const styles = useStyles({ maxFontSizeMultiplier })
188
- const fontScale = useFontScale({ maxFontSizeMultiplier })
195
+ const { size, maxFontSizeMultiplier, minFontSizeMultiplier } = useAvatarContext()
196
+ const styles = useStyles({ maxFontSizeMultiplier, minFontSizeMultiplier })
197
+ const fontScale = useFontScale({ maxFontSizeMultiplier, minFontSizeMultiplier })
189
198
  const scaledIconSize = AVATAR_FALLBACK_ICON_PX[size] * fontScale
190
199
 
191
200
  return (
@@ -213,8 +222,8 @@ interface AvatarGroupProps {
213
222
  type AvatarIndex = 0 | 1 | 2 | 3
214
223
 
215
224
  function AvatarGroup({ sourceUris }: AvatarGroupProps) {
216
- const { setAllImagesLoaded, maxFontSizeMultiplier } = useAvatarContext()
217
- const styles = useStyles({ maxFontSizeMultiplier })
225
+ const { setAllImagesLoaded, maxFontSizeMultiplier, minFontSizeMultiplier } = useAvatarContext()
226
+ const styles = useStyles({ maxFontSizeMultiplier, minFontSizeMultiplier })
218
227
  const [loadingStatus, setLoadingStatus] = useState<Record<AvatarIndex, boolean>>({
219
228
  0: false,
220
229
  1: false,
@@ -323,9 +332,9 @@ AvatarGroup.displayName = 'Avatar.Group'
323
332
  // =================================
324
333
 
325
334
  function AvatarGroupLoader() {
326
- const { size, allImagesLoaded, maxFontSizeMultiplier } = useAvatarContext()
327
- const styles = useStyles({ size, maxFontSizeMultiplier })
328
- const fontScale = useFontScale({ maxFontSizeMultiplier })
335
+ const { size, allImagesLoaded, maxFontSizeMultiplier, minFontSizeMultiplier } = useAvatarContext()
336
+ const styles = useStyles({ size, maxFontSizeMultiplier, minFontSizeMultiplier })
337
+ const fontScale = useFontScale({ maxFontSizeMultiplier, minFontSizeMultiplier })
329
338
  const scaledSpinnerSize = AVATAR_PX[size] * fontScale
330
339
 
331
340
  if (allImagesLoaded) return null
@@ -348,8 +357,8 @@ interface AvatarPresenceProps extends ViewProps {
348
357
  }
349
358
 
350
359
  function AvatarPresence({ presence, ...props }: AvatarPresenceProps) {
351
- const { size, maxFontSizeMultiplier } = useAvatarContext()
352
- const styles = useStyles({ size, presence, maxFontSizeMultiplier })
360
+ const { size, maxFontSizeMultiplier, minFontSizeMultiplier } = useAvatarContext()
361
+ const styles = useStyles({ size, presence, maxFontSizeMultiplier, minFontSizeMultiplier })
353
362
 
354
363
  return <View style={styles.presence} {...props} />
355
364
  }
@@ -364,15 +373,17 @@ interface Styles {
364
373
  size?: AvatarSize
365
374
  presence?: AvatarPresenceType
366
375
  maxFontSizeMultiplier?: number
376
+ minFontSizeMultiplier?: number
367
377
  }
368
378
 
369
379
  const useStyles = ({
370
380
  size = 'md',
371
381
  presence = 'offline',
372
382
  maxFontSizeMultiplier = MAX_FONT_SIZE_MULTIPLIER,
383
+ minFontSizeMultiplier = undefined,
373
384
  }: Styles = {}) => {
374
385
  const { colors } = useTheme()
375
- const fontScale = useFontScale({ maxFontSizeMultiplier })
386
+ const fontScale = useFontScale({ maxFontSizeMultiplier, minFontSizeMultiplier })
376
387
  const PRESENCE_COLOR = {
377
388
  online: colors.fillColorInteractionOnlineDefault,
378
389
  offline: colors.iconColorDefaultDisabled,
@@ -35,6 +35,8 @@ import { CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL } from '../utils/styles'
35
35
  import { useConversationJoltEvents } from '../hooks/use_conversation_jolt_events'
36
36
  import { JumpToBottomButton } from '../components/conversation/jump_to_bottom_button'
37
37
 
38
+ export const REPLIES_FEATURE_ENABLED = false
39
+
38
40
  export type ConversationRouteProps = {
39
41
  conversation_id: number
40
42
  chat_group_graph_id?: string
@@ -55,7 +55,7 @@ export function ConversationsScreen({ route }: ConversationsScreenProps) {
55
55
  }
56
56
 
57
57
  const handleGetHelp = useCallback(() => {
58
- navigation.navigate('GetHelp')
58
+ navigation.navigate('GetHelp', { type: 'chat' })
59
59
  }, [navigation])
60
60
 
61
61
  return (
@@ -1,13 +1,16 @@
1
- import { useNavigation } from '@react-navigation/native'
2
- import { NativeStackScreenProps } from '@react-navigation/native-stack'
3
- import { useCallback } from 'react'
1
+ import { StaticScreenProps, useNavigation } from '@react-navigation/native'
2
+ import { useCallback, useMemo } from 'react'
4
3
  import { Linking, StyleSheet, View } from 'react-native'
5
4
  import { Heading, PressableRow, Text, TextInlineButton } from '../components'
6
5
  import { useApiGet, useTheme } from '../hooks'
7
6
  import { useAppName } from '../hooks/use_app_name'
8
7
  import { ResourceObject } from '../types'
9
8
 
10
- type GetHelpScreenProps = NativeStackScreenProps<{}>
9
+ type GetHelpScreenRouteProps = {
10
+ type?: 'chat' | 'general'
11
+ }
12
+
13
+ type GetHelpScreenProps = StaticScreenProps<GetHelpScreenRouteProps>
11
14
 
12
15
  interface OrganizationResource extends ResourceObject {
13
16
  id: number
@@ -37,16 +40,42 @@ const useOrganization = () => {
37
40
  return organization
38
41
  }
39
42
 
40
- export const GetHelpScreen = ({}: GetHelpScreenProps) => {
43
+ interface HelpUrlResource extends ResourceObject {
44
+ id: number
45
+ link: string
46
+ title: string
47
+ product: 'services' | 'churchcenter'
48
+ type: 'chat' | 'general'
49
+ }
50
+
51
+ const useHelpUrls = () => {
52
+ const { data: helpUrls = [] } = useApiGet<HelpUrlResource[]>({
53
+ url: '/help_urls',
54
+ data: {
55
+ fields: {
56
+ HelpUrl: [],
57
+ },
58
+ },
59
+ })
60
+
61
+ return helpUrls
62
+ }
63
+
64
+ export const GetHelpScreen = ({ route }: GetHelpScreenProps) => {
65
+ const { type = 'chat' } = route.params || {}
41
66
  const styles = useStyles()
42
67
  const appName = useAppName()
43
- const isChurchCenter = appName === 'churchcenter'
44
68
  const navigation = useNavigation()
45
69
  const organization = useOrganization()
46
70
  const contactPresent = organization?.contactEmail || organization?.contactPhoneNumber
47
- const getHelpLink = isChurchCenter
48
- ? organization?.churchCenterChatHelpUrl
49
- : organization?.servicesChatHelpUrl
71
+ const helpUrls = useHelpUrls()
72
+ const appSpecificHelpUrls = useMemo(
73
+ () =>
74
+ helpUrls
75
+ .filter(helpUrl => helpUrl.product === appName)
76
+ .filter(helpUrl => helpUrl.type.includes(type)),
77
+ [helpUrls, appName, type]
78
+ )
50
79
 
51
80
  const handleNavigateToBugReport = useCallback(() => {
52
81
  navigation.navigate('BugReport')
@@ -54,15 +83,20 @@ export const GetHelpScreen = ({}: GetHelpScreenProps) => {
54
83
 
55
84
  return (
56
85
  <View style={[styles.container]}>
57
- <Heading variant="h2" style={[styles.heading, styles.headingBottomBorder]}>
58
- How-to articles
59
- </Heading>
60
- <PressableRow
61
- onPress={() => Linking.openURL(getHelpLink || '')}
62
- text="Get help with chat"
63
- isActive={true}
64
- iconPath="general.newWindow"
65
- />
86
+ {appSpecificHelpUrls.length > 0 && (
87
+ <Heading variant="h2" style={[styles.heading, styles.headingBottomBorder]}>
88
+ How-to articles
89
+ </Heading>
90
+ )}
91
+ {appSpecificHelpUrls.map(helpUrl => (
92
+ <PressableRow
93
+ key={helpUrl.id}
94
+ onPress={() => Linking.openURL(helpUrl.link)}
95
+ text={helpUrl.title}
96
+ isActive={true}
97
+ iconPath="general.newWindow"
98
+ />
99
+ ))}
66
100
  {contactPresent && (
67
101
  <Heading variant="h2" style={[styles.heading, styles.headingBottomBorder]}>
68
102
  Contact {organization?.name}
@@ -5,6 +5,7 @@ export const MAX_FONT_SIZE_MULTIPLIER = 1.5
5
5
  export const MAX_FONT_SIZE_MULTIPLIER_LANDMARK = 1.2 // Use in headers, footers, toolbars, etc.
6
6
 
7
7
  export const CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL = 16 // TODO: move to `screens/conversation/utils/styles` when `/screens/conversation` is created
8
+ export const MESSAGE_AUTHOR_AVATAR_COLUMN_WIDTH = 32 // TODO: move to `screens/conversation/utils/styles` when `/screens/conversation` is created
8
9
 
9
10
  export const platformFontWeightMedium = Platform.select({
10
11
  ios: tokens.fontWeightMedium,