@planningcenter/chat-react-native 4.4.1-rc.0 → 4.5.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/components/conversation/message.d.ts.map +1 -1
- package/build/components/conversation/message.js +193 -145
- package/build/components/conversation/message.js.map +1 -1
- package/build/components/conversation/reply_shadow_message.d.ts.map +1 -1
- package/build/components/conversation/reply_shadow_message.js +135 -107
- package/build/components/conversation/reply_shadow_message.js.map +1 -1
- 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/build/utils/styles.d.ts +1 -0
- package/build/utils/styles.d.ts.map +1 -1
- package/build/utils/styles.js +1 -1
- package/build/utils/styles.js.map +1 -1
- package/package.json +2 -2
- package/src/components/conversation/message.tsx +10 -2
- package/src/components/conversation/reply_shadow_message.tsx +14 -3
- 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
- package/src/utils/styles.ts +1 -0
|
@@ -9,7 +9,7 @@ import { ChatContext, ChatContextValue } from '../../contexts/chat_context'
|
|
|
9
9
|
import { Session } from '../../utils'
|
|
10
10
|
import { Client } from '../../utils/client'
|
|
11
11
|
import { ResponseError } from '../../utils/response_error'
|
|
12
|
-
import { useJoltClient, useJoltForegroundPing } from '../use_jolt'
|
|
12
|
+
import { useJoltChannel, useJoltClient, useJoltEvent, useJoltForegroundPing } from '../use_jolt'
|
|
13
13
|
|
|
14
14
|
let caught: unknown
|
|
15
15
|
|
|
@@ -167,3 +167,78 @@ describe('useJoltForegroundPing', () => {
|
|
|
167
167
|
expect(ping).toHaveBeenCalledTimes(1)
|
|
168
168
|
})
|
|
169
169
|
})
|
|
170
|
+
|
|
171
|
+
describe('useJoltChannel', () => {
|
|
172
|
+
type Listener = (_event: unknown) => void
|
|
173
|
+
const channelName = 'chat.people.1'
|
|
174
|
+
|
|
175
|
+
const buildJolt = () => {
|
|
176
|
+
const listeners: Listener[] = []
|
|
177
|
+
const subscription = {
|
|
178
|
+
channel: channelName,
|
|
179
|
+
bind: jest.fn((_event: string, listener: Listener) => {
|
|
180
|
+
listeners.push(listener)
|
|
181
|
+
return () => listeners.splice(listeners.indexOf(listener), 1)
|
|
182
|
+
}),
|
|
183
|
+
}
|
|
184
|
+
const jolt = {
|
|
185
|
+
subscriptions: [] as (typeof subscription)[],
|
|
186
|
+
subscribe: jest.fn(() => {
|
|
187
|
+
jolt.subscriptions.push(subscription)
|
|
188
|
+
return subscription
|
|
189
|
+
}),
|
|
190
|
+
// The real client unbinds every listener on the subscription when it unsubscribes.
|
|
191
|
+
unsubscribe: jest.fn(() => {
|
|
192
|
+
listeners.length = 0
|
|
193
|
+
jolt.subscriptions = []
|
|
194
|
+
}),
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return { jolt, emit: (event: unknown) => listeners.forEach(listener => listener(event)) }
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const ChannelProbe = ({ onEvent }: { onEvent: Listener }) => {
|
|
201
|
+
const channel = useJoltChannel(channelName)
|
|
202
|
+
useJoltEvent(channel, 'conversation.updated', onEvent)
|
|
203
|
+
return null
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const renderConsumers = (jolt: unknown, onEvent: Listener) => {
|
|
207
|
+
const session = new Session({ token: { access_token: 'a-real-token' } })
|
|
208
|
+
const queryClient = buildTestQueryClient({ defaultOptions: { queries: { gcTime: Infinity } } })
|
|
209
|
+
queryClient.setQueryData(['wss-url'], 'wss://jolt.test')
|
|
210
|
+
queryClient.setQueryData(['jolt-client', 'wss://jolt.test', 'a-real-token'], jolt)
|
|
211
|
+
|
|
212
|
+
const tree = (count: number) => (
|
|
213
|
+
<QueryClientProvider client={queryClient}>
|
|
214
|
+
<ChatContext.Provider value={{ session } as unknown as ChatContextValue}>
|
|
215
|
+
<Suspense fallback={null}>
|
|
216
|
+
{Array.from({ length: count }, (_, index) => (
|
|
217
|
+
<ChannelProbe key={index} onEvent={onEvent} />
|
|
218
|
+
))}
|
|
219
|
+
</Suspense>
|
|
220
|
+
</ChatContext.Provider>
|
|
221
|
+
</QueryClientProvider>
|
|
222
|
+
)
|
|
223
|
+
const result = render(tree(2))
|
|
224
|
+
|
|
225
|
+
return { setConsumers: (count: number) => result.rerender(tree(count)) }
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
it('keeps delivering events to the remaining consumer after another unmounts', async () => {
|
|
229
|
+
const { jolt, emit } = buildJolt()
|
|
230
|
+
const onEvent = jest.fn()
|
|
231
|
+
const { setConsumers } = renderConsumers(jolt, onEvent)
|
|
232
|
+
await waitFor(() => expect(jolt.subscribe).toHaveBeenCalledTimes(1))
|
|
233
|
+
|
|
234
|
+
setConsumers(1)
|
|
235
|
+
emit({ event: 'conversation.updated' })
|
|
236
|
+
|
|
237
|
+
expect(jolt.unsubscribe).not.toHaveBeenCalled()
|
|
238
|
+
expect(onEvent).toHaveBeenCalledTimes(1)
|
|
239
|
+
|
|
240
|
+
setConsumers(0)
|
|
241
|
+
|
|
242
|
+
expect(jolt.unsubscribe).toHaveBeenCalledWith(channelName)
|
|
243
|
+
})
|
|
244
|
+
})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { InfiniteData, useQueryClient } from '@tanstack/react-query'
|
|
2
|
-
import { ApiCollection,
|
|
2
|
+
import { ApiCollection, ConversationResource } from '../types'
|
|
3
3
|
import {
|
|
4
4
|
deleteRecordInPagesData,
|
|
5
5
|
updateRecordInPagesData,
|
|
@@ -21,16 +21,13 @@ export function useConversationsCache(args?: Partial<ConversationRequestArgs>) {
|
|
|
21
21
|
const fetchConversation = async (id: number) => {
|
|
22
22
|
if (!id) return
|
|
23
23
|
|
|
24
|
-
const { data
|
|
25
|
-
const { data } = await apiClient.chat.get<
|
|
26
|
-
url
|
|
27
|
-
data: {
|
|
28
|
-
fields: argsData.fields,
|
|
29
|
-
include: argsData.include,
|
|
30
|
-
},
|
|
24
|
+
const { url, data } = conversationsRequestArgs
|
|
25
|
+
const { data: conversations } = await apiClient.chat.get<ApiCollection<ConversationResource>>({
|
|
26
|
+
url,
|
|
27
|
+
data: { ...data, where: { ...data.where, id }, perPage: 1 },
|
|
31
28
|
})
|
|
32
29
|
|
|
33
|
-
return
|
|
30
|
+
return conversations[0]
|
|
34
31
|
}
|
|
35
32
|
|
|
36
33
|
const updateOrCreate = async (conversation: ConversationResource) => {
|
|
@@ -70,20 +67,6 @@ export function useConversationsCache(args?: Partial<ConversationRequestArgs>) {
|
|
|
70
67
|
)
|
|
71
68
|
}
|
|
72
69
|
|
|
73
|
-
const fetchAndUpdate = async ({ id }: { id: number }) => {
|
|
74
|
-
const conversation: ConversationResource = await fetchConversation(id).catch(c => c)
|
|
75
|
-
if (!conversation.id) return
|
|
76
|
-
|
|
77
|
-
updateOnly(conversation)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const fetchAndUpdateOrCreate = async ({ id }: { id: number }) => {
|
|
81
|
-
const conversation: ConversationResource = await fetchConversation(id).catch(c => c)
|
|
82
|
-
if (!conversation.id) return
|
|
83
|
-
|
|
84
|
-
updateOrCreate(conversation)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
70
|
const handleConversationDestroy = ({ id }: { id: number }) => {
|
|
88
71
|
if (!id) return
|
|
89
72
|
|
|
@@ -95,10 +78,32 @@ export function useConversationsCache(args?: Partial<ConversationRequestArgs>) {
|
|
|
95
78
|
)
|
|
96
79
|
}
|
|
97
80
|
|
|
81
|
+
const fetchAndUpdate = async ({ id }: { id: number }) => {
|
|
82
|
+
const conversation = await fetchConversation(id).catch(() => undefined)
|
|
83
|
+
if (!conversation) return
|
|
84
|
+
|
|
85
|
+
updateOnly(conversation)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const fetchAndSync = async ({ id }: { id: number }) => {
|
|
89
|
+
let conversation: ConversationResource | undefined
|
|
90
|
+
try {
|
|
91
|
+
conversation = await fetchConversation(id)
|
|
92
|
+
} catch {
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (conversation) {
|
|
97
|
+
updateOrCreate(conversation)
|
|
98
|
+
} else {
|
|
99
|
+
handleConversationDestroy({ id })
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
98
103
|
return {
|
|
99
104
|
create: updateOrCreate,
|
|
100
105
|
destroy: handleConversationDestroy,
|
|
101
|
-
|
|
106
|
+
fetchSync: fetchAndSync,
|
|
102
107
|
fetchUpdate: fetchAndUpdate,
|
|
103
108
|
invalidate: () => queryClient.invalidateQueries({ queryKey: conversationQueryKey }),
|
|
104
109
|
update: updateOrCreate,
|
|
@@ -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) {
|
package/src/utils/styles.ts
CHANGED
|
@@ -6,6 +6,7 @@ export const MAX_FONT_SIZE_MULTIPLIER_LANDMARK = 1.2 // Use in headers, footers,
|
|
|
6
6
|
|
|
7
7
|
export const CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL = 16 // TODO: move to `screens/conversation/utils/styles` when `/screens/conversation` is created
|
|
8
8
|
export const MESSAGE_AUTHOR_AVATAR_COLUMN_WIDTH = 32 // TODO: move to `screens/conversation/utils/styles` when `/screens/conversation` is created
|
|
9
|
+
export const MESSAGE_BACKGROUND_OUTSET = 2
|
|
9
10
|
|
|
10
11
|
export const platformFontWeightMedium = Platform.select({
|
|
11
12
|
ios: tokens.fontWeightMedium,
|