@planningcenter/chat-react-native 3.4.1-rc.9 → 3.4.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 (109) hide show
  1. package/build/components/conversation/attachments/audio_attachment.d.ts +2 -1
  2. package/build/components/conversation/attachments/audio_attachment.d.ts.map +1 -1
  3. package/build/components/conversation/attachments/audio_attachment.js +14 -11
  4. package/build/components/conversation/attachments/audio_attachment.js.map +1 -1
  5. package/build/components/conversation/attachments/expanded_link.d.ts +4 -2
  6. package/build/components/conversation/attachments/expanded_link.d.ts.map +1 -1
  7. package/build/components/conversation/attachments/expanded_link.js +5 -4
  8. package/build/components/conversation/attachments/expanded_link.js.map +1 -1
  9. package/build/components/conversation/attachments/generic_file_attachment.d.ts +2 -1
  10. package/build/components/conversation/attachments/generic_file_attachment.d.ts.map +1 -1
  11. package/build/components/conversation/attachments/generic_file_attachment.js +5 -4
  12. package/build/components/conversation/attachments/generic_file_attachment.js.map +1 -1
  13. package/build/components/conversation/attachments/giphy_attachment.d.ts +2 -1
  14. package/build/components/conversation/attachments/giphy_attachment.d.ts.map +1 -1
  15. package/build/components/conversation/attachments/giphy_attachment.js +2 -2
  16. package/build/components/conversation/attachments/giphy_attachment.js.map +1 -1
  17. package/build/components/conversation/attachments/image_attachment.d.ts +4 -2
  18. package/build/components/conversation/attachments/image_attachment.d.ts.map +1 -1
  19. package/build/components/conversation/attachments/image_attachment.js +5 -4
  20. package/build/components/conversation/attachments/image_attachment.js.map +1 -1
  21. package/build/components/conversation/attachments/video_attachment.d.ts +2 -1
  22. package/build/components/conversation/attachments/video_attachment.d.ts.map +1 -1
  23. package/build/components/conversation/attachments/video_attachment.js +5 -4
  24. package/build/components/conversation/attachments/video_attachment.js.map +1 -1
  25. package/build/components/conversation/message.d.ts.map +1 -1
  26. package/build/components/conversation/message.js +6 -1
  27. package/build/components/conversation/message.js.map +1 -1
  28. package/build/components/conversation/message_attachments.d.ts +1 -0
  29. package/build/components/conversation/message_attachments.d.ts.map +1 -1
  30. package/build/components/conversation/message_attachments.js +9 -9
  31. package/build/components/conversation/message_attachments.js.map +1 -1
  32. package/build/components/page/error_boundary.d.ts.map +1 -1
  33. package/build/components/page/error_boundary.js +4 -3
  34. package/build/components/page/error_boundary.js.map +1 -1
  35. package/build/components/primitive/actions_form_sheet.d.ts +34 -0
  36. package/build/components/primitive/actions_form_sheet.d.ts.map +1 -0
  37. package/build/components/primitive/actions_form_sheet.js +91 -0
  38. package/build/components/primitive/actions_form_sheet.js.map +1 -0
  39. package/build/contexts/chat_context.d.ts +7 -6
  40. package/build/contexts/chat_context.d.ts.map +1 -1
  41. package/build/contexts/chat_context.js +2 -2
  42. package/build/contexts/chat_context.js.map +1 -1
  43. package/build/hooks/use_conversation.d.ts +4 -4
  44. package/build/hooks/use_suspense_api.d.ts +6 -5
  45. package/build/hooks/use_suspense_api.d.ts.map +1 -1
  46. package/build/hooks/use_suspense_api.js +16 -3
  47. package/build/hooks/use_suspense_api.js.map +1 -1
  48. package/build/index.d.ts +6 -4
  49. package/build/index.d.ts.map +1 -1
  50. package/build/index.js +6 -4
  51. package/build/index.js.map +1 -1
  52. package/build/navigation/index.d.ts +5 -0
  53. package/build/navigation/index.d.ts.map +1 -1
  54. package/build/navigation/index.js +6 -0
  55. package/build/navigation/index.js.map +1 -1
  56. package/build/screens/attachment_actions/attachment_actions_screen.d.ts +9 -0
  57. package/build/screens/attachment_actions/attachment_actions_screen.d.ts.map +1 -0
  58. package/build/screens/attachment_actions/attachment_actions_screen.js +39 -0
  59. package/build/screens/attachment_actions/attachment_actions_screen.js.map +1 -0
  60. package/build/types/api_primitives.d.ts +1 -1
  61. package/build/types/api_primitives.d.ts.map +1 -1
  62. package/build/types/api_primitives.js.map +1 -1
  63. package/build/utils/index.d.ts +1 -0
  64. package/build/utils/index.d.ts.map +1 -1
  65. package/build/utils/index.js +1 -0
  66. package/build/utils/index.js.map +1 -1
  67. package/build/utils/native_adapters/configuration.d.ts +8 -5
  68. package/build/utils/native_adapters/configuration.d.ts.map +1 -1
  69. package/build/utils/native_adapters/configuration.js +7 -5
  70. package/build/utils/native_adapters/configuration.js.map +1 -1
  71. package/build/utils/native_adapters/index.d.ts +1 -0
  72. package/build/utils/native_adapters/index.d.ts.map +1 -1
  73. package/build/utils/native_adapters/index.js +1 -0
  74. package/build/utils/native_adapters/index.js.map +1 -1
  75. package/build/utils/native_adapters/log.d.ts +15 -0
  76. package/build/utils/native_adapters/log.d.ts.map +1 -0
  77. package/build/utils/native_adapters/log.js +21 -0
  78. package/build/utils/native_adapters/log.js.map +1 -0
  79. package/build/utils/navigation_constants.d.ts +2 -0
  80. package/build/utils/navigation_constants.d.ts.map +1 -0
  81. package/build/utils/navigation_constants.js +11 -0
  82. package/build/utils/navigation_constants.js.map +1 -0
  83. package/build/utils/response_error.d.ts +9 -0
  84. package/build/utils/response_error.d.ts.map +1 -0
  85. package/build/utils/response_error.js +15 -0
  86. package/build/utils/response_error.js.map +1 -0
  87. package/package.json +2 -2
  88. package/src/components/conversation/attachments/audio_attachment.tsx +25 -17
  89. package/src/components/conversation/attachments/expanded_link.tsx +17 -4
  90. package/src/components/conversation/attachments/generic_file_attachment.tsx +9 -3
  91. package/src/components/conversation/attachments/giphy_attachment.tsx +8 -1
  92. package/src/components/conversation/attachments/image_attachment.tsx +12 -4
  93. package/src/components/conversation/attachments/video_attachment.tsx +9 -3
  94. package/src/components/conversation/message.tsx +12 -1
  95. package/src/components/conversation/message_attachments.tsx +43 -8
  96. package/src/components/page/error_boundary.tsx +4 -3
  97. package/src/components/primitive/actions_form_sheet.tsx +159 -0
  98. package/src/contexts/chat_context.tsx +12 -8
  99. package/src/hooks/use_suspense_api.ts +23 -7
  100. package/src/index.tsx +6 -12
  101. package/src/navigation/index.tsx +6 -0
  102. package/src/screens/attachment_actions/attachment_actions_screen.tsx +56 -0
  103. package/src/types/api_primitives.ts +1 -1
  104. package/src/utils/index.ts +1 -0
  105. package/src/utils/native_adapters/configuration.ts +8 -5
  106. package/src/utils/native_adapters/index.ts +1 -0
  107. package/src/utils/native_adapters/log.ts +29 -0
  108. package/src/utils/navigation_constants.ts +12 -0
  109. package/src/utils/response_error.ts +17 -0
@@ -1,15 +1,18 @@
1
1
  import React from 'react'
2
- import { Pressable, StyleSheet, View } from 'react-native'
2
+ import { StyleSheet, View } from 'react-native'
3
3
  import { DenormalizedMessageAttachmentResource } from '../../../types/resources/denormalized_attachment_resource'
4
4
  import { AttachmentCard, AttachmentCardTitle } from './attachment_card'
5
5
  import { Icon } from '../../display'
6
6
  import { DownloadAttachmentButton } from './download_attachment_button'
7
7
  import { useTheme } from '../../../hooks'
8
+ import { PlatformPressable } from '@react-navigation/elements'
8
9
 
9
10
  export function GenericFileAttachment({
10
11
  attachment,
12
+ onAttachmentLongPress,
11
13
  }: {
12
14
  attachment: DenormalizedMessageAttachmentResource
15
+ onAttachmentLongPress: (attachment: DenormalizedMessageAttachmentResource) => void
13
16
  }) {
14
17
  const styles = useStyles()
15
18
  const { url, filename, contentType } = attachment.attributes
@@ -17,7 +20,10 @@ export function GenericFileAttachment({
17
20
  const fileTypeLabel = contentType === 'application/pdf' ? 'PDF' : 'file'
18
21
 
19
22
  return (
20
- <Pressable>
23
+ <PlatformPressable
24
+ onLongPress={() => onAttachmentLongPress(attachment)}
25
+ accessibilityHint="Long press for more options"
26
+ >
21
27
  <AttachmentCard>
22
28
  <View style={styles.container}>
23
29
  <View style={styles.stack}>
@@ -31,7 +37,7 @@ export function GenericFileAttachment({
31
37
  />
32
38
  </View>
33
39
  </AttachmentCard>
34
- </Pressable>
40
+ </PlatformPressable>
35
41
  )
36
42
  }
37
43
 
@@ -6,8 +6,10 @@ import { DenormalizedGiphyAttachmentResource } from '../../../types/resources/de
6
6
 
7
7
  export function GiphyAttachment({
8
8
  attachment,
9
+ onAttachmentLongPress,
9
10
  }: {
10
11
  attachment: DenormalizedGiphyAttachmentResource
12
+ onAttachmentLongPress: (attachment: DenormalizedGiphyAttachmentResource) => void
11
13
  }) {
12
14
  const styles = useStyles()
13
15
  const { title, titleLink, giphy } = attachment
@@ -21,7 +23,12 @@ export function GiphyAttachment({
21
23
  }
22
24
 
23
25
  return (
24
- <Pressable onPress={handlePress} style={styles.container}>
26
+ <Pressable
27
+ onPress={handlePress}
28
+ style={styles.container}
29
+ onLongPress={() => onAttachmentLongPress(attachment)}
30
+ accessibilityHint="Long press for more options"
31
+ >
25
32
  <Image
26
33
  source={{ uri: url }}
27
34
  style={[styles.image, { width, height }]}
@@ -2,7 +2,6 @@ import React, { useCallback, useMemo, useState } from 'react'
2
2
  import {
3
3
  StyleSheet,
4
4
  Modal,
5
- Pressable,
6
5
  useWindowDimensions,
7
6
  SafeAreaView,
8
7
  View,
@@ -26,6 +25,8 @@ import { tokens } from '../../../vendor/tapestry/tokens'
26
25
  import { IconButton, Image, Heading, Text } from '../../display'
27
26
  import colorFunction from 'color'
28
27
  import { formatDatePreview } from '../../../utils/date'
28
+ import { DenormalizedMessageAttachmentResource } from '../../../types/resources/denormalized_attachment_resource'
29
+ import { PlatformPressable } from '@react-navigation/elements'
29
30
 
30
31
  const PAN_THRESHOLD_PX = 300
31
32
 
@@ -37,9 +38,11 @@ export type MetaProps = {
37
38
  export function ImageAttachment({
38
39
  attachment,
39
40
  metaProps,
41
+ onAttachmentLongPress,
40
42
  }: {
41
- attachment: any
43
+ attachment: DenormalizedMessageAttachmentResource
42
44
  metaProps: MetaProps
45
+ onAttachmentLongPress: (attachment: DenormalizedMessageAttachmentResource) => void
43
46
  }) {
44
47
  const { attributes } = attachment
45
48
  const { url, urlMedium, filename, metadata = {} } = attributes
@@ -77,14 +80,19 @@ export function ImageAttachment({
77
80
 
78
81
  return (
79
82
  <>
80
- <Pressable style={styles.container} onPress={() => setVisible(true)}>
83
+ <PlatformPressable
84
+ style={styles.container}
85
+ onPress={() => setVisible(true)}
86
+ onLongPress={() => onAttachmentLongPress(attachment)}
87
+ accessibilityHint="Long press for more options"
88
+ >
81
89
  <Image
82
90
  source={{ uri: urlMedium || url }}
83
91
  style={{ borderRadius: 8 }}
84
92
  wrapperStyle={styles.imageWrapper}
85
93
  alt={filename}
86
94
  />
87
- </Pressable>
95
+ </PlatformPressable>
88
96
  <LightboxModal
89
97
  visible={visible}
90
98
  handleCloseModal={handleCloseModal}
@@ -1,14 +1,17 @@
1
1
  import React, { useRef, useState } from 'react'
2
- import { View, StyleSheet, Pressable } from 'react-native'
2
+ import { View, StyleSheet } from 'react-native'
3
3
  import { DenormalizedMessageAttachmentResource } from '../../../types/resources/denormalized_attachment_resource'
4
4
  import { MESSAGE_ATTACHMENT_WIDTH_SINGLE } from './constants'
5
5
  import { IconButton } from '../../display'
6
6
  import { Video, VideoPlayerHandle } from '../../../utils/native_adapters'
7
+ import { PlatformPressable } from '@react-navigation/elements'
7
8
 
8
9
  export function VideoAttachment({
9
10
  attachment,
11
+ onAttachmentLongPress,
10
12
  }: {
11
13
  attachment: DenormalizedMessageAttachmentResource
14
+ onAttachmentLongPress: (attachment: DenormalizedMessageAttachmentResource) => void
12
15
  }) {
13
16
  const { attributes } = attachment
14
17
  const { width = MESSAGE_ATTACHMENT_WIDTH_SINGLE, height = MESSAGE_ATTACHMENT_WIDTH_SINGLE } =
@@ -35,7 +38,10 @@ export function VideoAttachment({
35
38
 
36
39
  return (
37
40
  <View style={styles.container} ref={viewRef}>
38
- <Pressable onPress={openVideo}>
41
+ <PlatformPressable
42
+ onLongPress={() => onAttachmentLongPress(attachment)}
43
+ accessibilityHint="Long press for more options"
44
+ >
39
45
  <Video.Player
40
46
  ref={videoRef}
41
47
  source={{ uri: url }}
@@ -54,7 +60,7 @@ export function VideoAttachment({
54
60
  />
55
61
  </View>
56
62
  )}
57
- </Pressable>
63
+ </PlatformPressable>
58
64
  </View>
59
65
  )
60
66
  }
@@ -11,6 +11,7 @@ import { ReactionCountResource } from '../../types/resources/reaction'
11
11
  import { MessageAttachments } from './message_attachments'
12
12
  import ErrorBoundary from '../page/error_boundary'
13
13
  import { MessageMarkdown } from './message_markdown'
14
+ import { DenormalizedAttachmentResource } from '../../types/resources/denormalized_attachment_resource'
14
15
 
15
16
  /** Message
16
17
  * Component for display of a message within a conversation list
@@ -33,6 +34,12 @@ export function Message(props: MessageResource & { conversation_id: number }) {
33
34
  })
34
35
  }
35
36
 
37
+ const handleAttachmentLongPress = (attachment: DenormalizedAttachmentResource) => {
38
+ navigation.navigate('AttachmentActions', {
39
+ attachment,
40
+ })
41
+ }
42
+
36
43
  const metaProps = {
37
44
  authorName: props.author.name,
38
45
  createdAt: props.createdAt,
@@ -49,7 +56,11 @@ export function Message(props: MessageResource & { conversation_id: number }) {
49
56
  {!props.mine && <Text variant="tertiary">{props.author.name}</Text>}
50
57
  <PlatformPressable style={styles.messageBubble} onLongPress={handleMessagePress}>
51
58
  <ErrorBoundary>
52
- <MessageAttachments attachments={props.attachments} metaProps={metaProps} />
59
+ <MessageAttachments
60
+ attachments={props.attachments}
61
+ metaProps={metaProps}
62
+ onAttachmentLongPress={handleAttachmentLongPress}
63
+ />
53
64
  </ErrorBoundary>
54
65
  {text && (
55
66
  <View style={styles.messageText}>
@@ -1,6 +1,9 @@
1
1
  import React from 'react'
2
2
  import { View, StyleSheet } from 'react-native'
3
- import { DenormalizedAttachmentResource } from '../../types/resources/denormalized_attachment_resource'
3
+ import {
4
+ DenormalizedAttachmentResource,
5
+ DenormalizedMessageAttachmentResource,
6
+ } from '../../types/resources/denormalized_attachment_resource'
4
7
  import { AudioAttachment } from './attachments/audio_attachment'
5
8
  import { VideoAttachment } from './attachments/video_attachment'
6
9
  import { GiphyAttachment } from './attachments/giphy_attachment'
@@ -11,9 +14,10 @@ import { ImageAttachment, type MetaProps } from './attachments/image_attachment'
11
14
  export function MessageAttachments(props: {
12
15
  attachments: DenormalizedAttachmentResource[]
13
16
  metaProps: MetaProps
17
+ onAttachmentLongPress: (attachment: DenormalizedAttachmentResource) => void
14
18
  }) {
15
19
  const styles = useStyles()
16
- const { attachments, metaProps } = props
20
+ const { attachments, metaProps, onAttachmentLongPress } = props
17
21
  if (!attachments || attachments.length === 0) return null
18
22
  return (
19
23
  <View style={styles.attachmentsContainer}>
@@ -25,6 +29,7 @@ export function MessageAttachments(props: {
25
29
  key={attachment.id}
26
30
  attachment={attachment}
27
31
  metaProps={metaProps}
32
+ onAttachmentLongPress={onAttachmentLongPress}
28
33
  />
29
34
  )
30
35
  case 'giphy':
@@ -32,10 +37,17 @@ export function MessageAttachments(props: {
32
37
  <GiphyAttachment
33
38
  key={attachment.id || attachment.titleLink}
34
39
  attachment={attachment}
40
+ onAttachmentLongPress={onAttachmentLongPress}
35
41
  />
36
42
  )
37
43
  case 'ExpandedLink':
38
- return <ExpandedLink key={attachment.id} attachment={attachment} />
44
+ return (
45
+ <ExpandedLink
46
+ key={attachment.id}
47
+ attachment={attachment}
48
+ onAttachmentLongPress={onAttachmentLongPress}
49
+ />
50
+ )
39
51
  default:
40
52
  return null
41
53
  }
@@ -44,19 +56,42 @@ export function MessageAttachments(props: {
44
56
  )
45
57
  }
46
58
 
47
- function MessageAttachment({ attachment, metaProps }: { attachment: any; metaProps: MetaProps }) {
59
+ function MessageAttachment({
60
+ attachment,
61
+ metaProps,
62
+ onAttachmentLongPress,
63
+ }: {
64
+ attachment: DenormalizedMessageAttachmentResource
65
+ metaProps: MetaProps
66
+ onAttachmentLongPress: (attachment: DenormalizedMessageAttachmentResource) => void
67
+ }) {
48
68
  const { attributes } = attachment
49
69
  const contentType = attributes?.contentType
50
70
  const basicType = contentType ? contentType.split('/')[0] : ''
51
71
  switch (basicType) {
52
72
  case 'image':
53
- return <ImageAttachment attachment={attachment} metaProps={metaProps} />
73
+ return (
74
+ <ImageAttachment
75
+ attachment={attachment}
76
+ metaProps={metaProps}
77
+ onAttachmentLongPress={onAttachmentLongPress}
78
+ />
79
+ )
54
80
  case 'video':
55
- return <VideoAttachment attachment={attachment} />
81
+ return (
82
+ <VideoAttachment attachment={attachment} onAttachmentLongPress={onAttachmentLongPress} />
83
+ )
56
84
  case 'audio':
57
- return <AudioAttachment attachment={attachment} />
85
+ return (
86
+ <AudioAttachment attachment={attachment} onAttachmentLongPress={onAttachmentLongPress} />
87
+ )
58
88
  default:
59
- return <GenericFileAttachment attachment={attachment} />
89
+ return (
90
+ <GenericFileAttachment
91
+ attachment={attachment}
92
+ onAttachmentLongPress={onAttachmentLongPress}
93
+ />
94
+ )
60
95
  }
61
96
  }
62
97
 
@@ -5,6 +5,7 @@ import { StyleSheet, View } from 'react-native'
5
5
  import { useSafeAreaInsets } from 'react-native-safe-area-context'
6
6
  import { Button, Heading, Icon, Text } from '../display'
7
7
  import { useTheme } from '../../hooks'
8
+ import { ResponseError } from '../../utils/response_error'
8
9
 
9
10
  type ErrorBoundaryState = {
10
11
  error: Response | Error | null
@@ -50,14 +51,14 @@ function ErrorView({ error, onReset }: { error: Error | Response; onReset: () =>
50
51
  }
51
52
  }, [reset, onReset])
52
53
 
53
- if (error instanceof Response) {
54
- return <ResponseErrorView error={error} onReset={onReset} />
54
+ if (error instanceof ResponseError) {
55
+ return <ResponseErrorView response={error.response} onReset={onReset} />
55
56
  }
56
57
 
57
58
  return <ErrorContent heading={'Oops!'} body={'Something unexpected happened.'} />
58
59
  }
59
60
 
60
- function ResponseErrorView({ error: response }: { error: Response; onReset: () => void }) {
61
+ function ResponseErrorView({ response }: { response: Response; onReset: () => void }) {
61
62
  const { status } = response
62
63
  const heading = useMemo(() => {
63
64
  switch (status) {
@@ -0,0 +1,159 @@
1
+ import { NativeStackNavigationOptions } from '@react-navigation/native-stack'
2
+ import React, { ReactNode } from 'react'
3
+ import { AccessibilityRole, StyleSheet, View } from 'react-native'
4
+ import { useSafeAreaInsets } from 'react-native-safe-area-context'
5
+ import { useTheme } from '../../hooks'
6
+ import { PLATFORM_SHEET_ALLOWED_DETENTS_FIT_TO_CONTENTS } from '../../utils/navigation_constants'
7
+ import { PlatformPressable } from '@react-navigation/elements'
8
+ import { Icon, IconString, Text } from '../display'
9
+
10
+ // =================================
11
+ // ====== Exports ==================
12
+ // =================================
13
+
14
+ export const BaseActionsFormSheetOptions: NativeStackNavigationOptions = {
15
+ presentation: 'formSheet',
16
+ headerShown: false,
17
+ sheetAllowedDetents: PLATFORM_SHEET_ALLOWED_DETENTS_FIT_TO_CONTENTS,
18
+ }
19
+
20
+ const ActionsFormSheet = {
21
+ Root: ActionsFormSheetRoot,
22
+ Action: ActionsFormSheetAction,
23
+ } as const
24
+
25
+ type ActionFormSheetComponents = {
26
+ Root: React.FC<ActionsFormSheetRootProps>
27
+ Action: React.FC<ActionsFormSheetActionProps>
28
+ }
29
+
30
+ export default ActionsFormSheet as ActionFormSheetComponents
31
+ export type { ActionsFormSheetRootProps, ActionsFormSheetActionProps }
32
+
33
+ // ====================================
34
+ // ====== ActionsFormSheetRoot ========
35
+ // ====================================
36
+
37
+ interface ActionsFormSheetRootProps {
38
+ children: ReactNode
39
+ }
40
+
41
+ export function ActionsFormSheetRoot({ children }: ActionsFormSheetRootProps) {
42
+ const styles = useStyles()
43
+
44
+ return (
45
+ <View style={styles.container}>
46
+ <View style={styles.gestureBar} />
47
+ <View style={styles.actions}>{children}</View>
48
+ </View>
49
+ )
50
+ }
51
+
52
+ ActionsFormSheetRoot.displayName = 'ActionsFormSheet.Root'
53
+
54
+ // ====================================
55
+ // ====== ActionsFormSheetAction ======
56
+ // ====================================
57
+
58
+ const ACTION_FORM_SHEET_ACTION_APPEARANCES = {
59
+ neutral: 'neutral',
60
+ danger: 'danger',
61
+ } as const
62
+
63
+ type ActionsFormSheetActionAppearanceUnion =
64
+ (typeof ACTION_FORM_SHEET_ACTION_APPEARANCES)[keyof typeof ACTION_FORM_SHEET_ACTION_APPEARANCES]
65
+
66
+ interface ActionsFormSheetActionProps {
67
+ title: string
68
+ iconName: IconString
69
+ onPress: () => void
70
+ accessibilityLabel?: string
71
+ accessibilityHint?: string
72
+ accessibilityRole?: AccessibilityRole
73
+ appearance?: ActionsFormSheetActionAppearanceUnion
74
+ }
75
+
76
+ function ActionsFormSheetAction({
77
+ title,
78
+ iconName,
79
+ onPress,
80
+ accessibilityLabel,
81
+ accessibilityHint,
82
+ accessibilityRole = 'button',
83
+ appearance = 'neutral',
84
+ }: ActionsFormSheetActionProps) {
85
+ const styles = useStyles({ appearance })
86
+
87
+ return (
88
+ <PlatformPressable
89
+ onPress={onPress}
90
+ accessibilityLabel={accessibilityLabel}
91
+ accessibilityHint={accessibilityHint}
92
+ accessibilityRole={accessibilityRole}
93
+ style={styles.actionPressable}
94
+ >
95
+ <Icon name={iconName} size={16} accessibilityElementsHidden style={styles.actionIcon} />
96
+ <Text style={styles.actionTitle}>{title}</Text>
97
+ </PlatformPressable>
98
+ )
99
+ }
100
+
101
+ ActionsFormSheetAction.displayName = 'ActionsFormSheet.Action'
102
+
103
+ // ==================================
104
+ // ====== Styles ====================
105
+ // ==================================
106
+
107
+ interface Styles {
108
+ appearance?: ActionsFormSheetActionAppearanceUnion
109
+ }
110
+
111
+ const useStyles = ({ appearance = 'neutral' }: Styles = {}) => {
112
+ const { colors } = useTheme()
113
+ const { bottom } = useSafeAreaInsets()
114
+
115
+ const appearanceColorsMap = {
116
+ neutral: {
117
+ iconColor: colors.iconColorDefaultPrimary,
118
+ textColor: colors.textColorDefaultPrimary,
119
+ },
120
+ danger: {
121
+ iconColor: colors.fillColorStatusErrorMedium,
122
+ textColor: colors.fillColorStatusErrorMedium,
123
+ },
124
+ }
125
+
126
+ return StyleSheet.create({
127
+ container: {
128
+ flex: 1,
129
+ justifyContent: 'flex-start',
130
+ paddingTop: 12,
131
+ paddingBottom: bottom,
132
+ width: '100%',
133
+ backgroundColor: colors.fillColorNeutral100Inverted,
134
+ },
135
+ gestureBar: {
136
+ width: 32,
137
+ height: 4,
138
+ backgroundColor: colors.fillColorNeutral050Base,
139
+ borderRadius: 100,
140
+ alignSelf: 'center',
141
+ },
142
+ actions: {
143
+ paddingTop: 20,
144
+ },
145
+ actionPressable: {
146
+ flexDirection: 'row',
147
+ alignItems: 'center',
148
+ paddingVertical: 12,
149
+ paddingHorizontal: 16,
150
+ gap: 8,
151
+ },
152
+ actionIcon: {
153
+ color: appearanceColorsMap[appearance].iconColor,
154
+ },
155
+ actionTitle: {
156
+ color: appearanceColorsMap[appearance].textColor,
157
+ },
158
+ })
159
+ }
@@ -2,20 +2,21 @@ import { merge } from 'lodash'
2
2
  import React, { createContext, useMemo } from 'react'
3
3
  import { ColorSchemeName, useColorScheme } from 'react-native'
4
4
  import { DeepPartial } from '../types'
5
- import { ENV, PartialToken, ResponseError, Session } from '../utils'
5
+ import { ENV, OauthType, PartialToken, ResponseError, Session } from '../utils'
6
6
  import { ChatTheme, defaultTheme, DefaultTheme } from '../utils/theme'
7
7
 
8
- export type ChatContextValue = {
8
+ export interface ChatProviderProps {
9
9
  env?: ENV
10
10
  giphyApiKey?: string
11
11
  onUnauthorizedResponse: (_response: ResponseError) => void
12
- session: Session
13
- theme: ChatTheme
14
12
  token?: PartialToken
13
+ tokenType?: OauthType
14
+ theme: CreateChatThemeProps
15
15
  }
16
16
 
17
- export interface ChatProviderProps extends Omit<ChatContextValue, 'client' | 'theme' | 'session'> {
18
- theme: CreateChatThemeProps
17
+ export interface ChatContextValue extends Omit<ChatProviderProps, 'theme'> {
18
+ session: Session
19
+ theme: ChatTheme
19
20
  }
20
21
 
21
22
  export const ChatContext = createContext<ChatContextValue>({
@@ -28,9 +29,12 @@ export const ChatContext = createContext<ChatContextValue>({
28
29
  })
29
30
 
30
31
  export function ChatProvider({ children, value }: { children: any; value: ChatProviderProps }) {
31
- const { env, token, onUnauthorizedResponse, giphyApiKey } = value
32
+ const { env, token, onUnauthorizedResponse, giphyApiKey, tokenType } = value
32
33
  const theme = useCreateChatTheme(value.theme || {})
33
- const session = useMemo(() => new Session({ token, env }), [env, token])
34
+ const session = useMemo(
35
+ () => new Session({ token, env, type: tokenType }),
36
+ [env, token, tokenType]
37
+ )
34
38
 
35
39
  const contextValue: ChatContextValue = {
36
40
  env,
@@ -7,6 +7,9 @@ import {
7
7
  import { ApiCollection, ApiResource, ResourceObject } from '../types'
8
8
  import { GetRequest, RequestData } from '../utils/client/types'
9
9
  import { App, useApiClient } from './use_api_client'
10
+ import { Log } from '../utils'
11
+ import { ApiError } from '../types/api_primitives'
12
+ import { ResponseError } from '../utils/response_error'
10
13
 
11
14
  interface SuspenseGetOptions extends GetRequest {
12
15
  app?: App
@@ -18,12 +21,13 @@ export const useSuspenseGet = <T extends ResourceObject | ResourceObject[]>(
18
21
  type Resource = ApiResource<T>
19
22
  const apiClient = useApiClient()
20
23
 
21
- const { data, ...query } = useSuspenseQuery<Resource, Response>({
24
+ const { data, ...query } = useSuspenseQuery<Resource, ApiError>({
22
25
  queryKey: getRequestQueryKey(args),
23
26
  queryFn: ({ queryKey }) => {
24
27
  const [url, d, headers, app = 'chat'] = queryKey as RequestQueryKey
25
-
26
- return apiClient[app].get({ url, data: d, headers }) as Promise<Resource>
28
+ return apiClient[app]
29
+ .get({ url, data: d, headers })
30
+ .catch(throwResponseError) as Promise<Resource>
27
31
  },
28
32
  })
29
33
 
@@ -54,6 +58,8 @@ export const useSuspensePaginator = <T extends ResourceObject>(
54
58
  >({
55
59
  queryKey: getRequestQueryKey(args),
56
60
  queryFn: ({ pageParam }) => {
61
+ Log.info('useSuspenseGet', { pageParam })
62
+
57
63
  const pageParmWhere = pageParam?.where || {}
58
64
  const argsWhere = args.data.where || {}
59
65
  const where = { ...argsWhere, ...pageParmWhere }
@@ -61,10 +67,12 @@ export const useSuspensePaginator = <T extends ResourceObject>(
61
67
  const offset = pageParam?.offset || args.data.offset
62
68
  const data = { ...args.data, where, offset }
63
69
 
64
- return apiClient[app].get({
65
- url: args.url,
66
- data,
67
- })
70
+ return apiClient[app]
71
+ .get<ApiCollection<T>>({
72
+ url: args.url,
73
+ data,
74
+ })
75
+ .catch(throwResponseError)
68
76
  },
69
77
  initialPageParam: {} as Partial<RequestData>,
70
78
  getNextPageParam: lastPage => {
@@ -84,6 +92,14 @@ export const useSuspensePaginator = <T extends ResourceObject>(
84
92
  return { ...query, data }
85
93
  }
86
94
 
95
+ const throwResponseError = (error: unknown) => {
96
+ if (error instanceof Response) {
97
+ throw new ResponseError(error as ApiError)
98
+ }
99
+
100
+ return Promise.reject(error)
101
+ }
102
+
87
103
  export type RequestQueryKey = [
88
104
  SuspenseGetOptions['url'],
89
105
  SuspenseGetOptions['data'],
package/src/index.tsx CHANGED
@@ -1,16 +1,10 @@
1
- export * from './contexts/chat_context'
1
+ export { GroupConversations } from './components'
2
2
  export { ApiProvider, chatQueryClient } from './contexts/api_provider'
3
- export { DesignSystemScreen } from './screens'
3
+ export * from './contexts/chat_context'
4
4
  export * from './navigation'
5
+ export { ScreenLayout } from './navigation/screenLayout'
6
+ export { DesignSystemScreen } from './screens'
5
7
  export * from './types'
8
+ export { platformFontWeightBold, Session, TemporaryDefaultColorsType, Uri } from './utils'
6
9
  export * from './utils/client'
7
- export { GroupConversations } from './components'
8
-
9
- export {
10
- TemporaryDefaultColorsType,
11
- ChatAdapters,
12
- ClipboardAdapter,
13
- Session,
14
- Uri,
15
- platformFontWeightBold,
16
- } from './utils'
10
+ export * from './utils/native_adapters'
@@ -31,6 +31,8 @@ import {
31
31
  import { ConversationFiltersParams } from '../screens/conversation_filters/screen_props'
32
32
  import { ConversationSelectGroupRecipientsScreen } from '../screens/conversation_select_recipients/conversation_select_group_recipients_screen'
33
33
  import { ConversationSelectTeamsILeadRecipientsScreen } from '../screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen'
34
+ import { AttachmentActionsScreenOptions } from '../screens/attachment_actions/attachment_actions_screen'
35
+ import { AttachmentActionsScreen } from '../screens/attachment_actions/attachment_actions_screen'
34
36
 
35
37
  export const NewConversationStack = createNativeStackNavigator({
36
38
  initialRouteName: 'ConversationSelectRecipients',
@@ -169,6 +171,10 @@ export const ChatStack = createNativeStackNavigator({
169
171
  // Something about sheetAllowedDetents declared inline breaks TS
170
172
  options: MessageActionsScreenOptions,
171
173
  },
174
+ AttachmentActions: {
175
+ screen: AttachmentActionsScreen,
176
+ options: AttachmentActionsScreenOptions,
177
+ },
172
178
  Reactions: {
173
179
  screen: ReactionsScreen,
174
180
  options: ReactionsScreenOptions,