@planningcenter/chat-react-native 3.42.2-qa-staging.2 → 3.42.2-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/components/conversations/conversation_preview.d.ts.map +1 -1
- package/build/components/conversations/conversation_preview.js +12 -46
- package/build/components/conversations/conversation_preview.js.map +1 -1
- package/build/components/conversations/unread_count_badge.d.ts +1 -2
- package/build/components/conversations/unread_count_badge.d.ts.map +1 -1
- package/build/components/conversations/unread_count_badge.js +4 -5
- package/build/components/conversations/unread_count_badge.js.map +1 -1
- package/build/hooks/use_conversation.d.ts.map +1 -1
- package/build/hooks/use_conversation.js +0 -2
- package/build/hooks/use_conversation.js.map +1 -1
- package/build/hooks/use_conversation_jolt_events.d.ts.map +1 -1
- package/build/hooks/use_conversation_jolt_events.js +19 -4
- package/build/hooks/use_conversation_jolt_events.js.map +1 -1
- package/build/hooks/use_conversation_messages.d.ts +1 -0
- package/build/hooks/use_conversation_messages.d.ts.map +1 -1
- package/build/hooks/use_conversation_messages.js +2 -2
- package/build/hooks/use_conversation_messages.js.map +1 -1
- package/build/hooks/use_conversations_actions.d.ts.map +1 -1
- package/build/hooks/use_conversations_actions.js +1 -3
- package/build/hooks/use_conversations_actions.js.map +1 -1
- package/build/hooks/use_mark_latest_message_read.d.ts.map +1 -1
- package/build/hooks/use_mark_latest_message_read.js +4 -3
- package/build/hooks/use_mark_latest_message_read.js.map +1 -1
- package/build/hooks/use_suspense_api.d.ts.map +1 -1
- package/build/hooks/use_suspense_api.js +7 -1
- package/build/hooks/use_suspense_api.js.map +1 -1
- package/build/screens/conversation_screen.d.ts.map +1 -1
- package/build/screens/conversation_screen.js +2 -2
- package/build/screens/conversation_screen.js.map +1 -1
- package/build/screens/conversations/components/list_header_component.js +1 -1
- package/build/screens/conversations/components/list_header_component.js.map +1 -1
- package/build/types/resources/conversation.d.ts +1 -10
- package/build/types/resources/conversation.d.ts.map +1 -1
- package/build/types/resources/conversation.js.map +1 -1
- package/build/utils/client/client.d.ts.map +1 -1
- package/build/utils/client/client.js +1 -2
- package/build/utils/client/client.js.map +1 -1
- package/build/utils/request/conversation.d.ts.map +1 -1
- package/build/utils/request/conversation.js +0 -2
- package/build/utils/request/conversation.js.map +1 -1
- package/build/utils/response_error.d.ts +0 -1
- package/build/utils/response_error.d.ts.map +1 -1
- package/build/utils/response_error.js +1 -10
- package/build/utils/response_error.js.map +1 -1
- package/package.json +3 -3
- package/src/components/conversations/conversation_preview.tsx +23 -58
- package/src/components/conversations/unread_count_badge.tsx +4 -13
- package/src/hooks/use_conversation.ts +0 -2
- package/src/hooks/use_conversation_jolt_events.ts +19 -3
- package/src/hooks/use_conversation_messages.ts +6 -5
- package/src/hooks/use_conversations_actions.ts +1 -3
- package/src/hooks/use_mark_latest_message_read.ts +4 -3
- package/src/hooks/use_suspense_api.ts +9 -1
- package/src/screens/conversation_screen.tsx +4 -2
- package/src/screens/conversations/components/list_header_component.tsx +6 -6
- package/src/types/resources/conversation.ts +1 -11
- package/src/utils/client/client.ts +1 -2
- package/src/utils/request/conversation.ts +0 -2
- package/src/utils/response_error.ts +1 -12
- package/src/utils/__tests__/response_error.test.ts +0 -40
|
@@ -6,10 +6,8 @@ import {
|
|
|
6
6
|
useFontScale,
|
|
7
7
|
useScalableNumberOfLines,
|
|
8
8
|
} from '../../hooks'
|
|
9
|
-
import {
|
|
10
|
-
import { REACTION_EMOJIS, platformFontWeightBold } from '../../utils'
|
|
9
|
+
import { ConversationResource } from '../../types'
|
|
11
10
|
import { formatDatePreview } from '../../utils/date'
|
|
12
|
-
import { tokens } from '../../vendor/tapestry/tokens'
|
|
13
11
|
import { AvatarGroup, ConversationAvatar, Heading, Text, Badge, type BadgeProps } from '../display'
|
|
14
12
|
import { ConversationActions } from './conversation_actions'
|
|
15
13
|
import { MuteIndicator } from './mute_indicator'
|
|
@@ -32,26 +30,25 @@ export const ConversationPreview = ({
|
|
|
32
30
|
}: ConversationPreviewProps) => {
|
|
33
31
|
const styles = useStyles()
|
|
34
32
|
const {
|
|
35
|
-
|
|
33
|
+
lastMessageAuthorName,
|
|
36
34
|
lastMessageCreatedAt,
|
|
35
|
+
lastMessageTextPreview,
|
|
37
36
|
title,
|
|
38
37
|
unreadCount,
|
|
39
38
|
badges,
|
|
40
39
|
muted,
|
|
41
40
|
disabled,
|
|
42
|
-
unreadReaction,
|
|
43
41
|
} = conversation
|
|
44
42
|
|
|
45
43
|
const emptyConversation = !lastMessageCreatedAt
|
|
46
44
|
const fallbackIconName = emptyConversation ? 'people.noTextMessage' : 'general.person'
|
|
47
45
|
const hasMetaContent = lastMessageCreatedAt || unreadCount > 0 || muted
|
|
48
|
-
const isUnread = unreadCount > 0 || unreadReaction
|
|
49
46
|
|
|
50
47
|
const numberOfLinesScaledTitle = useScalableNumberOfLines(1)
|
|
51
48
|
const numberOfLinesScaledPreviewText = useScalableNumberOfLines(2)
|
|
52
49
|
|
|
53
|
-
const conversationPreviewText =
|
|
54
|
-
?
|
|
50
|
+
const conversationPreviewText = lastMessageTextPreview
|
|
51
|
+
? `${lastMessageAuthorName}: ${lastMessageTextPreview}`
|
|
55
52
|
: EMPTY_CONVERSATION_PREVIEW_TEXT
|
|
56
53
|
|
|
57
54
|
const conversationAccessibilityLabel = getConversationAccessibilityLabel({
|
|
@@ -72,20 +69,13 @@ export const ConversationPreview = ({
|
|
|
72
69
|
fallbackIconName={fallbackIconName}
|
|
73
70
|
/>
|
|
74
71
|
<View style={styles.conversationBody}>
|
|
75
|
-
<Heading
|
|
76
|
-
numberOfLines={numberOfLinesScaledTitle}
|
|
77
|
-
variant="h3"
|
|
78
|
-
style={[styles.title, isUnread && styles.unreadTitle]}
|
|
79
|
-
>
|
|
72
|
+
<Heading numberOfLines={numberOfLinesScaledTitle} variant="h3" style={styles.title}>
|
|
80
73
|
{title}
|
|
81
74
|
</Heading>
|
|
82
75
|
<Text
|
|
83
76
|
variant="tertiary"
|
|
84
77
|
numberOfLines={numberOfLinesScaledPreviewText}
|
|
85
|
-
style={
|
|
86
|
-
emptyConversation && styles.emptyConversationPreviewText,
|
|
87
|
-
isUnread && styles.unreadText,
|
|
88
|
-
]}
|
|
78
|
+
style={emptyConversation && styles.emptyConversationPreviewText}
|
|
89
79
|
>
|
|
90
80
|
{conversationPreviewText}
|
|
91
81
|
</Text>
|
|
@@ -93,9 +83,9 @@ export const ConversationPreview = ({
|
|
|
93
83
|
</View>
|
|
94
84
|
{hasMetaContent && (
|
|
95
85
|
<View style={styles.metaContainer}>
|
|
96
|
-
<Text variant="tertiary">{formatDatePreview(
|
|
86
|
+
<Text variant="tertiary">{formatDatePreview(lastMessageCreatedAt)}</Text>
|
|
97
87
|
<View style={styles.statusContainer}>
|
|
98
|
-
<UnreadCountBadge count={unreadCount}
|
|
88
|
+
<UnreadCountBadge count={unreadCount} showDot={muted} />
|
|
99
89
|
<MuteIndicator muted={muted} />
|
|
100
90
|
</View>
|
|
101
91
|
</View>
|
|
@@ -162,30 +152,6 @@ export const ConversationPreviewSkeleton = () => {
|
|
|
162
152
|
)
|
|
163
153
|
}
|
|
164
154
|
|
|
165
|
-
function reactionEmoji(value: ConversationPreviewData['value']): string | null {
|
|
166
|
-
if (!value) return null
|
|
167
|
-
return REACTION_EMOJIS[value] ?? null
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function formatConversationPreviewText(preview: ConversationPreviewData) {
|
|
171
|
-
if (preview.kind === 'reaction') {
|
|
172
|
-
const emoji = reactionEmoji(preview.value)
|
|
173
|
-
return `${preview.authorName}${emoji ? ` ${emoji}` : ' reacted'} to: "${preview.text}"`
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return `${preview.authorName}: ${preview.text}`
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function formatConversationPreviewAccessibilityLabel(preview: ConversationPreviewData) {
|
|
180
|
-
const date = formatDatePreview(preview.createdAt)
|
|
181
|
-
|
|
182
|
-
if (preview.kind === 'reaction') {
|
|
183
|
-
return `${preview.authorName} reacted to your message sent ${date} and says ${preview.text}`
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return `Last message sent ${date} from ${preview.authorName} and says ${preview.text}`
|
|
187
|
-
}
|
|
188
|
-
|
|
189
155
|
interface GetConversationAccessibilityLabelProps {
|
|
190
156
|
conversation: ConversationResource
|
|
191
157
|
showBadges: boolean
|
|
@@ -194,24 +160,29 @@ const getConversationAccessibilityLabel = ({
|
|
|
194
160
|
conversation,
|
|
195
161
|
showBadges,
|
|
196
162
|
}: GetConversationAccessibilityLabelProps) => {
|
|
197
|
-
const {
|
|
198
|
-
|
|
163
|
+
const {
|
|
164
|
+
badges,
|
|
165
|
+
title,
|
|
166
|
+
lastMessageAuthorName,
|
|
167
|
+
lastMessageTextPreview,
|
|
168
|
+
muted,
|
|
169
|
+
disabled,
|
|
170
|
+
unreadCount,
|
|
171
|
+
lastMessageCreatedAt,
|
|
172
|
+
} = conversation
|
|
199
173
|
|
|
200
174
|
const hasBadges = badges && showBadges
|
|
201
175
|
const hasUnreadMessages = unreadCount > 0
|
|
176
|
+
const hasLastMessage = lastMessageCreatedAt
|
|
202
177
|
|
|
203
178
|
const label = {
|
|
204
179
|
title,
|
|
205
180
|
badge: hasBadges ? `for ${badges[0]?.pcoResourceType} ${badges[0]?.text || ''}` : '',
|
|
206
181
|
disabled: disabled ? 'Disabled' : '',
|
|
207
182
|
muted: muted ? 'Muted' : '',
|
|
208
|
-
readStatus: hasUnreadMessages
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
? 'unread reaction'
|
|
212
|
-
: '',
|
|
213
|
-
lastMessage: conversationPreview
|
|
214
|
-
? formatConversationPreviewAccessibilityLabel(conversationPreview)
|
|
183
|
+
readStatus: hasUnreadMessages ? `${unreadCount} unread messages` : '',
|
|
184
|
+
lastMessage: hasLastMessage
|
|
185
|
+
? `Last message sent ${formatDatePreview(lastMessageCreatedAt)} from ${lastMessageAuthorName} and says ${lastMessageTextPreview}`
|
|
215
186
|
: EMPTY_CONVERSATION_PREVIEW_TEXT,
|
|
216
187
|
}
|
|
217
188
|
|
|
@@ -251,12 +222,6 @@ const useStyles = () => {
|
|
|
251
222
|
fontSize: 16,
|
|
252
223
|
color: colors.textColorDefaultPrimary,
|
|
253
224
|
},
|
|
254
|
-
unreadTitle: {
|
|
255
|
-
fontWeight: tokens.fontWeightBold,
|
|
256
|
-
},
|
|
257
|
-
unreadText: {
|
|
258
|
-
fontWeight: platformFontWeightBold,
|
|
259
|
-
},
|
|
260
225
|
titleLoading: {
|
|
261
226
|
width: 120,
|
|
262
227
|
height: 20,
|
|
@@ -4,21 +4,12 @@ import { useFontScale, useTheme } from '../../hooks'
|
|
|
4
4
|
import { MAX_FONT_SIZE_MULTIPLIER, platformFontWeightBold } from '../../utils'
|
|
5
5
|
import { Text } from '../display'
|
|
6
6
|
|
|
7
|
-
export const UnreadCountBadge = ({
|
|
8
|
-
count,
|
|
9
|
-
unreadReaction,
|
|
10
|
-
showDot,
|
|
11
|
-
}: {
|
|
12
|
-
count: number
|
|
13
|
-
unreadReaction: boolean
|
|
14
|
-
showDot: boolean
|
|
15
|
-
}) => {
|
|
7
|
+
export const UnreadCountBadge = ({ count, showDot }: { count: number; showDot: boolean }) => {
|
|
16
8
|
const styles = useStyles()
|
|
17
9
|
const displayCount = count > 99 ? '99+' : count
|
|
18
|
-
const isUnread = count > 0 || unreadReaction
|
|
19
10
|
|
|
20
|
-
if (
|
|
21
|
-
if (showDot
|
|
11
|
+
if (count === 0) return null
|
|
12
|
+
if (showDot) return <View style={styles.dot} />
|
|
22
13
|
|
|
23
14
|
return (
|
|
24
15
|
<View style={styles.badge}>
|
|
@@ -33,7 +24,7 @@ const useStyles = () => {
|
|
|
33
24
|
const { colors } = useTheme()
|
|
34
25
|
const dotScaleLimit = MAX_FONT_SIZE_MULTIPLIER * 1.5
|
|
35
26
|
const fontScale = useFontScale({ maxFontSizeMultiplier: dotScaleLimit })
|
|
36
|
-
const size =
|
|
27
|
+
const size = 8 * fontScale
|
|
37
28
|
|
|
38
29
|
return StyleSheet.create({
|
|
39
30
|
badge: {
|
|
@@ -16,7 +16,6 @@ export const getConversationRequestArgs = ({ conversation_id }: { conversation_i
|
|
|
16
16
|
'created_at',
|
|
17
17
|
'badges',
|
|
18
18
|
'conversation_membership',
|
|
19
|
-
'conversation_preview',
|
|
20
19
|
'gender_option',
|
|
21
20
|
'groups',
|
|
22
21
|
'last_message_author_id',
|
|
@@ -37,7 +36,6 @@ export const getConversationRequestArgs = ({ conversation_id }: { conversation_i
|
|
|
37
36
|
'replies_disabled',
|
|
38
37
|
'title',
|
|
39
38
|
'unread_count',
|
|
40
|
-
'unread_reaction',
|
|
41
39
|
'updated_at',
|
|
42
40
|
],
|
|
43
41
|
AnalyticsMetadata: ['metadata'],
|
|
@@ -63,7 +63,7 @@ export function useConversationJoltEvents({ conversationId }: Props) {
|
|
|
63
63
|
data: {
|
|
64
64
|
...prev.data,
|
|
65
65
|
latestReadMessageSortKey: latest_read_message_sort_key,
|
|
66
|
-
|
|
66
|
+
unreadReactionCount: 0,
|
|
67
67
|
},
|
|
68
68
|
}
|
|
69
69
|
})
|
|
@@ -87,9 +87,25 @@ export function useConversationJoltEvents({ conversationId }: Props) {
|
|
|
87
87
|
|
|
88
88
|
const handleReactionJoltEvent = useCallback(
|
|
89
89
|
(event: JoltReactionEvent) => {
|
|
90
|
-
|
|
90
|
+
queryClient.setQueryData<ApiResource<ConversationResource>>(queryKey, prev => {
|
|
91
|
+
if (!prev?.data) return prev
|
|
91
92
|
|
|
92
|
-
|
|
93
|
+
if (event.data.data.author_id === currentPersonId) {
|
|
94
|
+
return prev
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
...prev,
|
|
99
|
+
data: {
|
|
100
|
+
...prev.data,
|
|
101
|
+
// Not a real count, just a derived value from Jolt events so we can
|
|
102
|
+
// determine if we should mark the conversation as read
|
|
103
|
+
unreadReactionCount: prev.data.unreadReactionCount
|
|
104
|
+
? prev.data.unreadReactionCount + 1
|
|
105
|
+
: 1,
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
})
|
|
93
109
|
},
|
|
94
110
|
[queryClient, queryKey, currentPersonId]
|
|
95
111
|
)
|
|
@@ -7,10 +7,11 @@ export const useConversationMessages = (
|
|
|
7
7
|
{ conversation_id, reply_root_id }: { conversation_id: number; reply_root_id?: string | null },
|
|
8
8
|
opts?: SuspensePaginatorOptions
|
|
9
9
|
) => {
|
|
10
|
-
const { data, refetch, isRefetching, fetchNextPage } =
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
const { data, refetch, isRefetching, fetchNextPage, hasNextPage } =
|
|
11
|
+
useSuspensePaginator<MessageResource>(
|
|
12
|
+
getMessagesRequestArgs({ conversation_id, reply_root_id }),
|
|
13
|
+
opts
|
|
14
|
+
)
|
|
14
15
|
const queryKey = getMessagesQueryKey({ conversation_id, reply_root_id })
|
|
15
16
|
const messages = useMemo(
|
|
16
17
|
() =>
|
|
@@ -24,5 +25,5 @@ export const useConversationMessages = (
|
|
|
24
25
|
[data]
|
|
25
26
|
)
|
|
26
27
|
|
|
27
|
-
return { messages, refetch, isRefetching, fetchNextPage, queryKey }
|
|
28
|
+
return { messages, refetch, isRefetching, fetchNextPage, hasNextPage, queryKey }
|
|
28
29
|
}
|
|
@@ -20,7 +20,7 @@ export const useConversationsMarkRead = ({
|
|
|
20
20
|
currentPersonCache.update({}, person => {
|
|
21
21
|
const currentUnread = person.unreadCount
|
|
22
22
|
const updatedUnread = read ? Math.max(currentUnread - 1, 0) : currentUnread + 1
|
|
23
|
-
return { ...person, unreadCount: updatedUnread }
|
|
23
|
+
return { ...person, unreadCount: updatedUnread, unreadReactionCount: 0 }
|
|
24
24
|
})
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -29,7 +29,6 @@ export const useConversationsMarkRead = ({
|
|
|
29
29
|
update({
|
|
30
30
|
...conversation,
|
|
31
31
|
unreadCount: read ? 0 : 1,
|
|
32
|
-
...(read ? { unreadReaction: false } : {}),
|
|
33
32
|
})
|
|
34
33
|
handlePersonUnreadCount(read)
|
|
35
34
|
},
|
|
@@ -107,7 +106,6 @@ export const useMarkAllRead = () => {
|
|
|
107
106
|
onMutate: () => {
|
|
108
107
|
updateAll({
|
|
109
108
|
unreadCount: 0,
|
|
110
|
-
unreadReaction: false,
|
|
111
109
|
})
|
|
112
110
|
},
|
|
113
111
|
mutationKey: ['markAllRead', args],
|
|
@@ -17,9 +17,9 @@ export function useMarkLatestMessageRead({ conversation }: Props) {
|
|
|
17
17
|
[markRead]
|
|
18
18
|
)
|
|
19
19
|
const unreadCount = conversation.unreadCount
|
|
20
|
-
const
|
|
20
|
+
const unreadReactionCount = conversation.unreadReactionCount || 0
|
|
21
21
|
|
|
22
|
-
const shouldMarkRead = Boolean(unreadCount >= 1 ||
|
|
22
|
+
const shouldMarkRead = Boolean(unreadCount >= 1 || unreadReactionCount > 0 || !firedOnce.current)
|
|
23
23
|
const appState = useAppState()
|
|
24
24
|
const isActive = appState === 'active'
|
|
25
25
|
|
|
@@ -29,5 +29,6 @@ export function useMarkLatestMessageRead({ conversation }: Props) {
|
|
|
29
29
|
firedOnce.current = true
|
|
30
30
|
|
|
31
31
|
debouncedMarkRead(true)
|
|
32
|
-
|
|
32
|
+
// keeping unreadReactionCount in the dependency array to watch for changes
|
|
33
|
+
}, [debouncedMarkRead, isActive, shouldMarkRead, unreadReactionCount])
|
|
33
34
|
}
|
|
@@ -9,7 +9,7 @@ import { ApiCollection, ApiResource, ResourceObject } from '../types'
|
|
|
9
9
|
import { FailedResponse } from '../types/api_primitives'
|
|
10
10
|
import { Log } from '../utils'
|
|
11
11
|
import { GetRequest, RequestData } from '../utils/client'
|
|
12
|
-
import {
|
|
12
|
+
import { ResponseError } from '../utils/response_error'
|
|
13
13
|
import { getNextPageParamFromMeta } from './paginator_meta'
|
|
14
14
|
import { App, useApiClient } from './use_api_client'
|
|
15
15
|
|
|
@@ -90,6 +90,14 @@ export const useSuspensePaginator = <T extends ResourceObject>(
|
|
|
90
90
|
return { ...query, data, totalCount }
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
const throwResponseError = (error: unknown) => {
|
|
94
|
+
if (error instanceof Response) {
|
|
95
|
+
throw new ResponseError(error as FailedResponse)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return Promise.reject(error)
|
|
99
|
+
}
|
|
100
|
+
|
|
93
101
|
export type RequestQueryKey = [
|
|
94
102
|
SuspenseGetOptions['url'],
|
|
95
103
|
SuspenseGetOptions['data'],
|
|
@@ -89,7 +89,7 @@ function ConversationScreenContent({ route }: ConversationScreenProps) {
|
|
|
89
89
|
const { conversation_id, editing_message_id, reply_root_id, reply_root_author_name } =
|
|
90
90
|
route.params
|
|
91
91
|
const { data: conversation } = useConversation(route.params)
|
|
92
|
-
const { messages, fetchNextPage } = useConversationMessages({
|
|
92
|
+
const { messages, fetchNextPage, hasNextPage } = useConversationMessages({
|
|
93
93
|
conversation_id,
|
|
94
94
|
reply_root_id,
|
|
95
95
|
})
|
|
@@ -181,7 +181,9 @@ function ConversationScreenContent({ route }: ConversationScreenProps) {
|
|
|
181
181
|
onScroll={trackScroll}
|
|
182
182
|
scrollEventThrottle={10}
|
|
183
183
|
ListFooterComponent={
|
|
184
|
-
|
|
184
|
+
hasNextPage ? null : (
|
|
185
|
+
<ConversationStartBanner conversation={conversation} hasMessages={true} />
|
|
186
|
+
)
|
|
185
187
|
}
|
|
186
188
|
renderItem={({ item }) => {
|
|
187
189
|
if (item.type === 'DateSeparator') {
|
|
@@ -158,6 +158,12 @@ export const ListHeaderComponent = () => {
|
|
|
158
158
|
</TextButton>
|
|
159
159
|
</View>
|
|
160
160
|
<View style={styles.actionsContainer}>
|
|
161
|
+
<ListHeaderActionButton
|
|
162
|
+
name="general.cog"
|
|
163
|
+
onPress={handleChatSettings}
|
|
164
|
+
accessibilityLabel="Chat settings"
|
|
165
|
+
maxFontSizeMultiplier={MAX_FONT_SIZE_MULTIPLIER_LANDMARK}
|
|
166
|
+
/>
|
|
161
167
|
{canCreateConversations && (
|
|
162
168
|
<ListHeaderActionButton
|
|
163
169
|
name="churchCenter.signups"
|
|
@@ -167,12 +173,6 @@ export const ListHeaderComponent = () => {
|
|
|
167
173
|
maxFontSizeMultiplier={MAX_FONT_SIZE_MULTIPLIER_LANDMARK}
|
|
168
174
|
/>
|
|
169
175
|
)}
|
|
170
|
-
<ListHeaderActionButton
|
|
171
|
-
name="general.cog"
|
|
172
|
-
onPress={handleChatSettings}
|
|
173
|
-
accessibilityLabel="Chat settings"
|
|
174
|
-
maxFontSizeMultiplier={MAX_FONT_SIZE_MULTIPLIER_LANDMARK}
|
|
175
|
-
/>
|
|
176
176
|
{shouldShowHeaderFiltersButton && <ToggleButton {...moreFiltersButtonProps} />}
|
|
177
177
|
</View>
|
|
178
178
|
</View>
|
|
@@ -3,25 +3,15 @@ import { ConversationBadgeResource } from './conversation_badge'
|
|
|
3
3
|
import { ConversationMembershipResource } from './conversation_membership'
|
|
4
4
|
import { GroupResource } from './group_resource'
|
|
5
5
|
import { MemberAbilityResource } from './member_ability'
|
|
6
|
-
import { ReactionCountResource } from './reaction'
|
|
7
6
|
|
|
8
7
|
export type ConversationDisabledReason = 'safety_check' | 'direct_messages_disabled'
|
|
9
8
|
|
|
10
|
-
export interface ConversationPreviewData {
|
|
11
|
-
kind: 'reaction' | 'message'
|
|
12
|
-
authorName: string
|
|
13
|
-
value: ReactionCountResource['value'] | null
|
|
14
|
-
text: string
|
|
15
|
-
createdAt: string
|
|
16
|
-
}
|
|
17
|
-
|
|
18
9
|
export interface ConversationResource {
|
|
19
10
|
type: 'Conversation'
|
|
20
11
|
id: number
|
|
21
12
|
badges?: ConversationBadgeResource[]
|
|
22
13
|
analyticsMetadata?: AnalyticsMetadataResource
|
|
23
14
|
conversationMembership?: Partial<ConversationMembershipResource>
|
|
24
|
-
conversationPreview: ConversationPreviewData | null
|
|
25
15
|
createdAt: string
|
|
26
16
|
deleted?: boolean
|
|
27
17
|
directMessage?: boolean
|
|
@@ -44,6 +34,6 @@ export interface ConversationResource {
|
|
|
44
34
|
repliesDisabled: boolean
|
|
45
35
|
title: string
|
|
46
36
|
unreadCount: number
|
|
47
|
-
|
|
37
|
+
unreadReactionCount?: number // Derived from Jolt events
|
|
48
38
|
updatedAt: string
|
|
49
39
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ApiCollection, ApiResource, FailedResponse } from '../../types'
|
|
2
|
-
import { isResponseLike } from '../response_error'
|
|
3
2
|
import {
|
|
4
3
|
concatRecords,
|
|
5
4
|
ensureNoQueryParamsInDev,
|
|
@@ -107,7 +106,7 @@ export class Client {
|
|
|
107
106
|
}
|
|
108
107
|
|
|
109
108
|
handleNotOk = async (response: Response | Error) => {
|
|
110
|
-
if (!
|
|
109
|
+
if (!(response instanceof Response)) return Promise.reject(response)
|
|
111
110
|
|
|
112
111
|
const errorData = await this.parseErrorResponse(response)
|
|
113
112
|
const notOkResponse = response.clone() as FailedResponse
|
|
@@ -38,7 +38,6 @@ export const getConversationsRequestArgs = ({
|
|
|
38
38
|
Conversation: [
|
|
39
39
|
'created_at',
|
|
40
40
|
'badges',
|
|
41
|
-
'conversation_preview',
|
|
42
41
|
'disabled',
|
|
43
42
|
'disabled_reason',
|
|
44
43
|
'groups',
|
|
@@ -56,7 +55,6 @@ export const getConversationsRequestArgs = ({
|
|
|
56
55
|
'replies_disabled',
|
|
57
56
|
'title',
|
|
58
57
|
'unread_count',
|
|
59
|
-
'unread_reaction',
|
|
60
58
|
'updated_at',
|
|
61
59
|
],
|
|
62
60
|
AnalyticsMetadata: ['metadata'],
|
|
@@ -16,19 +16,8 @@ export class ResponseError extends Error {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
// Detect a fetch Response by shape, not `instanceof`: Expo's winter runtime
|
|
20
|
-
// swaps in a `fetch` whose `FetchResponse` isn't an instance of the global
|
|
21
|
-
// `Response`, so `instanceof` silently misses it under Expo.
|
|
22
|
-
export const isResponseLike = (value: unknown): value is Response =>
|
|
23
|
-
typeof value === 'object' &&
|
|
24
|
-
value !== null &&
|
|
25
|
-
typeof (value as Response).status === 'number' &&
|
|
26
|
-
typeof (value as Response).ok === 'boolean' &&
|
|
27
|
-
typeof (value as Response).clone === 'function' &&
|
|
28
|
-
typeof (value as Response).json === 'function'
|
|
29
|
-
|
|
30
19
|
export const throwResponseError = (error: unknown) => {
|
|
31
|
-
if (
|
|
20
|
+
if (error instanceof Response) {
|
|
32
21
|
throw new ResponseError(error as FailedResponse)
|
|
33
22
|
}
|
|
34
23
|
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { isResponseLike, ResponseError, throwResponseError } from '../response_error'
|
|
2
|
-
|
|
3
|
-
// Mimics Expo's winter `FetchResponse`: the full Response shape, but NOT an
|
|
4
|
-
// instance of the global `Response` (so `instanceof Response` returns false).
|
|
5
|
-
const fetchResponseLike = (status: number) => ({
|
|
6
|
-
status,
|
|
7
|
-
ok: status >= 200 && status < 300,
|
|
8
|
-
statusText: 'unauthorized',
|
|
9
|
-
url: 'https://api.planningcenteronline.com/chat/v2/me',
|
|
10
|
-
clone() {
|
|
11
|
-
return this
|
|
12
|
-
},
|
|
13
|
-
json: async () => ({ errors: [] }),
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
describe('isResponseLike', () => {
|
|
17
|
-
it('recognizes a Response by shape even when it is not an instanceof Response', () => {
|
|
18
|
-
const notARealResponse = fetchResponseLike(401)
|
|
19
|
-
expect(notARealResponse instanceof Response).toBe(false)
|
|
20
|
-
expect(isResponseLike(notARealResponse)).toBe(true)
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
it('rejects things that are not responses', () => {
|
|
24
|
-
expect(isResponseLike(null)).toBe(false)
|
|
25
|
-
expect(isResponseLike(new Error('boom'))).toBe(false)
|
|
26
|
-
expect(isResponseLike(new ResponseError({ status: 401 } as never))).toBe(false)
|
|
27
|
-
expect(isResponseLike({ status: 401 })).toBe(false)
|
|
28
|
-
})
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
describe('throwResponseError', () => {
|
|
32
|
-
it('wraps a Response-like rejection into a ResponseError', () => {
|
|
33
|
-
expect(() => throwResponseError(fetchResponseLike(401))).toThrow(ResponseError)
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('passes through non-response errors untouched', async () => {
|
|
37
|
-
const error = new Error('network down')
|
|
38
|
-
await expect(throwResponseError(error)).rejects.toBe(error)
|
|
39
|
-
})
|
|
40
|
-
})
|