@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.
- package/build/components/conversation/attachments/audio_attachment.d.ts +2 -1
- package/build/components/conversation/attachments/audio_attachment.d.ts.map +1 -1
- package/build/components/conversation/attachments/audio_attachment.js +14 -11
- package/build/components/conversation/attachments/audio_attachment.js.map +1 -1
- package/build/components/conversation/attachments/expanded_link.d.ts +4 -2
- package/build/components/conversation/attachments/expanded_link.d.ts.map +1 -1
- package/build/components/conversation/attachments/expanded_link.js +5 -4
- package/build/components/conversation/attachments/expanded_link.js.map +1 -1
- package/build/components/conversation/attachments/generic_file_attachment.d.ts +2 -1
- package/build/components/conversation/attachments/generic_file_attachment.d.ts.map +1 -1
- package/build/components/conversation/attachments/generic_file_attachment.js +5 -4
- package/build/components/conversation/attachments/generic_file_attachment.js.map +1 -1
- package/build/components/conversation/attachments/giphy_attachment.d.ts +2 -1
- package/build/components/conversation/attachments/giphy_attachment.d.ts.map +1 -1
- package/build/components/conversation/attachments/giphy_attachment.js +2 -2
- package/build/components/conversation/attachments/giphy_attachment.js.map +1 -1
- package/build/components/conversation/attachments/image_attachment.d.ts +4 -2
- package/build/components/conversation/attachments/image_attachment.d.ts.map +1 -1
- package/build/components/conversation/attachments/image_attachment.js +5 -4
- package/build/components/conversation/attachments/image_attachment.js.map +1 -1
- package/build/components/conversation/attachments/video_attachment.d.ts +2 -1
- package/build/components/conversation/attachments/video_attachment.d.ts.map +1 -1
- package/build/components/conversation/attachments/video_attachment.js +5 -4
- package/build/components/conversation/attachments/video_attachment.js.map +1 -1
- package/build/components/conversation/message.d.ts.map +1 -1
- package/build/components/conversation/message.js +6 -1
- package/build/components/conversation/message.js.map +1 -1
- package/build/components/conversation/message_attachments.d.ts +1 -0
- package/build/components/conversation/message_attachments.d.ts.map +1 -1
- package/build/components/conversation/message_attachments.js +9 -9
- package/build/components/conversation/message_attachments.js.map +1 -1
- package/build/components/page/error_boundary.d.ts.map +1 -1
- package/build/components/page/error_boundary.js +4 -3
- package/build/components/page/error_boundary.js.map +1 -1
- package/build/components/primitive/actions_form_sheet.d.ts +34 -0
- package/build/components/primitive/actions_form_sheet.d.ts.map +1 -0
- package/build/components/primitive/actions_form_sheet.js +91 -0
- package/build/components/primitive/actions_form_sheet.js.map +1 -0
- package/build/contexts/chat_context.d.ts +7 -6
- package/build/contexts/chat_context.d.ts.map +1 -1
- package/build/contexts/chat_context.js +2 -2
- package/build/contexts/chat_context.js.map +1 -1
- package/build/hooks/use_conversation.d.ts +4 -4
- package/build/hooks/use_suspense_api.d.ts +6 -5
- package/build/hooks/use_suspense_api.d.ts.map +1 -1
- package/build/hooks/use_suspense_api.js +16 -3
- package/build/hooks/use_suspense_api.js.map +1 -1
- package/build/index.d.ts +6 -4
- package/build/index.d.ts.map +1 -1
- package/build/index.js +6 -4
- package/build/index.js.map +1 -1
- package/build/navigation/index.d.ts +5 -0
- package/build/navigation/index.d.ts.map +1 -1
- package/build/navigation/index.js +6 -0
- package/build/navigation/index.js.map +1 -1
- package/build/screens/attachment_actions/attachment_actions_screen.d.ts +9 -0
- package/build/screens/attachment_actions/attachment_actions_screen.d.ts.map +1 -0
- package/build/screens/attachment_actions/attachment_actions_screen.js +39 -0
- package/build/screens/attachment_actions/attachment_actions_screen.js.map +1 -0
- package/build/types/api_primitives.d.ts +1 -1
- package/build/types/api_primitives.d.ts.map +1 -1
- package/build/types/api_primitives.js.map +1 -1
- package/build/utils/index.d.ts +1 -0
- package/build/utils/index.d.ts.map +1 -1
- package/build/utils/index.js +1 -0
- package/build/utils/index.js.map +1 -1
- package/build/utils/native_adapters/configuration.d.ts +8 -5
- package/build/utils/native_adapters/configuration.d.ts.map +1 -1
- package/build/utils/native_adapters/configuration.js +7 -5
- package/build/utils/native_adapters/configuration.js.map +1 -1
- package/build/utils/native_adapters/index.d.ts +1 -0
- package/build/utils/native_adapters/index.d.ts.map +1 -1
- package/build/utils/native_adapters/index.js +1 -0
- package/build/utils/native_adapters/index.js.map +1 -1
- package/build/utils/native_adapters/log.d.ts +15 -0
- package/build/utils/native_adapters/log.d.ts.map +1 -0
- package/build/utils/native_adapters/log.js +21 -0
- package/build/utils/native_adapters/log.js.map +1 -0
- package/build/utils/navigation_constants.d.ts +2 -0
- package/build/utils/navigation_constants.d.ts.map +1 -0
- package/build/utils/navigation_constants.js +11 -0
- package/build/utils/navigation_constants.js.map +1 -0
- package/build/utils/response_error.d.ts +9 -0
- package/build/utils/response_error.d.ts.map +1 -0
- package/build/utils/response_error.js +15 -0
- package/build/utils/response_error.js.map +1 -0
- package/package.json +2 -2
- package/src/components/conversation/attachments/audio_attachment.tsx +25 -17
- package/src/components/conversation/attachments/expanded_link.tsx +17 -4
- package/src/components/conversation/attachments/generic_file_attachment.tsx +9 -3
- package/src/components/conversation/attachments/giphy_attachment.tsx +8 -1
- package/src/components/conversation/attachments/image_attachment.tsx +12 -4
- package/src/components/conversation/attachments/video_attachment.tsx +9 -3
- package/src/components/conversation/message.tsx +12 -1
- package/src/components/conversation/message_attachments.tsx +43 -8
- package/src/components/page/error_boundary.tsx +4 -3
- package/src/components/primitive/actions_form_sheet.tsx +159 -0
- package/src/contexts/chat_context.tsx +12 -8
- package/src/hooks/use_suspense_api.ts +23 -7
- package/src/index.tsx +6 -12
- package/src/navigation/index.tsx +6 -0
- package/src/screens/attachment_actions/attachment_actions_screen.tsx +56 -0
- package/src/types/api_primitives.ts +1 -1
- package/src/utils/index.ts +1 -0
- package/src/utils/native_adapters/configuration.ts +8 -5
- package/src/utils/native_adapters/index.ts +1 -0
- package/src/utils/native_adapters/log.ts +29 -0
- package/src/utils/navigation_constants.ts +12 -0
- package/src/utils/response_error.ts +17 -0
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import {
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
|
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:
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
|
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 {
|
|
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
|
|
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({
|
|
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
|
|
73
|
+
return (
|
|
74
|
+
<ImageAttachment
|
|
75
|
+
attachment={attachment}
|
|
76
|
+
metaProps={metaProps}
|
|
77
|
+
onAttachmentLongPress={onAttachmentLongPress}
|
|
78
|
+
/>
|
|
79
|
+
)
|
|
54
80
|
case 'video':
|
|
55
|
-
return
|
|
81
|
+
return (
|
|
82
|
+
<VideoAttachment attachment={attachment} onAttachmentLongPress={onAttachmentLongPress} />
|
|
83
|
+
)
|
|
56
84
|
case 'audio':
|
|
57
|
-
return
|
|
85
|
+
return (
|
|
86
|
+
<AudioAttachment attachment={attachment} onAttachmentLongPress={onAttachmentLongPress} />
|
|
87
|
+
)
|
|
58
88
|
default:
|
|
59
|
-
return
|
|
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
|
|
54
|
-
return <ResponseErrorView
|
|
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({
|
|
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
|
|
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
|
|
18
|
-
|
|
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(
|
|
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,
|
|
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
|
-
|
|
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]
|
|
65
|
-
|
|
66
|
-
|
|
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
|
|
1
|
+
export { GroupConversations } from './components'
|
|
2
2
|
export { ApiProvider, chatQueryClient } from './contexts/api_provider'
|
|
3
|
-
export
|
|
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
|
|
8
|
-
|
|
9
|
-
export {
|
|
10
|
-
TemporaryDefaultColorsType,
|
|
11
|
-
ChatAdapters,
|
|
12
|
-
ClipboardAdapter,
|
|
13
|
-
Session,
|
|
14
|
-
Uri,
|
|
15
|
-
platformFontWeightBold,
|
|
16
|
-
} from './utils'
|
|
10
|
+
export * from './utils/native_adapters'
|
package/src/navigation/index.tsx
CHANGED
|
@@ -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,
|