@planningcenter/chat-react-native 4.4.1-qa-staging.16 → 4.4.1-qa-staging.18
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/archived_results_footer.d.ts +6 -0
- package/build/components/conversations/archived_results_footer.d.ts.map +1 -0
- package/build/components/conversations/archived_results_footer.js +51 -0
- package/build/components/conversations/archived_results_footer.js.map +1 -0
- package/build/components/conversations/conversation_actions.d.ts.map +1 -1
- package/build/components/conversations/conversation_actions.js +241 -206
- package/build/components/conversations/conversation_actions.js.map +1 -1
- package/build/components/conversations/conversation_preview.d.ts.map +1 -1
- package/build/components/conversations/conversation_preview.js +99 -93
- package/build/components/conversations/conversation_preview.js.map +1 -1
- package/build/components/conversations/conversations.d.ts.map +1 -1
- package/build/components/conversations/conversations.js +65 -40
- package/build/components/conversations/conversations.js.map +1 -1
- package/build/components/conversations/conversations_blank_state.d.ts.map +1 -1
- package/build/components/conversations/conversations_blank_state.js +55 -12
- package/build/components/conversations/conversations_blank_state.js.map +1 -1
- package/build/components/conversations/filtered_results_footer.d.ts +2 -0
- package/build/components/conversations/filtered_results_footer.d.ts.map +1 -0
- package/build/components/conversations/filtered_results_footer.js +77 -0
- package/build/components/conversations/filtered_results_footer.js.map +1 -0
- package/build/components/display/filter_dropdown.d.ts +2 -1
- package/build/components/display/filter_dropdown.d.ts.map +1 -1
- package/build/components/display/filter_dropdown.js +63 -58
- package/build/components/display/filter_dropdown.js.map +1 -1
- package/build/components/display/icon_tile.d.ts +12 -0
- package/build/components/display/icon_tile.d.ts.map +1 -0
- package/build/components/display/icon_tile.js +109 -0
- package/build/components/display/icon_tile.js.map +1 -0
- package/build/components/display/index.d.ts +1 -0
- package/build/components/display/index.d.ts.map +1 -1
- package/build/components/display/index.js +1 -0
- package/build/components/display/index.js.map +1 -1
- package/build/contexts/conversations_context.d.ts.map +1 -1
- package/build/contexts/conversations_context.js +21 -18
- package/build/contexts/conversations_context.js.map +1 -1
- package/build/hooks/index.d.ts +2 -0
- package/build/hooks/index.d.ts.map +1 -1
- package/build/hooks/index.js +2 -0
- package/build/hooks/index.js.map +1 -1
- package/build/hooks/use_can_browse_archived_conversations.d.ts +2 -0
- package/build/hooks/use_can_browse_archived_conversations.d.ts.map +1 -0
- package/build/hooks/use_can_browse_archived_conversations.js +29 -0
- package/build/hooks/use_can_browse_archived_conversations.js.map +1 -0
- package/build/hooks/use_clear_conversation_filters.d.ts +2 -0
- package/build/hooks/use_clear_conversation_filters.d.ts.map +1 -0
- package/build/hooks/use_clear_conversation_filters.js +25 -0
- package/build/hooks/use_clear_conversation_filters.js.map +1 -0
- package/build/hooks/use_conversation.d.ts.map +1 -1
- package/build/hooks/use_conversation.js +32 -24
- package/build/hooks/use_conversation.js.map +1 -1
- package/build/hooks/use_current_person.d.ts.map +1 -1
- package/build/hooks/use_current_person.js +1 -1
- package/build/hooks/use_current_person.js.map +1 -1
- package/build/hooks/use_jolt.d.ts.map +1 -1
- package/build/hooks/use_jolt.js +29 -8
- package/build/hooks/use_jolt.js.map +1 -1
- package/build/hooks/use_manual_archiving_enabled.d.ts +2 -0
- package/build/hooks/use_manual_archiving_enabled.d.ts.map +1 -0
- package/build/hooks/use_manual_archiving_enabled.js +18 -0
- package/build/hooks/use_manual_archiving_enabled.js.map +1 -0
- package/build/hooks/use_reduce_motion.d.ts.map +1 -1
- package/build/hooks/use_reduce_motion.js +2 -1
- package/build/hooks/use_reduce_motion.js.map +1 -1
- package/build/screens/conversation_details_screen.d.ts.map +1 -1
- package/build/screens/conversation_details_screen.js +2 -1
- package/build/screens/conversation_details_screen.js.map +1 -1
- package/build/screens/conversation_filters/components/conversation_filters.d.ts.map +1 -1
- package/build/screens/conversation_filters/components/conversation_filters.js +88 -18
- package/build/screens/conversation_filters/components/conversation_filters.js.map +1 -1
- package/build/screens/conversation_filters/components/rows.d.ts +9 -0
- package/build/screens/conversation_filters/components/rows.d.ts.map +1 -1
- package/build/screens/conversation_filters/components/rows.js +92 -1
- package/build/screens/conversation_filters/components/rows.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/filter_types.d.ts +1 -0
- package/build/screens/conversation_filters/filter_types.d.ts.map +1 -1
- package/build/screens/conversation_filters/filter_types.js +1 -0
- package/build/screens/conversation_filters/filter_types.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/conversation_filters_screen.d.ts +1 -0
- package/build/screens/conversation_filters_screen.d.ts.map +1 -1
- package/build/screens/conversation_filters_screen.js +79 -48
- package/build/screens/conversation_filters_screen.js.map +1 -1
- package/build/screens/conversations/components/filter_dropdown_list_header_component.d.ts +2 -0
- package/build/screens/conversations/components/filter_dropdown_list_header_component.d.ts.map +1 -0
- package/build/screens/conversations/components/filter_dropdown_list_header_component.js +587 -0
- package/build/screens/conversations/components/filter_dropdown_list_header_component.js.map +1 -0
- package/build/screens/conversations/components/list_header_component.d.ts.map +1 -1
- package/build/screens/conversations/components/list_header_component.js +95 -90
- package/build/screens/conversations/components/list_header_component.js.map +1 -1
- package/build/screens/conversations/components/search_list_header_component.d.ts.map +1 -1
- package/build/screens/conversations/components/search_list_header_component.js +100 -95
- package/build/screens/conversations/components/search_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 +15 -2
- package/build/screens/conversations/conversations_screen.js.map +1 -1
- package/build/types/resources/person.d.ts +1 -0
- package/build/types/resources/person.d.ts.map +1 -1
- package/build/types/resources/person.js.map +1 -1
- package/build/utils/date.d.ts.map +1 -1
- package/build/utils/date.js +5 -10
- package/build/utils/date.js.map +1 -1
- package/build/utils/native_adapters/menu.d.ts +1 -0
- package/build/utils/native_adapters/menu.d.ts.map +1 -1
- package/build/utils/native_adapters/menu.js +1 -0
- package/build/utils/native_adapters/menu.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/vendor/font_awesome_pro_solid_icons.d.ts +1 -0
- package/build/vendor/font_awesome_pro_solid_icons.d.ts.map +1 -1
- package/build/vendor/font_awesome_pro_solid_icons.js +5 -0
- package/build/vendor/font_awesome_pro_solid_icons.js.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/hooks/use_can_browse_archived_conversations.test.tsx +98 -0
- package/src/__tests__/screens/conversation_filters/components/conversation_filters.test.tsx +132 -0
- package/src/__tests__/screens/conversation_filters_screen.test.tsx +76 -0
- package/src/__tests__/screens/conversation_new/components/services_form.test.tsx +1 -0
- package/src/components/conversations/__tests__/conversation_actions.test.tsx +67 -0
- package/src/components/conversations/__tests__/conversation_preview.test.tsx +38 -3
- package/src/components/conversations/__tests__/conversations.test.tsx +82 -0
- package/src/components/conversations/__tests__/conversations_blank_state.test.tsx +73 -0
- package/src/components/conversations/archived_results_footer.tsx +36 -0
- package/src/components/conversations/conversation_actions.tsx +27 -19
- package/src/components/conversations/conversation_preview.tsx +30 -10
- package/src/components/conversations/conversations.tsx +25 -2
- package/src/components/conversations/conversations_blank_state.tsx +21 -0
- package/src/components/conversations/filtered_results_footer.tsx +38 -0
- package/src/components/display/__tests__/filter_dropdown.test.tsx +9 -1
- package/src/components/display/filter_dropdown.tsx +14 -13
- package/src/components/display/icon_tile.tsx +54 -0
- package/src/components/display/index.ts +1 -0
- package/src/contexts/conversations_context.tsx +2 -1
- package/src/hooks/__tests__/use_conversation_archive.test.tsx +22 -0
- package/src/hooks/__tests__/use_jolt.test.tsx +30 -8
- package/src/hooks/__tests__/use_reduce_motion.test.tsx +13 -1
- package/src/hooks/index.ts +2 -0
- package/src/hooks/use_can_browse_archived_conversations.ts +17 -0
- package/src/hooks/use_clear_conversation_filters.ts +16 -0
- package/src/hooks/use_conversation.ts +4 -1
- package/src/hooks/use_current_person.ts +1 -0
- package/src/hooks/use_jolt.ts +33 -8
- package/src/hooks/use_manual_archiving_enabled.ts +10 -0
- package/src/hooks/use_reduce_motion.ts +2 -1
- package/src/screens/conversation_details_screen.tsx +2 -3
- package/src/screens/conversation_filters/__tests__/conversation_filters.test.tsx +101 -0
- package/src/screens/conversation_filters/components/conversation_filters.tsx +40 -7
- package/src/screens/conversation_filters/components/rows.tsx +29 -1
- package/src/screens/conversation_filters/context/conversation_filter_context.tsx +4 -0
- package/src/screens/conversation_filters/filter_types.ts +1 -0
- package/src/screens/conversation_filters/screen_props.ts +1 -0
- package/src/screens/conversation_filters_screen.tsx +25 -5
- package/src/screens/conversations/__tests__/conversations_screen.test.tsx +52 -0
- package/src/screens/conversations/components/__tests__/filter_dropdown_list_header_component.test.tsx +248 -0
- package/src/screens/conversations/components/__tests__/list_header_component.test.tsx +21 -4
- package/src/screens/conversations/components/filter_dropdown_list_header_component.tsx +284 -0
- package/src/screens/conversations/components/list_header_component.tsx +17 -12
- package/src/screens/conversations/components/search_list_header_component.tsx +17 -12
- package/src/screens/conversations/conversations_screen.tsx +15 -2
- package/src/types/resources/person.ts +1 -0
- package/src/utils/__tests__/conversation.test.ts +1 -0
- package/src/utils/__tests__/date.test.ts +4 -0
- package/src/utils/date.ts +5 -4
- package/src/utils/native_adapters/__tests__/menu.test.tsx +6 -2
- package/src/utils/native_adapters/menu.tsx +2 -0
- package/src/utils/request/conversation.ts +4 -0
- package/src/vendor/font_awesome_pro_solid_icons.ts +12 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { fireEvent, render, screen } from '@testing-library/react-native'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { Pressable, Text } from 'react-native'
|
|
4
|
+
import {
|
|
5
|
+
AudioAdapter,
|
|
6
|
+
ChatAdapters,
|
|
7
|
+
ClipboardAdapter,
|
|
8
|
+
ImagePickerAdapter,
|
|
9
|
+
MenuAction,
|
|
10
|
+
MenuAdapter,
|
|
11
|
+
MenuViewProps,
|
|
12
|
+
VideoAdapter,
|
|
13
|
+
} from '../../../../utils/native_adapters'
|
|
14
|
+
import { FilterDropdownListHeaderComponent } from '../filter_dropdown_list_header_component'
|
|
15
|
+
|
|
16
|
+
const mockNavigate = jest.fn()
|
|
17
|
+
const mockSetParams = jest.fn()
|
|
18
|
+
const mockRouteParams: Record<string, unknown> = {}
|
|
19
|
+
const mockCanDisplayGroups = jest.fn()
|
|
20
|
+
const mockSearchEnabled = jest.fn()
|
|
21
|
+
const mockCanBrowseArchived = jest.fn()
|
|
22
|
+
const mockGroupsByGraphId: Record<string, { name: string }> = {}
|
|
23
|
+
|
|
24
|
+
jest.mock('@react-navigation/native', () => ({
|
|
25
|
+
...jest.requireActual('@react-navigation/native'),
|
|
26
|
+
useNavigation: () => ({ setParams: mockSetParams, navigate: mockNavigate }),
|
|
27
|
+
useRoute: () => ({ params: mockRouteParams }),
|
|
28
|
+
}))
|
|
29
|
+
|
|
30
|
+
jest.mock('../../../../hooks/use_api', () => ({
|
|
31
|
+
useApiGet: ({ url }: { url: string }, { enabled }: { enabled: boolean }) => ({
|
|
32
|
+
data: enabled ? mockGroupsByGraphId[url.replace('/me/groups/', '')] : undefined,
|
|
33
|
+
}),
|
|
34
|
+
}))
|
|
35
|
+
jest.mock('../../../../hooks/use_groups', () => ({
|
|
36
|
+
useCanDisplayGroups: (args: { source_type: string }) => mockCanDisplayGroups(args),
|
|
37
|
+
}))
|
|
38
|
+
jest.mock('../../../../hooks/use_direct_messages_eligible', () => ({
|
|
39
|
+
useDirectMessagesEligible: () => true,
|
|
40
|
+
}))
|
|
41
|
+
jest.mock('../../../../hooks/use_has_direct_messages', () => ({
|
|
42
|
+
useHasDirectMessages: () => true,
|
|
43
|
+
}))
|
|
44
|
+
jest.mock('../../../../hooks/use_can_browse_archived_conversations', () => ({
|
|
45
|
+
useCanBrowseArchivedConversations: () => mockCanBrowseArchived(),
|
|
46
|
+
}))
|
|
47
|
+
jest.mock('../../../../hooks/use_chat_permissions', () => ({
|
|
48
|
+
useCanCreateConversations: () => true,
|
|
49
|
+
}))
|
|
50
|
+
jest.mock('../../../../hooks/use_new_conversation_entry', () => ({
|
|
51
|
+
useNewConversationEntry: () => 'select_type',
|
|
52
|
+
}))
|
|
53
|
+
jest.mock('../../../../hooks/use_current_person', () => ({
|
|
54
|
+
useCurrentPersonCache: () => ({ update: jest.fn() }),
|
|
55
|
+
}))
|
|
56
|
+
jest.mock('../../../../hooks/use_conversations_actions', () => ({
|
|
57
|
+
useMarkAllRead: () => ({ markAllRead: jest.fn(), isPending: false }),
|
|
58
|
+
}))
|
|
59
|
+
jest.mock('../../../../hooks/use_new_conversation_from_filter', () => ({
|
|
60
|
+
useNewConversationFromFilter: () => undefined,
|
|
61
|
+
}))
|
|
62
|
+
jest.mock('../../../../hooks/use_search_enabled', () => ({
|
|
63
|
+
useSearchEnabled: () => mockSearchEnabled(),
|
|
64
|
+
}))
|
|
65
|
+
|
|
66
|
+
const FakeMenuView = ({ actions, onPressAction, children }: MenuViewProps) => {
|
|
67
|
+
const flatten = (items: MenuAction[]): MenuAction[] =>
|
|
68
|
+
items.flatMap(item => (item.displayInline && item.subactions ? item.subactions : [item]))
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<>
|
|
72
|
+
{children}
|
|
73
|
+
{flatten(actions).map(action => (
|
|
74
|
+
<Pressable
|
|
75
|
+
key={action.id}
|
|
76
|
+
accessibilityRole="menuitem"
|
|
77
|
+
accessibilityLabel={action.title}
|
|
78
|
+
accessibilityState={{ selected: action.state === 'on' }}
|
|
79
|
+
onPress={() => onPressAction?.({ nativeEvent: { event: action.id ?? action.title } })}
|
|
80
|
+
>
|
|
81
|
+
<Text>{action.title}</Text>
|
|
82
|
+
</Pressable>
|
|
83
|
+
))}
|
|
84
|
+
</>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
beforeAll(() => {
|
|
89
|
+
ChatAdapters.configure({
|
|
90
|
+
clipboard: new ClipboardAdapter({
|
|
91
|
+
getStringAsync: async () => '',
|
|
92
|
+
setStringAsync: async () => {},
|
|
93
|
+
}),
|
|
94
|
+
audio: new AudioAdapter({ useAudio: () => ({}) as any }),
|
|
95
|
+
video: new VideoAdapter({ Player: (() => null) as any }),
|
|
96
|
+
imagePicker: new ImagePickerAdapter({
|
|
97
|
+
openCameraAsync: async () => ({ canceled: true, assets: null }),
|
|
98
|
+
openImageLibraryAsync: async () => ({ canceled: true, assets: null }),
|
|
99
|
+
}),
|
|
100
|
+
menu: new MenuAdapter({ View: FakeMenuView }),
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
const setCapabilities = ({ groups, teams }: { groups: boolean; teams: boolean }) => {
|
|
105
|
+
mockCanDisplayGroups.mockImplementation(({ source_type }: { source_type: string }) =>
|
|
106
|
+
source_type === 'Group' ? groups : teams
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const setRouteParams = (params: Record<string, unknown>) => {
|
|
111
|
+
Object.keys(mockRouteParams).forEach(key => delete mockRouteParams[key])
|
|
112
|
+
Object.assign(mockRouteParams, params)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const menuItemNames = () =>
|
|
116
|
+
screen.getAllByRole('menuitem').map(item => item.props.accessibilityLabel)
|
|
117
|
+
|
|
118
|
+
describe('FilterDropdownListHeaderComponent', () => {
|
|
119
|
+
beforeEach(() => {
|
|
120
|
+
mockNavigate.mockClear()
|
|
121
|
+
mockSetParams.mockClear()
|
|
122
|
+
mockSearchEnabled.mockReturnValue(true)
|
|
123
|
+
mockCanBrowseArchived.mockReturnValue(false)
|
|
124
|
+
setCapabilities({ groups: true, teams: true })
|
|
125
|
+
setRouteParams({})
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('offers every filter the person can use, then More filters', () => {
|
|
129
|
+
render(<FilterDropdownListHeaderComponent />)
|
|
130
|
+
|
|
131
|
+
expect(menuItemNames()).toEqual(['All', 'Groups', 'Teams', 'DMs', 'More filters'])
|
|
132
|
+
expect(screen.getByRole('button', { name: 'All conversations' })).toBeTruthy()
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('leaves out Teams and More filters when only DMs are filterable', () => {
|
|
136
|
+
setCapabilities({ groups: false, teams: false })
|
|
137
|
+
|
|
138
|
+
render(<FilterDropdownListHeaderComponent />)
|
|
139
|
+
|
|
140
|
+
expect(menuItemNames()).toEqual(['All', 'DMs'])
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it('selecting Groups filters to group conversations', () => {
|
|
144
|
+
render(<FilterDropdownListHeaderComponent />)
|
|
145
|
+
|
|
146
|
+
fireEvent.press(screen.getByRole('menuitem', { name: 'Groups' }))
|
|
147
|
+
|
|
148
|
+
expect(mockSetParams).toHaveBeenCalledWith(
|
|
149
|
+
expect.objectContaining({ group_source_app_name: 'Groups', show_direct_messages: undefined })
|
|
150
|
+
)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('labels the trigger with the active filter and checks it', () => {
|
|
154
|
+
setRouteParams({ group_source_app_name: 'Services' })
|
|
155
|
+
|
|
156
|
+
render(<FilterDropdownListHeaderComponent />)
|
|
157
|
+
|
|
158
|
+
expect(screen.getByRole('button', { name: 'Teams' })).toBeTruthy()
|
|
159
|
+
expect(screen.getByRole('menuitem', { name: 'Teams' }).props.accessibilityState.selected).toBe(
|
|
160
|
+
true
|
|
161
|
+
)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it("labels the trigger with the group's name when filtered to one group from More filters", () => {
|
|
165
|
+
mockGroupsByGraphId['Group/1'] = { name: 'Youth Group' }
|
|
166
|
+
setRouteParams({ chat_group_graph_id: 'Group/1' })
|
|
167
|
+
|
|
168
|
+
render(<FilterDropdownListHeaderComponent />)
|
|
169
|
+
|
|
170
|
+
expect(screen.getByRole('button', { name: 'Youth Group' })).toBeTruthy()
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('falls back to the route title while the group is still loading', () => {
|
|
174
|
+
delete mockGroupsByGraphId['Group/2']
|
|
175
|
+
setRouteParams({ chat_group_graph_id: 'Group/2', title: 'Youth Group' })
|
|
176
|
+
|
|
177
|
+
render(<FilterDropdownListHeaderComponent />)
|
|
178
|
+
|
|
179
|
+
expect(screen.getByRole('button', { name: 'Youth Group' })).toBeTruthy()
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it('offers More filters when archived conversations are the only extra filter', () => {
|
|
183
|
+
setCapabilities({ groups: false, teams: false })
|
|
184
|
+
mockCanBrowseArchived.mockReturnValue(true)
|
|
185
|
+
|
|
186
|
+
render(<FilterDropdownListHeaderComponent />)
|
|
187
|
+
|
|
188
|
+
expect(menuItemNames()).toEqual(['All', 'DMs', 'More filters'])
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
it('labels the archived list and carries it into More filters', () => {
|
|
192
|
+
setRouteParams({ show_archived: true })
|
|
193
|
+
|
|
194
|
+
render(<FilterDropdownListHeaderComponent />)
|
|
195
|
+
|
|
196
|
+
expect(screen.getByRole('button', { name: 'Archived' })).toBeTruthy()
|
|
197
|
+
|
|
198
|
+
fireEvent.press(screen.getByRole('menuitem', { name: 'More filters' }))
|
|
199
|
+
|
|
200
|
+
expect(mockNavigate).toHaveBeenCalledWith(
|
|
201
|
+
'ConversationFilters',
|
|
202
|
+
expect.objectContaining({ show_archived: true })
|
|
203
|
+
)
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
it('leaves the archived list when another filter is picked', () => {
|
|
207
|
+
setRouteParams({ show_archived: true })
|
|
208
|
+
|
|
209
|
+
render(<FilterDropdownListHeaderComponent />)
|
|
210
|
+
|
|
211
|
+
fireEvent.press(screen.getByRole('menuitem', { name: 'Groups' }))
|
|
212
|
+
|
|
213
|
+
expect(mockSetParams).toHaveBeenCalledWith(
|
|
214
|
+
expect.objectContaining({ group_source_app_name: 'Groups', show_archived: undefined })
|
|
215
|
+
)
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it('More filters opens the filters sheet', () => {
|
|
219
|
+
render(<FilterDropdownListHeaderComponent />)
|
|
220
|
+
|
|
221
|
+
fireEvent.press(screen.getByRole('menuitem', { name: 'More filters' }))
|
|
222
|
+
|
|
223
|
+
expect(mockNavigate).toHaveBeenCalledWith('ConversationFilters', expect.any(Object))
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
it('shows the search button only when search is enabled', () => {
|
|
227
|
+
mockSearchEnabled.mockReturnValue(false)
|
|
228
|
+
const view = render(<FilterDropdownListHeaderComponent />)
|
|
229
|
+
|
|
230
|
+
expect(view.queryByTestId('conversations-search-button')).toBeNull()
|
|
231
|
+
|
|
232
|
+
mockSearchEnabled.mockReturnValue(true)
|
|
233
|
+
view.rerender(<FilterDropdownListHeaderComponent />)
|
|
234
|
+
|
|
235
|
+
fireEvent.press(view.getByTestId('conversations-search-button'))
|
|
236
|
+
|
|
237
|
+
expect(mockNavigate).toHaveBeenCalledWith('Search')
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it('renders the search button after the other actions', () => {
|
|
241
|
+
const view = render(<FilterDropdownListHeaderComponent />)
|
|
242
|
+
|
|
243
|
+
const buttons = view.getAllByRole('button').map(button => button.props.accessibilityLabel)
|
|
244
|
+
|
|
245
|
+
expect(buttons.indexOf('Search')).toBeGreaterThan(buttons.indexOf('Chat settings'))
|
|
246
|
+
expect(buttons.indexOf('Search')).toBeGreaterThan(buttons.indexOf('New conversation'))
|
|
247
|
+
})
|
|
248
|
+
})
|
|
@@ -3,13 +3,12 @@ import React from 'react'
|
|
|
3
3
|
import { ListHeaderComponent } from '../list_header_component'
|
|
4
4
|
|
|
5
5
|
const mockSetParams = jest.fn()
|
|
6
|
+
let mockParams: Record<string, unknown> = {}
|
|
6
7
|
|
|
7
8
|
jest.mock('@react-navigation/native', () => ({
|
|
8
9
|
...jest.requireActual('@react-navigation/native'),
|
|
9
10
|
useNavigation: () => ({ setParams: mockSetParams, navigate: jest.fn() }),
|
|
10
|
-
useRoute: () => ({
|
|
11
|
-
params: { chat_group_graph_id: 'Groups/Group/1', title: 'Waco Family Group' },
|
|
12
|
-
}),
|
|
11
|
+
useRoute: () => ({ params: mockParams }),
|
|
13
12
|
}))
|
|
14
13
|
|
|
15
14
|
jest.mock('../../../../hooks/use_groups', () => ({
|
|
@@ -45,10 +44,14 @@ const clearedFilter = {
|
|
|
45
44
|
chat_group_graph_id: undefined,
|
|
46
45
|
group_source_app_name: undefined,
|
|
47
46
|
show_direct_messages: undefined,
|
|
47
|
+
show_archived: undefined,
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
describe('ListHeaderComponent filter toggles', () => {
|
|
51
|
-
beforeEach(() =>
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
mockSetParams.mockClear()
|
|
53
|
+
mockParams = { chat_group_graph_id: 'Groups/Group/1', title: 'Waco Family Group' }
|
|
54
|
+
})
|
|
52
55
|
|
|
53
56
|
it.each([
|
|
54
57
|
['Show all conversations', clearedFilter],
|
|
@@ -64,3 +67,17 @@ describe('ListHeaderComponent filter toggles', () => {
|
|
|
64
67
|
expect(mockSetParams.mock.calls[0][0]).toStrictEqual(expected)
|
|
65
68
|
})
|
|
66
69
|
})
|
|
70
|
+
|
|
71
|
+
describe('ListHeaderComponent mark all read', () => {
|
|
72
|
+
it('shows Mark all read on the active list', () => {
|
|
73
|
+
mockParams = {}
|
|
74
|
+
|
|
75
|
+
expect(render(<ListHeaderComponent />).queryByText('Mark all read')).not.toBeNull()
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('hides Mark all read on the archived list', () => {
|
|
79
|
+
mockParams = { show_archived: true }
|
|
80
|
+
|
|
81
|
+
expect(render(<ListHeaderComponent />).queryByText('Mark all read')).toBeNull()
|
|
82
|
+
})
|
|
83
|
+
})
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native'
|
|
2
|
+
import React, { useCallback, useMemo } from 'react'
|
|
3
|
+
import { Alert, StyleSheet, View } from 'react-native'
|
|
4
|
+
import { FilterDropdown, FilterDropdownOption, TextButton } from '../../../components'
|
|
5
|
+
import {
|
|
6
|
+
useAtFontScaleBreakpoint,
|
|
7
|
+
useCanBrowseArchivedConversations,
|
|
8
|
+
useCanCreateConversations,
|
|
9
|
+
useCurrentPersonCache,
|
|
10
|
+
useDirectMessagesEligible,
|
|
11
|
+
useHasDirectMessages,
|
|
12
|
+
useNewConversationEntry,
|
|
13
|
+
useTheme,
|
|
14
|
+
} from '../../../hooks'
|
|
15
|
+
import { useApiGet } from '../../../hooks/use_api'
|
|
16
|
+
import { useMarkAllRead } from '../../../hooks/use_conversations_actions'
|
|
17
|
+
import { useCanDisplayGroups } from '../../../hooks/use_groups'
|
|
18
|
+
import { useNewConversationFromFilter } from '../../../hooks/use_new_conversation_from_filter'
|
|
19
|
+
import { useSearchEnabled } from '../../../hooks/use_search_enabled'
|
|
20
|
+
import { GraphId, GroupResource } from '../../../types/resources/group_resource'
|
|
21
|
+
import { MAX_FONT_SIZE_MULTIPLIER_LANDMARK } from '../../../utils'
|
|
22
|
+
import { Haptic } from '../../../utils/native_adapters'
|
|
23
|
+
import { ConversationFiltersParams } from '../../conversation_filters/screen_props'
|
|
24
|
+
import { ConversationsScreenProps } from '../conversations_screen'
|
|
25
|
+
import { ListHeaderActionButton } from './list_header_action_button'
|
|
26
|
+
|
|
27
|
+
const ROW_PADDING_HORIZONTAL = 16
|
|
28
|
+
const ALL_CONVERSATIONS_LABEL = 'All conversations'
|
|
29
|
+
const ARCHIVED_LABEL = 'Archived'
|
|
30
|
+
|
|
31
|
+
enum FilterTypes {
|
|
32
|
+
All = 'All',
|
|
33
|
+
Groups = 'Groups',
|
|
34
|
+
Teams = 'Teams',
|
|
35
|
+
DMs = 'DMs',
|
|
36
|
+
More = 'More',
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const FILTER_PARAMS: Record<Exclude<FilterTypes, FilterTypes.More>, ConversationFiltersParams> = {
|
|
40
|
+
[FilterTypes.All]: {},
|
|
41
|
+
[FilterTypes.Groups]: { group_source_app_name: 'Groups' },
|
|
42
|
+
[FilterTypes.Teams]: { group_source_app_name: 'Services' },
|
|
43
|
+
[FilterTypes.DMs]: { show_direct_messages: true },
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const FilterDropdownListHeaderComponent = () => {
|
|
47
|
+
const navigation = useNavigation()
|
|
48
|
+
const canFilterByTeams = useCanDisplayGroups({ source_app_name: 'Services', source_type: 'Team' })
|
|
49
|
+
const canFilterByGroups = useCanDisplayGroups({ source_app_name: 'Groups', source_type: 'Group' })
|
|
50
|
+
const directMessagesEligible = useDirectMessagesEligible()
|
|
51
|
+
const hasDirectMessages = useHasDirectMessages()
|
|
52
|
+
const canFilterByDMs = directMessagesEligible && hasDirectMessages
|
|
53
|
+
const canBrowseArchived = useCanBrowseArchivedConversations()
|
|
54
|
+
const searchEnabled = useSearchEnabled()
|
|
55
|
+
const route = useRoute<RouteProp<ConversationsScreenProps['route']>>()
|
|
56
|
+
const {
|
|
57
|
+
title,
|
|
58
|
+
chat_group_graph_id,
|
|
59
|
+
group_source_app_name = '',
|
|
60
|
+
show_direct_messages,
|
|
61
|
+
show_archived,
|
|
62
|
+
} = route.params || {}
|
|
63
|
+
const currentPersonCache = useCurrentPersonCache()
|
|
64
|
+
const { markAllRead, isPending } = useMarkAllRead()
|
|
65
|
+
const canCreateConversations = useCanCreateConversations()
|
|
66
|
+
const entryMode = useNewConversationEntry()
|
|
67
|
+
const newConversationFromFilter = useNewConversationFromFilter(
|
|
68
|
+
chat_group_graph_id as GraphId | undefined
|
|
69
|
+
)
|
|
70
|
+
const { data: group } = useApiGet<GroupResource>(
|
|
71
|
+
{ url: `/me/groups/${chat_group_graph_id}`, data: { fields: { Group: [] } }, app: 'chat' },
|
|
72
|
+
{ enabled: !!chat_group_graph_id }
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
const active: FilterTypes = useMemo(() => {
|
|
76
|
+
if (chat_group_graph_id || show_archived) {
|
|
77
|
+
return FilterTypes.More
|
|
78
|
+
} else if (/groups/i.test(group_source_app_name)) {
|
|
79
|
+
return FilterTypes.Groups
|
|
80
|
+
} else if (/services/i.test(group_source_app_name)) {
|
|
81
|
+
return FilterTypes.Teams
|
|
82
|
+
} else if (show_direct_messages) {
|
|
83
|
+
return FilterTypes.DMs
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return FilterTypes.All
|
|
87
|
+
}, [chat_group_graph_id, group_source_app_name, show_direct_messages, show_archived])
|
|
88
|
+
|
|
89
|
+
const options: FilterDropdownOption[] = useMemo(
|
|
90
|
+
() => [
|
|
91
|
+
{ id: FilterTypes.All, label: FilterTypes.All },
|
|
92
|
+
...(canFilterByGroups ? [{ id: FilterTypes.Groups, label: FilterTypes.Groups }] : []),
|
|
93
|
+
...(canFilterByTeams ? [{ id: FilterTypes.Teams, label: FilterTypes.Teams }] : []),
|
|
94
|
+
...(canFilterByDMs ? [{ id: FilterTypes.DMs, label: FilterTypes.DMs }] : []),
|
|
95
|
+
],
|
|
96
|
+
[canFilterByGroups, canFilterByTeams, canFilterByDMs]
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
const triggerLabel = useMemo(() => {
|
|
100
|
+
if (chat_group_graph_id) return group?.name || title || ALL_CONVERSATIONS_LABEL
|
|
101
|
+
if (show_archived) return ARCHIVED_LABEL
|
|
102
|
+
if (active === FilterTypes.All) return ALL_CONVERSATIONS_LABEL
|
|
103
|
+
|
|
104
|
+
return active
|
|
105
|
+
}, [chat_group_graph_id, group?.name, title, show_archived, active])
|
|
106
|
+
|
|
107
|
+
const handleNewConversationNavigation = useCallback(() => {
|
|
108
|
+
if (newConversationFromFilter || entryMode === 'select_type') {
|
|
109
|
+
return navigation.navigate('ConversationSelectType', {
|
|
110
|
+
...route.params,
|
|
111
|
+
newConversationFromFilter,
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (entryMode === 'groups') {
|
|
116
|
+
return navigation.navigate('New', {
|
|
117
|
+
screen: 'ConversationSelectGroupRecipients',
|
|
118
|
+
params: { ...route.params },
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (entryMode === 'teams') {
|
|
123
|
+
return navigation.navigate('New', {
|
|
124
|
+
screen: 'ConversationSelectTeamsILeadRecipients',
|
|
125
|
+
params: { ...route.params },
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (entryMode === 'direct_message') {
|
|
130
|
+
return navigation.navigate('New', {
|
|
131
|
+
screen: 'ConversationSelectDirectMessageRecipients',
|
|
132
|
+
params: { ...route.params },
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
}, [navigation, route.params, entryMode, newConversationFromFilter])
|
|
136
|
+
|
|
137
|
+
const handleSetFilter = useCallback(
|
|
138
|
+
(params: ConversationFiltersParams) => {
|
|
139
|
+
navigation.setParams({
|
|
140
|
+
title: undefined,
|
|
141
|
+
chat_group_graph_id: undefined,
|
|
142
|
+
group_source_app_name: undefined,
|
|
143
|
+
show_direct_messages: undefined,
|
|
144
|
+
show_archived: undefined,
|
|
145
|
+
...params,
|
|
146
|
+
})
|
|
147
|
+
},
|
|
148
|
+
[navigation]
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
const handleSelectFilter = useCallback(
|
|
152
|
+
(id: string) => {
|
|
153
|
+
const params = FILTER_PARAMS[id as keyof typeof FILTER_PARAMS]
|
|
154
|
+
if (params) handleSetFilter(params)
|
|
155
|
+
},
|
|
156
|
+
[handleSetFilter]
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
const handleSearch = useCallback(() => {
|
|
160
|
+
navigation.navigate('Search')
|
|
161
|
+
}, [navigation])
|
|
162
|
+
|
|
163
|
+
const handleChatSettings = useCallback(() => {
|
|
164
|
+
navigation.navigate('ChatSettings', {})
|
|
165
|
+
}, [navigation])
|
|
166
|
+
|
|
167
|
+
const handleOpenConversationFilters = useCallback(() => {
|
|
168
|
+
navigation.navigate('ConversationFilters', {
|
|
169
|
+
chat_group_graph_id,
|
|
170
|
+
group_source_app_name,
|
|
171
|
+
show_direct_messages,
|
|
172
|
+
show_archived,
|
|
173
|
+
})
|
|
174
|
+
}, [navigation, chat_group_graph_id, group_source_app_name, show_direct_messages, show_archived])
|
|
175
|
+
|
|
176
|
+
const handleMarkAllRead = useCallback(() => {
|
|
177
|
+
currentPersonCache.update({ unreadCount: 0 })
|
|
178
|
+
Haptic.notificationSuccess()
|
|
179
|
+
markAllRead()
|
|
180
|
+
}, [currentPersonCache, markAllRead])
|
|
181
|
+
|
|
182
|
+
const alertMarkAllRead = useCallback(() => {
|
|
183
|
+
if (active === FilterTypes.All) return handleMarkAllRead()
|
|
184
|
+
|
|
185
|
+
Alert.alert('Mark all read', 'This includes conversations not shown by the current filter.', [
|
|
186
|
+
{ text: 'Cancel' },
|
|
187
|
+
{ text: 'OK', onPress: () => handleMarkAllRead() },
|
|
188
|
+
])
|
|
189
|
+
}, [active, handleMarkAllRead])
|
|
190
|
+
|
|
191
|
+
const showMoreFilters = canFilterByGroups || canFilterByTeams || canBrowseArchived
|
|
192
|
+
|
|
193
|
+
const styles = useStyles()
|
|
194
|
+
|
|
195
|
+
return (
|
|
196
|
+
<View style={styles.listHeader}>
|
|
197
|
+
<View style={styles.headingRow}>
|
|
198
|
+
<View style={styles.titleContainer}>
|
|
199
|
+
<FilterDropdown
|
|
200
|
+
options={options}
|
|
201
|
+
selectedId={active}
|
|
202
|
+
onSelect={handleSelectFilter}
|
|
203
|
+
onMoreFilters={handleOpenConversationFilters}
|
|
204
|
+
showMoreFilters={showMoreFilters}
|
|
205
|
+
triggerLabel={triggerLabel}
|
|
206
|
+
/>
|
|
207
|
+
</View>
|
|
208
|
+
<View style={styles.actionsContainer}>
|
|
209
|
+
<ListHeaderActionButton
|
|
210
|
+
name="general.cog"
|
|
211
|
+
onPress={handleChatSettings}
|
|
212
|
+
accessibilityLabel="Chat settings"
|
|
213
|
+
testID="conversations-chat-settings-button"
|
|
214
|
+
maxFontSizeMultiplier={MAX_FONT_SIZE_MULTIPLIER_LANDMARK}
|
|
215
|
+
/>
|
|
216
|
+
{canCreateConversations && (
|
|
217
|
+
<ListHeaderActionButton
|
|
218
|
+
name="churchCenter.signups"
|
|
219
|
+
onPress={handleNewConversationNavigation}
|
|
220
|
+
accessibilityLabel="New conversation"
|
|
221
|
+
testID="conversations-new-conversation-button"
|
|
222
|
+
maxFontSizeMultiplier={MAX_FONT_SIZE_MULTIPLIER_LANDMARK}
|
|
223
|
+
/>
|
|
224
|
+
)}
|
|
225
|
+
{searchEnabled && (
|
|
226
|
+
<ListHeaderActionButton
|
|
227
|
+
name="general.search"
|
|
228
|
+
onPress={handleSearch}
|
|
229
|
+
accessibilityLabel="Search"
|
|
230
|
+
testID="conversations-search-button"
|
|
231
|
+
maxFontSizeMultiplier={MAX_FONT_SIZE_MULTIPLIER_LANDMARK}
|
|
232
|
+
/>
|
|
233
|
+
)}
|
|
234
|
+
</View>
|
|
235
|
+
</View>
|
|
236
|
+
<View style={styles.markAllReadRow}>
|
|
237
|
+
<TextButton
|
|
238
|
+
onPress={alertMarkAllRead}
|
|
239
|
+
disabled={isPending}
|
|
240
|
+
maxFontSizeMultiplier={MAX_FONT_SIZE_MULTIPLIER_LANDMARK}
|
|
241
|
+
accessibilityShowsLargeContentViewer
|
|
242
|
+
accessibilityLargeContentTitle="Mark all read"
|
|
243
|
+
>
|
|
244
|
+
Mark all read
|
|
245
|
+
</TextButton>
|
|
246
|
+
</View>
|
|
247
|
+
</View>
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const useStyles = () => {
|
|
252
|
+
const theme = useTheme()
|
|
253
|
+
const atFontScaleBreakpoint = useAtFontScaleBreakpoint()
|
|
254
|
+
|
|
255
|
+
return StyleSheet.create({
|
|
256
|
+
listHeader: {
|
|
257
|
+
borderBottomWidth: 1,
|
|
258
|
+
borderBottomColor: theme.colors.fillColorNeutral050Base,
|
|
259
|
+
paddingBottom: 16,
|
|
260
|
+
gap: 8,
|
|
261
|
+
},
|
|
262
|
+
headingRow: {
|
|
263
|
+
gap: 12,
|
|
264
|
+
flexDirection: atFontScaleBreakpoint ? 'column' : 'row',
|
|
265
|
+
alignItems: atFontScaleBreakpoint ? 'flex-start' : 'center',
|
|
266
|
+
justifyContent: 'space-between',
|
|
267
|
+
paddingBottom: atFontScaleBreakpoint ? 4 : 0,
|
|
268
|
+
paddingHorizontal: ROW_PADDING_HORIZONTAL,
|
|
269
|
+
},
|
|
270
|
+
titleContainer: {
|
|
271
|
+
flexShrink: 1,
|
|
272
|
+
minWidth: 0,
|
|
273
|
+
},
|
|
274
|
+
actionsContainer: {
|
|
275
|
+
flexDirection: 'row',
|
|
276
|
+
gap: 8,
|
|
277
|
+
alignItems: 'center',
|
|
278
|
+
},
|
|
279
|
+
markAllReadRow: {
|
|
280
|
+
alignItems: 'flex-start',
|
|
281
|
+
paddingHorizontal: ROW_PADDING_HORIZONTAL,
|
|
282
|
+
},
|
|
283
|
+
})
|
|
284
|
+
}
|
|
@@ -44,6 +44,7 @@ export const ListHeaderComponent = () => {
|
|
|
44
44
|
chat_group_graph_id,
|
|
45
45
|
group_source_app_name = '',
|
|
46
46
|
show_direct_messages,
|
|
47
|
+
show_archived,
|
|
47
48
|
} = route.params || {}
|
|
48
49
|
const currentPersonCache = useCurrentPersonCache()
|
|
49
50
|
const { markAllRead, isPending } = useMarkAllRead()
|
|
@@ -54,7 +55,7 @@ export const ListHeaderComponent = () => {
|
|
|
54
55
|
)
|
|
55
56
|
|
|
56
57
|
const active: FilterTypes = useMemo(() => {
|
|
57
|
-
if (chat_group_graph_id) {
|
|
58
|
+
if (chat_group_graph_id || show_archived) {
|
|
58
59
|
return FilterTypes.More
|
|
59
60
|
} else if (/groups/i.test(group_source_app_name)) {
|
|
60
61
|
return FilterTypes.Groups
|
|
@@ -65,7 +66,7 @@ export const ListHeaderComponent = () => {
|
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
return FilterTypes.All
|
|
68
|
-
}, [chat_group_graph_id, group_source_app_name, show_direct_messages])
|
|
69
|
+
}, [chat_group_graph_id, group_source_app_name, show_direct_messages, show_archived])
|
|
69
70
|
|
|
70
71
|
const handleNewConversationNavigation = useCallback(() => {
|
|
71
72
|
if (newConversationFromFilter || entryMode === 'select_type') {
|
|
@@ -104,6 +105,7 @@ export const ListHeaderComponent = () => {
|
|
|
104
105
|
chat_group_graph_id: undefined,
|
|
105
106
|
group_source_app_name: undefined,
|
|
106
107
|
show_direct_messages: undefined,
|
|
108
|
+
show_archived: undefined,
|
|
107
109
|
...params,
|
|
108
110
|
})
|
|
109
111
|
},
|
|
@@ -119,8 +121,9 @@ export const ListHeaderComponent = () => {
|
|
|
119
121
|
chat_group_graph_id,
|
|
120
122
|
group_source_app_name,
|
|
121
123
|
show_direct_messages,
|
|
124
|
+
show_archived,
|
|
122
125
|
})
|
|
123
|
-
}, [navigation, chat_group_graph_id, group_source_app_name, show_direct_messages])
|
|
126
|
+
}, [navigation, chat_group_graph_id, group_source_app_name, show_direct_messages, show_archived])
|
|
124
127
|
|
|
125
128
|
const handleMarkAllRead = useCallback(() => {
|
|
126
129
|
currentPersonCache.update({ unreadCount: 0 })
|
|
@@ -161,15 +164,17 @@ export const ListHeaderComponent = () => {
|
|
|
161
164
|
<Heading numberOfLines={1} variant="h2">
|
|
162
165
|
Conversations
|
|
163
166
|
</Heading>
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
167
|
+
{!show_archived && (
|
|
168
|
+
<TextButton
|
|
169
|
+
onPress={alertMarkAllRead}
|
|
170
|
+
disabled={isPending}
|
|
171
|
+
maxFontSizeMultiplier={MAX_FONT_SIZE_MULTIPLIER_LANDMARK}
|
|
172
|
+
accessibilityShowsLargeContentViewer
|
|
173
|
+
accessibilityLargeContentTitle="Mark all read"
|
|
174
|
+
>
|
|
175
|
+
Mark all read
|
|
176
|
+
</TextButton>
|
|
177
|
+
)}
|
|
173
178
|
</View>
|
|
174
179
|
<View style={styles.actionsContainer}>
|
|
175
180
|
<ListHeaderActionButton
|