@planningcenter/chat-react-native 4.4.1-rc.0 → 4.5.0-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/conversations.d.ts.map +1 -1
- package/build/components/conversations/conversations.js +3 -2
- package/build/components/conversations/conversations.js.map +1 -1
- package/build/contexts/conversations_context.d.ts.map +1 -1
- package/build/contexts/conversations_context.js +20 -17
- package/build/contexts/conversations_context.js.map +1 -1
- package/build/hooks/use_clear_conversation_filters.d.ts.map +1 -1
- package/build/hooks/use_clear_conversation_filters.js +2 -1
- package/build/hooks/use_clear_conversation_filters.js.map +1 -1
- package/build/hooks/use_conversations_cache.d.ts +1 -1
- package/build/hooks/use_conversations_cache.d.ts.map +1 -1
- package/build/hooks/use_conversations_cache.js +64 -55
- package/build/hooks/use_conversations_cache.js.map +1 -1
- package/build/hooks/use_conversations_jolt_events.js +2 -2
- package/build/hooks/use_conversations_jolt_events.js.map +1 -1
- package/build/hooks/use_jolt.d.ts.map +1 -1
- package/build/hooks/use_jolt.js +37 -9
- package/build/hooks/use_jolt.js.map +1 -1
- package/build/screens/conversation_filters/context/conversation_filter_context.d.ts.map +1 -1
- package/build/screens/conversation_filters/context/conversation_filter_context.js +8 -4
- package/build/screens/conversation_filters/context/conversation_filter_context.js.map +1 -1
- package/build/screens/conversation_filters/screen_props.d.ts +1 -0
- package/build/screens/conversation_filters/screen_props.d.ts.map +1 -1
- package/build/screens/conversation_filters/screen_props.js.map +1 -1
- package/build/screens/conversations/components/filter_dropdown_list_header_component.d.ts.map +1 -1
- package/build/screens/conversations/components/filter_dropdown_list_header_component.js +191 -168
- package/build/screens/conversations/components/filter_dropdown_list_header_component.js.map +1 -1
- package/build/screens/conversations/conversations_screen.d.ts +1 -0
- package/build/screens/conversations/conversations_screen.d.ts.map +1 -1
- package/build/screens/conversations/conversations_screen.js.map +1 -1
- package/build/utils/request/conversation.d.ts +2 -1
- package/build/utils/request/conversation.d.ts.map +1 -1
- package/build/utils/request/conversation.js +4 -1
- package/build/utils/request/conversation.js.map +1 -1
- package/package.json +2 -2
- package/src/components/conversations/__tests__/conversations_blank_state.test.tsx +1 -0
- package/src/components/conversations/conversations.tsx +12 -2
- package/src/contexts/conversations_context.tsx +8 -1
- package/src/hooks/__tests__/use_conversations_jolt_events.test.tsx +135 -0
- package/src/hooks/__tests__/use_jolt.test.tsx +76 -1
- package/src/hooks/use_clear_conversation_filters.ts +1 -0
- package/src/hooks/use_conversations_cache.ts +29 -24
- package/src/hooks/use_conversations_jolt_events.ts +2 -2
- package/src/hooks/use_jolt.ts +26 -2
- package/src/screens/conversation_filters/context/conversation_filter_context.tsx +4 -0
- package/src/screens/conversation_filters/screen_props.ts +1 -0
- package/src/screens/conversations/components/__tests__/filter_dropdown_list_header_component.test.tsx +55 -3
- package/src/screens/conversations/components/filter_dropdown_list_header_component.tsx +17 -2
- package/src/screens/conversations/conversations_screen.tsx +1 -0
- package/src/utils/__tests__/conversation.test.ts +1 -0
- package/src/utils/request/conversation.ts +4 -0
|
@@ -31,10 +31,10 @@ export function useConversationsJoltEvents() {
|
|
|
31
31
|
idempotentKey: e.data.data.last_message_idempotent_key,
|
|
32
32
|
})
|
|
33
33
|
}
|
|
34
|
-
cache.
|
|
34
|
+
cache.fetchSync({ id: e.data.data.id })
|
|
35
35
|
})
|
|
36
36
|
useJoltEvent(joltChannel, 'conversation.created', (e: JoltConversationEvent) =>
|
|
37
|
-
cache.
|
|
37
|
+
cache.fetchSync({ id: e.data.data.id })
|
|
38
38
|
)
|
|
39
39
|
useJoltEvent(joltChannel, 'conversation.destroyed', (e: JoltConversationEvent) =>
|
|
40
40
|
cache.destroy({ id: e.data.data.id })
|
package/src/hooks/use_jolt.ts
CHANGED
|
@@ -181,6 +181,25 @@ export const useJoltForegroundPing = () => {
|
|
|
181
181
|
}, [queryClient])
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
const channelConsumers = new WeakMap<JoltClient, Map<string, number>>()
|
|
185
|
+
|
|
186
|
+
const retainChannel = (jolt: JoltClient, channelName: string) => {
|
|
187
|
+
const counts = channelConsumers.get(jolt) ?? new Map<string, number>()
|
|
188
|
+
channelConsumers.set(jolt, counts)
|
|
189
|
+
counts.set(channelName, (counts.get(channelName) ?? 0) + 1)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const releaseChannel = (jolt: JoltClient, channelName: string) => {
|
|
193
|
+
const counts = channelConsumers.get(jolt)
|
|
194
|
+
const remaining = (counts?.get(channelName) ?? 1) - 1
|
|
195
|
+
if (remaining > 0) {
|
|
196
|
+
counts?.set(channelName, remaining)
|
|
197
|
+
} else {
|
|
198
|
+
counts?.delete(channelName)
|
|
199
|
+
}
|
|
200
|
+
return remaining
|
|
201
|
+
}
|
|
202
|
+
|
|
184
203
|
export function useJoltChannel(
|
|
185
204
|
channelName: string,
|
|
186
205
|
enabled: boolean = true
|
|
@@ -216,8 +235,13 @@ export function useJoltChannel(
|
|
|
216
235
|
}, [queryClient, channelName, jolt])
|
|
217
236
|
|
|
218
237
|
useEffect(() => {
|
|
219
|
-
return
|
|
220
|
-
|
|
238
|
+
if (!jolt) return
|
|
239
|
+
|
|
240
|
+
retainChannel(jolt, channelName)
|
|
241
|
+
return () => {
|
|
242
|
+
if (releaseChannel(jolt, channelName) === 0) handleUnsubscribe()
|
|
243
|
+
}
|
|
244
|
+
}, [jolt, channelName, handleUnsubscribe])
|
|
221
245
|
|
|
222
246
|
return subscription
|
|
223
247
|
}
|
|
@@ -23,6 +23,7 @@ export const FilterContext = createContext<{
|
|
|
23
23
|
group_source_app_name: undefined,
|
|
24
24
|
show_direct_messages: undefined,
|
|
25
25
|
show_archived: undefined,
|
|
26
|
+
show_unread: undefined,
|
|
26
27
|
},
|
|
27
28
|
scrollOffset: 0,
|
|
28
29
|
setScrollOffset: () => {},
|
|
@@ -59,6 +60,7 @@ export const FilterProvider = ({ children }: PropsWithChildren) => {
|
|
|
59
60
|
group_source_app_name: undefined,
|
|
60
61
|
show_direct_messages: undefined,
|
|
61
62
|
show_archived: undefined,
|
|
63
|
+
show_unread: undefined,
|
|
62
64
|
})
|
|
63
65
|
)
|
|
64
66
|
},
|
|
@@ -68,6 +70,7 @@ export const FilterProvider = ({ children }: PropsWithChildren) => {
|
|
|
68
70
|
group_source_app_name: undefined,
|
|
69
71
|
show_direct_messages: undefined,
|
|
70
72
|
show_archived: undefined,
|
|
73
|
+
show_unread: undefined,
|
|
71
74
|
})
|
|
72
75
|
},
|
|
73
76
|
setAppFilter: (params: Omit<ConversationFiltersParams, 'chat_group_graph_id'>) => {
|
|
@@ -76,6 +79,7 @@ export const FilterProvider = ({ children }: PropsWithChildren) => {
|
|
|
76
79
|
group_source_app_name: params.group_source_app_name,
|
|
77
80
|
show_direct_messages: params.show_direct_messages,
|
|
78
81
|
show_archived: params.show_archived,
|
|
82
|
+
show_unread: undefined,
|
|
79
83
|
})
|
|
80
84
|
},
|
|
81
85
|
applyFilters: (params: ConversationFiltersParams) => {
|
|
@@ -128,7 +128,7 @@ describe('FilterDropdownListHeaderComponent', () => {
|
|
|
128
128
|
it('offers every filter the person can use, then More filters', () => {
|
|
129
129
|
render(<FilterDropdownListHeaderComponent />)
|
|
130
130
|
|
|
131
|
-
expect(menuItemNames()).toEqual(['All', 'Groups', 'Teams', 'DMs', 'More filters'])
|
|
131
|
+
expect(menuItemNames()).toEqual(['All', 'Groups', 'Teams', 'DMs', 'Unread', 'More filters'])
|
|
132
132
|
expect(screen.getByRole('button', { name: 'All conversations' })).toBeTruthy()
|
|
133
133
|
})
|
|
134
134
|
|
|
@@ -137,7 +137,7 @@ describe('FilterDropdownListHeaderComponent', () => {
|
|
|
137
137
|
|
|
138
138
|
render(<FilterDropdownListHeaderComponent />)
|
|
139
139
|
|
|
140
|
-
expect(menuItemNames()).toEqual(['All', 'DMs'])
|
|
140
|
+
expect(menuItemNames()).toEqual(['All', 'DMs', 'Unread'])
|
|
141
141
|
})
|
|
142
142
|
|
|
143
143
|
it('selecting Groups filters to group conversations', () => {
|
|
@@ -150,6 +150,45 @@ describe('FilterDropdownListHeaderComponent', () => {
|
|
|
150
150
|
)
|
|
151
151
|
})
|
|
152
152
|
|
|
153
|
+
it('selecting Unread filters to unread conversations', () => {
|
|
154
|
+
setRouteParams({ group_source_app_name: 'Groups' })
|
|
155
|
+
|
|
156
|
+
render(<FilterDropdownListHeaderComponent />)
|
|
157
|
+
|
|
158
|
+
fireEvent.press(screen.getByRole('menuitem', { name: 'Unread' }))
|
|
159
|
+
|
|
160
|
+
expect(mockSetParams).toHaveBeenCalledWith({
|
|
161
|
+
title: undefined,
|
|
162
|
+
chat_group_graph_id: undefined,
|
|
163
|
+
group_source_app_name: undefined,
|
|
164
|
+
show_direct_messages: undefined,
|
|
165
|
+
show_unread: true,
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('selecting another filter clears Unread', () => {
|
|
170
|
+
setRouteParams({ show_unread: true })
|
|
171
|
+
|
|
172
|
+
render(<FilterDropdownListHeaderComponent />)
|
|
173
|
+
|
|
174
|
+
fireEvent.press(screen.getByRole('menuitem', { name: 'DMs' }))
|
|
175
|
+
|
|
176
|
+
expect(mockSetParams).toHaveBeenCalledWith(
|
|
177
|
+
expect.objectContaining({ show_direct_messages: true, show_unread: undefined })
|
|
178
|
+
)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('labels the trigger Unread and checks it when filtered to unread', () => {
|
|
182
|
+
setRouteParams({ show_unread: true })
|
|
183
|
+
|
|
184
|
+
render(<FilterDropdownListHeaderComponent />)
|
|
185
|
+
|
|
186
|
+
expect(screen.getByRole('button', { name: 'Unread' })).toBeTruthy()
|
|
187
|
+
expect(screen.getByRole('menuitem', { name: 'Unread' }).props.accessibilityState.selected).toBe(
|
|
188
|
+
true
|
|
189
|
+
)
|
|
190
|
+
})
|
|
191
|
+
|
|
153
192
|
it('labels the trigger with the active filter and checks it', () => {
|
|
154
193
|
setRouteParams({ group_source_app_name: 'Services' })
|
|
155
194
|
|
|
@@ -185,7 +224,7 @@ describe('FilterDropdownListHeaderComponent', () => {
|
|
|
185
224
|
|
|
186
225
|
render(<FilterDropdownListHeaderComponent />)
|
|
187
226
|
|
|
188
|
-
expect(menuItemNames()).toEqual(['All', 'DMs', 'More filters'])
|
|
227
|
+
expect(menuItemNames()).toEqual(['All', 'DMs', 'Unread', 'More filters'])
|
|
189
228
|
})
|
|
190
229
|
|
|
191
230
|
it('labels the archived list and carries it into More filters', () => {
|
|
@@ -234,6 +273,19 @@ describe('FilterDropdownListHeaderComponent', () => {
|
|
|
234
273
|
expect(mockNavigate).toHaveBeenCalledWith('ConversationFilters', expect.any(Object))
|
|
235
274
|
})
|
|
236
275
|
|
|
276
|
+
it('More filters carries the Unread filter into the sheet', () => {
|
|
277
|
+
setRouteParams({ show_unread: true })
|
|
278
|
+
|
|
279
|
+
render(<FilterDropdownListHeaderComponent />)
|
|
280
|
+
|
|
281
|
+
fireEvent.press(screen.getByRole('menuitem', { name: 'More filters' }))
|
|
282
|
+
|
|
283
|
+
expect(mockNavigate).toHaveBeenCalledWith(
|
|
284
|
+
'ConversationFilters',
|
|
285
|
+
expect.objectContaining({ show_unread: true })
|
|
286
|
+
)
|
|
287
|
+
})
|
|
288
|
+
|
|
237
289
|
it('shows the search button only when search is enabled', () => {
|
|
238
290
|
mockSearchEnabled.mockReturnValue(false)
|
|
239
291
|
const view = render(<FilterDropdownListHeaderComponent />)
|
|
@@ -33,6 +33,7 @@ enum FilterTypes {
|
|
|
33
33
|
Groups = 'Groups',
|
|
34
34
|
Teams = 'Teams',
|
|
35
35
|
DMs = 'DMs',
|
|
36
|
+
Unread = 'Unread',
|
|
36
37
|
More = 'More',
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -41,6 +42,7 @@ const FILTER_PARAMS: Record<Exclude<FilterTypes, FilterTypes.More>, Conversation
|
|
|
41
42
|
[FilterTypes.Groups]: { group_source_app_name: 'Groups' },
|
|
42
43
|
[FilterTypes.Teams]: { group_source_app_name: 'Services' },
|
|
43
44
|
[FilterTypes.DMs]: { show_direct_messages: true },
|
|
45
|
+
[FilterTypes.Unread]: { show_unread: true },
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
export const FilterDropdownListHeaderComponent = () => {
|
|
@@ -59,6 +61,7 @@ export const FilterDropdownListHeaderComponent = () => {
|
|
|
59
61
|
group_source_app_name = '',
|
|
60
62
|
show_direct_messages,
|
|
61
63
|
show_archived,
|
|
64
|
+
show_unread,
|
|
62
65
|
} = route.params || {}
|
|
63
66
|
const currentPersonCache = useCurrentPersonCache()
|
|
64
67
|
const { markAllRead, isPending } = useMarkAllRead()
|
|
@@ -81,10 +84,12 @@ export const FilterDropdownListHeaderComponent = () => {
|
|
|
81
84
|
return FilterTypes.Teams
|
|
82
85
|
} else if (show_direct_messages) {
|
|
83
86
|
return FilterTypes.DMs
|
|
87
|
+
} else if (show_unread) {
|
|
88
|
+
return FilterTypes.Unread
|
|
84
89
|
}
|
|
85
90
|
|
|
86
91
|
return FilterTypes.All
|
|
87
|
-
}, [chat_group_graph_id, group_source_app_name, show_direct_messages, show_archived])
|
|
92
|
+
}, [chat_group_graph_id, group_source_app_name, show_direct_messages, show_archived, show_unread])
|
|
88
93
|
|
|
89
94
|
const options: FilterDropdownOption[] = useMemo(
|
|
90
95
|
() => [
|
|
@@ -92,6 +97,7 @@ export const FilterDropdownListHeaderComponent = () => {
|
|
|
92
97
|
...(canFilterByGroups ? [{ id: FilterTypes.Groups, label: FilterTypes.Groups }] : []),
|
|
93
98
|
...(canFilterByTeams ? [{ id: FilterTypes.Teams, label: FilterTypes.Teams }] : []),
|
|
94
99
|
...(canFilterByDMs ? [{ id: FilterTypes.DMs, label: FilterTypes.DMs }] : []),
|
|
100
|
+
{ id: FilterTypes.Unread, label: FilterTypes.Unread },
|
|
95
101
|
],
|
|
96
102
|
[canFilterByGroups, canFilterByTeams, canFilterByDMs]
|
|
97
103
|
)
|
|
@@ -142,6 +148,7 @@ export const FilterDropdownListHeaderComponent = () => {
|
|
|
142
148
|
group_source_app_name: undefined,
|
|
143
149
|
show_direct_messages: undefined,
|
|
144
150
|
show_archived: undefined,
|
|
151
|
+
show_unread: undefined,
|
|
145
152
|
...params,
|
|
146
153
|
})
|
|
147
154
|
},
|
|
@@ -170,8 +177,16 @@ export const FilterDropdownListHeaderComponent = () => {
|
|
|
170
177
|
group_source_app_name,
|
|
171
178
|
show_direct_messages,
|
|
172
179
|
show_archived,
|
|
180
|
+
show_unread,
|
|
173
181
|
})
|
|
174
|
-
}, [
|
|
182
|
+
}, [
|
|
183
|
+
navigation,
|
|
184
|
+
chat_group_graph_id,
|
|
185
|
+
group_source_app_name,
|
|
186
|
+
show_direct_messages,
|
|
187
|
+
show_archived,
|
|
188
|
+
show_unread,
|
|
189
|
+
])
|
|
175
190
|
|
|
176
191
|
const handleMarkAllRead = useCallback(() => {
|
|
177
192
|
currentPersonCache.update({ unreadCount: 0 })
|
|
@@ -19,6 +19,7 @@ export type ConversationsScreenProps = StaticScreenProps<{
|
|
|
19
19
|
group_source_app_name?: AppName
|
|
20
20
|
show_direct_messages?: boolean
|
|
21
21
|
show_archived?: boolean
|
|
22
|
+
show_unread?: boolean
|
|
22
23
|
}>
|
|
23
24
|
|
|
24
25
|
export function ConversationsScreen({ route }: ConversationsScreenProps) {
|
|
@@ -13,6 +13,7 @@ describe('getConversationsRequestArgs', () => {
|
|
|
13
13
|
['chat_group_graph_id', { chat_group_graph_id: groupId }, 'mine_or_not_empty,group'],
|
|
14
14
|
['show_direct_messages', { show_direct_messages: true }, 'mine_or_not_empty,direct_message'],
|
|
15
15
|
['show_archived', { show_archived: true }, 'mine_or_not_empty,archived'],
|
|
16
|
+
['show_unread', { show_unread: true }, 'mine_or_not_empty,unread'],
|
|
16
17
|
])('%s uses filter: %s', (_label, args, expectedFilter) => {
|
|
17
18
|
expect(getConversationsRequestArgs(args).data.filter).toBe(expectedFilter)
|
|
18
19
|
})
|
|
@@ -7,6 +7,7 @@ export interface ConversationRequestArgs {
|
|
|
7
7
|
group_source_app_name?: string
|
|
8
8
|
show_direct_messages?: boolean
|
|
9
9
|
show_archived?: boolean
|
|
10
|
+
show_unread?: boolean
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export const getConversationsRequestArgs = ({
|
|
@@ -15,12 +16,15 @@ export const getConversationsRequestArgs = ({
|
|
|
15
16
|
group_source_app_name,
|
|
16
17
|
show_direct_messages,
|
|
17
18
|
show_archived,
|
|
19
|
+
show_unread,
|
|
18
20
|
}: Partial<ConversationRequestArgs> = {}): GetRequest => {
|
|
19
21
|
let filter: string
|
|
20
22
|
if (chat_group_graph_id) {
|
|
21
23
|
filter = 'mine_or_not_empty,group'
|
|
22
24
|
} else if (show_archived) {
|
|
23
25
|
filter = 'mine_or_not_empty,archived'
|
|
26
|
+
} else if (show_unread) {
|
|
27
|
+
filter = 'mine_or_not_empty,unread'
|
|
24
28
|
} else if (show_direct_messages) {
|
|
25
29
|
filter = 'mine_or_not_empty,direct_message'
|
|
26
30
|
} else if (group_source_app_name) {
|