@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
package/src/hooks/use_jolt.ts
CHANGED
|
@@ -140,19 +140,44 @@ const pingSubscribedJoltClients = (queryClient: QueryClient) => {
|
|
|
140
140
|
})
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
const foregroundPingListeners = new Map<QueryClient, { mounts: number; remove: () => void }>()
|
|
144
|
+
|
|
145
|
+
const subscribeToForegroundPing = (queryClient: QueryClient) => {
|
|
146
|
+
const existing = foregroundPingListeners.get(queryClient)
|
|
147
|
+
if (existing) {
|
|
148
|
+
existing.mounts += 1
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
let previousState: AppStateStatus = AppState.currentState
|
|
153
|
+
const subscription = AppState.addEventListener('change', nextState => {
|
|
154
|
+
if (nextState === 'active' && previousState !== 'active') {
|
|
155
|
+
pingSubscribedJoltClients(queryClient)
|
|
156
|
+
}
|
|
157
|
+
previousState = nextState
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
foregroundPingListeners.set(queryClient, { mounts: 1, remove: () => subscription.remove() })
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const unsubscribeFromForegroundPing = (queryClient: QueryClient) => {
|
|
164
|
+
const existing = foregroundPingListeners.get(queryClient)
|
|
165
|
+
if (!existing) return
|
|
166
|
+
|
|
167
|
+
existing.mounts -= 1
|
|
168
|
+
if (existing.mounts > 0) return
|
|
169
|
+
|
|
170
|
+
existing.remove()
|
|
171
|
+
foregroundPingListeners.delete(queryClient)
|
|
172
|
+
}
|
|
173
|
+
|
|
143
174
|
export const useJoltForegroundPing = () => {
|
|
144
175
|
const queryClient = useQueryClient()
|
|
145
176
|
|
|
146
177
|
useEffect(() => {
|
|
147
|
-
|
|
148
|
-
const subscription = AppState.addEventListener('change', nextState => {
|
|
149
|
-
if (nextState === 'active' && previousState !== 'active') {
|
|
150
|
-
pingSubscribedJoltClients(queryClient)
|
|
151
|
-
}
|
|
152
|
-
previousState = nextState
|
|
153
|
-
})
|
|
178
|
+
subscribeToForegroundPing(queryClient)
|
|
154
179
|
|
|
155
|
-
return () =>
|
|
180
|
+
return () => unsubscribeFromForegroundPing(queryClient)
|
|
156
181
|
}, [queryClient])
|
|
157
182
|
}
|
|
158
183
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { availableFeatures, useFeatures } from './use_features'
|
|
2
|
+
|
|
3
|
+
export function useManualArchivingEnabled() {
|
|
4
|
+
const { featureEnabled } = useFeatures()
|
|
5
|
+
|
|
6
|
+
return (
|
|
7
|
+
featureEnabled(availableFeatures.manual_archiving) ||
|
|
8
|
+
featureEnabled(availableFeatures.manual_archiving_rollout)
|
|
9
|
+
)
|
|
10
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react'
|
|
2
2
|
import { AccessibilityInfo } from 'react-native'
|
|
3
|
+
import { useReducedMotion } from 'react-native-reanimated'
|
|
3
4
|
|
|
4
5
|
export const useReduceMotion = () => {
|
|
5
|
-
const [reduceMotion, setReduceMotion] = useState(
|
|
6
|
+
const [reduceMotion, setReduceMotion] = useState(useReducedMotion())
|
|
6
7
|
|
|
7
8
|
useEffect(() => {
|
|
8
9
|
AccessibilityInfo.isReduceMotionEnabled().then(setReduceMotion)
|
|
@@ -50,6 +50,7 @@ import {
|
|
|
50
50
|
useConversationUpdate,
|
|
51
51
|
} from '../hooks/use_conversation'
|
|
52
52
|
import { availableFeatures, useFeatures } from '../hooks/use_features'
|
|
53
|
+
import { useManualArchivingEnabled } from '../hooks/use_manual_archiving_enabled'
|
|
53
54
|
import { usePreviewAvatarDiameter } from '../hooks/use_preview_avatar_diameter'
|
|
54
55
|
import { useUnarchiveConversationAction } from '../hooks/use_unarchive_conversation_action'
|
|
55
56
|
import { type ConversationResource, MemberResource, isDefined } from '../types'
|
|
@@ -113,9 +114,7 @@ export function ConversationDetailsScreen({ route }: ConversationDetailsScreenPr
|
|
|
113
114
|
const { featureEnabled } = useFeatures()
|
|
114
115
|
const granularNotificationsEnabled = featureEnabled(availableFeatures.granular_notifications_ui)
|
|
115
116
|
const customAvatarsEnabled = featureEnabled(availableFeatures.custom_conversation_avatars)
|
|
116
|
-
const manualArchivingEnabled =
|
|
117
|
-
featureEnabled(availableFeatures.manual_archiving) ||
|
|
118
|
-
featureEnabled(availableFeatures.manual_archiving_rollout)
|
|
117
|
+
const manualArchivingEnabled = useManualArchivingEnabled()
|
|
119
118
|
const showGenderFilter =
|
|
120
119
|
featureEnabled(availableFeatures.gender_specific_conversations) && !!conversation.genderOption
|
|
121
120
|
const genderLabel = conversation.genderOption ? genderDisplayLabel(conversation.genderOption) : ''
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { DefaultTheme, ThemeProvider } from '@react-navigation/native'
|
|
2
|
+
import { fireEvent, render, within } from '@testing-library/react-native'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { ConversationFilters } from '../components/conversation_filters'
|
|
5
|
+
import { FilterContext } from '../context/conversation_filter_context'
|
|
6
|
+
import { ConversationFiltersParams } from '../screen_props'
|
|
7
|
+
|
|
8
|
+
jest.mock('react-native-gesture-handler', () => {
|
|
9
|
+
const { createElement } = jest.requireActual('react')
|
|
10
|
+
const { FlatList } = jest.requireActual('react-native')
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
FlatList: (props: object) => createElement(FlatList, { ...props, initialNumToRender: 50 }),
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
let mockCanBrowseArchived = false
|
|
18
|
+
jest.mock('../../../hooks/use_can_browse_archived_conversations', () => ({
|
|
19
|
+
useCanBrowseArchivedConversations: () => mockCanBrowseArchived,
|
|
20
|
+
}))
|
|
21
|
+
jest.mock('../../../hooks/use_direct_messages_eligible', () => ({
|
|
22
|
+
useDirectMessagesEligible: () => false,
|
|
23
|
+
}))
|
|
24
|
+
jest.mock('../../../hooks/use_filter_dropdown_enabled', () => ({
|
|
25
|
+
useFilterDropdownEnabled: () => false,
|
|
26
|
+
}))
|
|
27
|
+
jest.mock('../../../hooks/use_has_direct_messages', () => ({
|
|
28
|
+
useHasDirectMessages: () => false,
|
|
29
|
+
}))
|
|
30
|
+
jest.mock('../hooks/filters', () => ({
|
|
31
|
+
useGroupsToFilter: () => ({ groups: [] }),
|
|
32
|
+
useTeamsToFilter: () => ({ teams: [] }),
|
|
33
|
+
}))
|
|
34
|
+
|
|
35
|
+
const mockSetAppFilter = jest.fn()
|
|
36
|
+
|
|
37
|
+
const renderFilters = (params: ConversationFiltersParams = {}) =>
|
|
38
|
+
render(
|
|
39
|
+
<ThemeProvider value={DefaultTheme}>
|
|
40
|
+
<FilterContext.Provider
|
|
41
|
+
value={{
|
|
42
|
+
params,
|
|
43
|
+
scrollOffset: 0,
|
|
44
|
+
setScrollOffset: jest.fn(),
|
|
45
|
+
setFormSheetHeaderHeight: jest.fn(),
|
|
46
|
+
resetFilter: jest.fn(),
|
|
47
|
+
setGroupFilter: jest.fn(),
|
|
48
|
+
setAppFilter: mockSetAppFilter,
|
|
49
|
+
applyFilters: jest.fn(),
|
|
50
|
+
platformListPaddingBottom: 0,
|
|
51
|
+
}}
|
|
52
|
+
>
|
|
53
|
+
<ConversationFilters />
|
|
54
|
+
</FilterContext.Provider>
|
|
55
|
+
</ThemeProvider>
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
describe('ConversationFilters archived option', () => {
|
|
59
|
+
beforeEach(() => {
|
|
60
|
+
mockSetAppFilter.mockClear()
|
|
61
|
+
mockCanBrowseArchived = false
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('hides the Other section when the person cannot browse archived conversations', () => {
|
|
65
|
+
const view = renderFilters()
|
|
66
|
+
|
|
67
|
+
expect(view.queryByText('Other')).toBeNull()
|
|
68
|
+
expect(view.queryByText('Archived conversations')).toBeNull()
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('offers Archived under Other when the person can browse archived conversations', () => {
|
|
72
|
+
mockCanBrowseArchived = true
|
|
73
|
+
|
|
74
|
+
const view = renderFilters()
|
|
75
|
+
|
|
76
|
+
expect(view.getByText('Other')).toBeTruthy()
|
|
77
|
+
expect(view.getByText('Archived conversations')).toBeTruthy()
|
|
78
|
+
expect(view.getByText('Archived for everybody')).toBeTruthy()
|
|
79
|
+
expect(view.queryByRole('button', { selected: true })).toBeNull()
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('selecting Archived sets the archived filter param', () => {
|
|
83
|
+
mockCanBrowseArchived = true
|
|
84
|
+
|
|
85
|
+
const view = renderFilters()
|
|
86
|
+
|
|
87
|
+
fireEvent.press(view.getByText('Archived conversations'))
|
|
88
|
+
|
|
89
|
+
expect(mockSetAppFilter).toHaveBeenCalledTimes(1)
|
|
90
|
+
expect(mockSetAppFilter).toHaveBeenCalledWith({ show_archived: true })
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('preselects Archived when it is the active filter', () => {
|
|
94
|
+
mockCanBrowseArchived = true
|
|
95
|
+
|
|
96
|
+
const view = renderFilters({ show_archived: true })
|
|
97
|
+
|
|
98
|
+
const selected = view.getByRole('button', { selected: true })
|
|
99
|
+
expect(within(selected).getByText('Archived conversations')).toBeTruthy()
|
|
100
|
+
})
|
|
101
|
+
})
|
|
@@ -4,10 +4,13 @@ import { FlatList } from 'react-native-gesture-handler'
|
|
|
4
4
|
import { Heading } from '../../../components'
|
|
5
5
|
import {
|
|
6
6
|
useAtFontScaleBreakpoint,
|
|
7
|
+
useCanBrowseArchivedConversations,
|
|
7
8
|
useDirectMessagesEligible,
|
|
8
9
|
useHasDirectMessages,
|
|
9
10
|
useTheme,
|
|
10
11
|
} from '../../../hooks'
|
|
12
|
+
import { useFilterDropdownEnabled } from '../../../hooks/use_filter_dropdown_enabled'
|
|
13
|
+
import { faBoxArchive } from '../../../vendor/font_awesome_pro_solid_icons'
|
|
11
14
|
import { FilterContext, useFilterContext } from '../context/conversation_filter_context'
|
|
12
15
|
import { FilterTypes } from '../filter_types'
|
|
13
16
|
import { useGroupsToFilter, useTeamsToFilter } from '../hooks/filters'
|
|
@@ -16,6 +19,8 @@ import {
|
|
|
16
19
|
FilterRow,
|
|
17
20
|
GroupRow,
|
|
18
21
|
GroupRowProps,
|
|
22
|
+
OtherRow,
|
|
23
|
+
OtherRowProps,
|
|
19
24
|
TeamRow,
|
|
20
25
|
TeamRowProps,
|
|
21
26
|
ViewMore,
|
|
@@ -32,6 +37,7 @@ enum SectionTypes {
|
|
|
32
37
|
header,
|
|
33
38
|
hidden,
|
|
34
39
|
more,
|
|
40
|
+
other,
|
|
35
41
|
teams,
|
|
36
42
|
}
|
|
37
43
|
|
|
@@ -41,6 +47,7 @@ type SectionListData = Array<
|
|
|
41
47
|
| DataItem<HeaderProps, SectionTypes.header>
|
|
42
48
|
| DataItem<any, SectionTypes.hidden>
|
|
43
49
|
| DataItem<ViewMoreRowProps, SectionTypes.more>
|
|
50
|
+
| DataItem<OtherRowProps, SectionTypes.other>
|
|
44
51
|
| DataItem<TeamRowProps, SectionTypes.teams>
|
|
45
52
|
>
|
|
46
53
|
|
|
@@ -57,11 +64,18 @@ interface DataItem<T, TName extends SectionTypes> {
|
|
|
57
64
|
export const ConversationFilters = () => {
|
|
58
65
|
const styles = useStyles()
|
|
59
66
|
const { setScrollOffset, params } = useContext(FilterContext)
|
|
60
|
-
const {
|
|
67
|
+
const {
|
|
68
|
+
chat_group_graph_id,
|
|
69
|
+
group_source_app_name = '',
|
|
70
|
+
show_direct_messages,
|
|
71
|
+
show_archived,
|
|
72
|
+
} = params
|
|
61
73
|
|
|
74
|
+
const filterDropdownEnabled = useFilterDropdownEnabled()
|
|
62
75
|
const directMessagesEligible = useDirectMessagesEligible()
|
|
63
76
|
const hasDirectMessages = useHasDirectMessages()
|
|
64
77
|
const canFilterByDMs = directMessagesEligible && hasDirectMessages
|
|
78
|
+
const canFilterByArchived = useCanBrowseArchivedConversations()
|
|
65
79
|
|
|
66
80
|
const activeFilter: FilterTypes = useMemo(() => {
|
|
67
81
|
if (chat_group_graph_id) {
|
|
@@ -72,10 +86,12 @@ export const ConversationFilters = () => {
|
|
|
72
86
|
return FilterTypes.Teams
|
|
73
87
|
} else if (show_direct_messages) {
|
|
74
88
|
return FilterTypes.DirectMessages
|
|
89
|
+
} else if (show_archived) {
|
|
90
|
+
return FilterTypes.Archived
|
|
75
91
|
}
|
|
76
92
|
|
|
77
93
|
return FilterTypes.All
|
|
78
|
-
}, [chat_group_graph_id, group_source_app_name, show_direct_messages])
|
|
94
|
+
}, [chat_group_graph_id, group_source_app_name, show_direct_messages, show_archived])
|
|
79
95
|
|
|
80
96
|
const { groups = [] } = useGroupsToFilter()
|
|
81
97
|
const { teams = [] } = useTeamsToFilter()
|
|
@@ -101,8 +117,9 @@ export const ConversationFilters = () => {
|
|
|
101
117
|
const groupItemData = selectActiveWithFirst(5)(groupItems)
|
|
102
118
|
const teamItemData = selectActiveWithFirst(5)(teamItems)
|
|
103
119
|
|
|
104
|
-
const
|
|
105
|
-
const
|
|
120
|
+
const showAppFilters = !filterDropdownEnabled && groupItems.length > 0 && teamItems.length > 0
|
|
121
|
+
const showDMFilter = !filterDropdownEnabled && canFilterByDMs
|
|
122
|
+
const showGeneralFilters = showAppFilters || showDMFilter
|
|
106
123
|
|
|
107
124
|
const listData: SectionListData = [
|
|
108
125
|
{
|
|
@@ -114,7 +131,7 @@ export const ConversationFilters = () => {
|
|
|
114
131
|
data: { filter: FilterTypes.All, isActive: activeFilter === FilterTypes.All },
|
|
115
132
|
},
|
|
116
133
|
{
|
|
117
|
-
type:
|
|
134
|
+
type: showAppFilters ? SectionTypes.filter : SectionTypes.hidden,
|
|
118
135
|
data: {
|
|
119
136
|
filter: FilterTypes.Groups,
|
|
120
137
|
group_source_app_name: 'groups',
|
|
@@ -122,7 +139,7 @@ export const ConversationFilters = () => {
|
|
|
122
139
|
},
|
|
123
140
|
},
|
|
124
141
|
{
|
|
125
|
-
type:
|
|
142
|
+
type: showAppFilters ? SectionTypes.filter : SectionTypes.hidden,
|
|
126
143
|
data: {
|
|
127
144
|
filter: FilterTypes.Teams,
|
|
128
145
|
group_source_app_name: 'services',
|
|
@@ -130,7 +147,7 @@ export const ConversationFilters = () => {
|
|
|
130
147
|
},
|
|
131
148
|
},
|
|
132
149
|
{
|
|
133
|
-
type:
|
|
150
|
+
type: showDMFilter ? SectionTypes.filter : SectionTypes.hidden,
|
|
134
151
|
data: {
|
|
135
152
|
filter: FilterTypes.DirectMessages,
|
|
136
153
|
show_direct_messages: true,
|
|
@@ -155,6 +172,20 @@ export const ConversationFilters = () => {
|
|
|
155
172
|
type: teamItems.length > 5 ? SectionTypes.more : SectionTypes.hidden,
|
|
156
173
|
data: { routeName: 'TeamFilters' },
|
|
157
174
|
},
|
|
175
|
+
{
|
|
176
|
+
type: canFilterByArchived ? SectionTypes.header : SectionTypes.hidden,
|
|
177
|
+
data: { title: 'Other', style: styles.extraHeaderScalableSpace },
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
type: canFilterByArchived ? SectionTypes.other : SectionTypes.hidden,
|
|
181
|
+
data: {
|
|
182
|
+
filter: FilterTypes.Archived,
|
|
183
|
+
description: 'Archived for everybody',
|
|
184
|
+
icon: faBoxArchive,
|
|
185
|
+
show_archived: true,
|
|
186
|
+
isActive: activeFilter === FilterTypes.Archived,
|
|
187
|
+
},
|
|
188
|
+
},
|
|
158
189
|
]
|
|
159
190
|
|
|
160
191
|
return (
|
|
@@ -175,6 +206,8 @@ export const ConversationFilters = () => {
|
|
|
175
206
|
return <TeamRow {...item.data} />
|
|
176
207
|
case SectionTypes.more:
|
|
177
208
|
return <ViewMore {...item.data} />
|
|
209
|
+
case SectionTypes.other:
|
|
210
|
+
return <OtherRow {...item.data} />
|
|
178
211
|
default:
|
|
179
212
|
return null
|
|
180
213
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import type { IconDefinition } from '@fortawesome/fontawesome-svg-core'
|
|
1
2
|
import { PlatformPressable } from '@react-navigation/elements'
|
|
2
3
|
import { NavigationProp, useNavigation } from '@react-navigation/native'
|
|
3
4
|
import React, { PropsWithChildren, useContext } from 'react'
|
|
4
5
|
import { StyleSheet, View, ViewStyle } from 'react-native'
|
|
5
|
-
import { Heading, Icon, Image, Text, TextButton } from '../../../components'
|
|
6
|
+
import { Heading, Icon, IconTile, Image, Text, TextButton } from '../../../components'
|
|
6
7
|
import {
|
|
7
8
|
useAtFontScaleBreakpoint,
|
|
8
9
|
useFontScale,
|
|
@@ -115,6 +116,33 @@ export const TeamRow = ({ team }: TeamRowProps) => {
|
|
|
115
116
|
)
|
|
116
117
|
}
|
|
117
118
|
|
|
119
|
+
export type OtherRowProps = {
|
|
120
|
+
show_archived?: boolean
|
|
121
|
+
isActive: boolean
|
|
122
|
+
filter: FilterTypes
|
|
123
|
+
description: string
|
|
124
|
+
icon: IconDefinition
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export const OtherRow = ({ show_archived, isActive, filter, description, icon }: OtherRowProps) => {
|
|
128
|
+
const styles = useRowStyles()
|
|
129
|
+
const { setAppFilter } = useContext(FilterContext)
|
|
130
|
+
|
|
131
|
+
return (
|
|
132
|
+
<PressableRow onPress={() => setAppFilter({ show_archived })} isActive={isActive}>
|
|
133
|
+
<IconTile icon={icon} size="lg" />
|
|
134
|
+
<View style={styles.rowTextContainer}>
|
|
135
|
+
<Heading variant="h3" style={styles.rowTitle}>
|
|
136
|
+
{filter}
|
|
137
|
+
</Heading>
|
|
138
|
+
<Text variant="footnote" style={styles.rowSubtitle}>
|
|
139
|
+
{description}
|
|
140
|
+
</Text>
|
|
141
|
+
</View>
|
|
142
|
+
</PressableRow>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
118
146
|
export type ViewMoreRowProps = { routeName: keyof ConversationFilterStackParamList }
|
|
119
147
|
|
|
120
148
|
export const ViewMore = ({ routeName }: ViewMoreRowProps) => {
|
|
@@ -22,6 +22,7 @@ export const FilterContext = createContext<{
|
|
|
22
22
|
chat_group_graph_id: undefined,
|
|
23
23
|
group_source_app_name: undefined,
|
|
24
24
|
show_direct_messages: undefined,
|
|
25
|
+
show_archived: undefined,
|
|
25
26
|
},
|
|
26
27
|
scrollOffset: 0,
|
|
27
28
|
setScrollOffset: () => {},
|
|
@@ -57,6 +58,7 @@ export const FilterProvider = ({ children }: PropsWithChildren) => {
|
|
|
57
58
|
chat_group_graph_id: undefined,
|
|
58
59
|
group_source_app_name: undefined,
|
|
59
60
|
show_direct_messages: undefined,
|
|
61
|
+
show_archived: undefined,
|
|
60
62
|
})
|
|
61
63
|
)
|
|
62
64
|
},
|
|
@@ -65,6 +67,7 @@ export const FilterProvider = ({ children }: PropsWithChildren) => {
|
|
|
65
67
|
chat_group_graph_id: params.chat_group_graph_id,
|
|
66
68
|
group_source_app_name: undefined,
|
|
67
69
|
show_direct_messages: undefined,
|
|
70
|
+
show_archived: undefined,
|
|
68
71
|
})
|
|
69
72
|
},
|
|
70
73
|
setAppFilter: (params: Omit<ConversationFiltersParams, 'chat_group_graph_id'>) => {
|
|
@@ -72,6 +75,7 @@ export const FilterProvider = ({ children }: PropsWithChildren) => {
|
|
|
72
75
|
chat_group_graph_id: undefined,
|
|
73
76
|
group_source_app_name: params.group_source_app_name,
|
|
74
77
|
show_direct_messages: params.show_direct_messages,
|
|
78
|
+
show_archived: params.show_archived,
|
|
75
79
|
})
|
|
76
80
|
},
|
|
77
81
|
applyFilters: (params: ConversationFiltersParams) => {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { useHeaderHeight } from '@react-navigation/elements'
|
|
1
|
+
import { HeaderBackButton, useHeaderHeight } from '@react-navigation/elements'
|
|
2
2
|
import * as ReactNavigationNative from '@react-navigation/native'
|
|
3
|
-
import { RouteProp, useRoute } from '@react-navigation/native'
|
|
3
|
+
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native'
|
|
4
4
|
import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
|
5
5
|
import React, { useCallback } from 'react'
|
|
6
6
|
import { LayoutChangeEvent, Platform, StyleSheet, useWindowDimensions, View } from 'react-native'
|
|
7
7
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
8
8
|
import FormSheet, { getFormSheetScreenOptions } from '../components/primitive/form_sheet'
|
|
9
|
+
import { useTheme } from '../hooks'
|
|
10
|
+
import { useFilterDropdownEnabled } from '../hooks/use_filter_dropdown_enabled'
|
|
9
11
|
import { ConversationFilters } from './conversation_filters/components/conversation_filters'
|
|
10
12
|
import {
|
|
11
13
|
FilterProvider,
|
|
@@ -86,16 +88,19 @@ export const ConversationFiltersScreen = (_props: ConversationFiltersScreenProps
|
|
|
86
88
|
return (
|
|
87
89
|
<FilterProvider>
|
|
88
90
|
<FormSheet.Root contentStyle={styles.contentContainer} showAndroidSheetGrabber={false}>
|
|
89
|
-
<
|
|
91
|
+
<ConversationFiltersHeader />
|
|
90
92
|
<Filters />
|
|
91
93
|
</FormSheet.Root>
|
|
92
94
|
</FilterProvider>
|
|
93
95
|
)
|
|
94
96
|
}
|
|
95
97
|
|
|
96
|
-
const
|
|
98
|
+
export const ConversationFiltersHeader = () => {
|
|
97
99
|
const { resetFilter, applyFilters, setFormSheetHeaderHeight } = useFilterContext()
|
|
100
|
+
const navigation = useNavigation()
|
|
98
101
|
const route = useRoute<RouteProp<ConversationFiltersScreenProps['route']>>()
|
|
102
|
+
const filterDropdownEnabled = useFilterDropdownEnabled()
|
|
103
|
+
const { colors } = useTheme()
|
|
99
104
|
const styles = useStyles()
|
|
100
105
|
|
|
101
106
|
const handleFormSheetHeaderLayout = useCallback(
|
|
@@ -108,7 +113,18 @@ const FormSheetHeaderWithContext = () => {
|
|
|
108
113
|
return (
|
|
109
114
|
<View onLayout={handleFormSheetHeaderLayout}>
|
|
110
115
|
<FormSheet.Header style={styles.header}>
|
|
111
|
-
|
|
116
|
+
{Platform.OS === 'android' && (
|
|
117
|
+
<HeaderBackButton
|
|
118
|
+
displayMode="minimal"
|
|
119
|
+
accessibilityLabel="Back"
|
|
120
|
+
tintColor={colors.iconColorDefaultPrimary}
|
|
121
|
+
onPress={navigation.goBack}
|
|
122
|
+
style={styles.backButton}
|
|
123
|
+
/>
|
|
124
|
+
)}
|
|
125
|
+
<FormSheet.HeaderTitle>
|
|
126
|
+
{filterDropdownEnabled ? 'More filters' : 'Filters'}
|
|
127
|
+
</FormSheet.HeaderTitle>
|
|
112
128
|
<FormSheet.HeaderActions>
|
|
113
129
|
<FormSheet.HeaderTextButton onPress={resetFilter}>Reset</FormSheet.HeaderTextButton>
|
|
114
130
|
<FormSheet.HeaderButton title="Apply" onPress={() => applyFilters(route.params)} />
|
|
@@ -141,5 +157,9 @@ const useStyles = () => {
|
|
|
141
157
|
// separator the way the non-scrolling reaction sheet does.
|
|
142
158
|
borderBottomWidth: 0,
|
|
143
159
|
},
|
|
160
|
+
backButton: {
|
|
161
|
+
marginLeft: -3,
|
|
162
|
+
marginRight: 8,
|
|
163
|
+
},
|
|
144
164
|
})
|
|
145
165
|
}
|
|
@@ -2,6 +2,14 @@ import { QueryClientProvider } from '@tanstack/react-query'
|
|
|
2
2
|
import { render } from '@testing-library/react-native'
|
|
3
3
|
import React, { Suspense } from 'react'
|
|
4
4
|
import { buildTestQueryClient } from '../../../__utils__/query_client'
|
|
5
|
+
import {
|
|
6
|
+
AudioAdapter,
|
|
7
|
+
ChatAdapters,
|
|
8
|
+
ClipboardAdapter,
|
|
9
|
+
ImagePickerAdapter,
|
|
10
|
+
MenuAdapter,
|
|
11
|
+
VideoAdapter,
|
|
12
|
+
} from '../../../utils/native_adapters'
|
|
5
13
|
import { ConversationsScreen } from '../conversations_screen'
|
|
6
14
|
|
|
7
15
|
const mockGet = jest.fn()
|
|
@@ -28,6 +36,26 @@ jest.mock('../components/search_list_header_component', () => {
|
|
|
28
36
|
const { Text } = require('react-native')
|
|
29
37
|
return { SearchListHeaderComponent: () => <Text>Search header</Text> }
|
|
30
38
|
})
|
|
39
|
+
jest.mock('../components/filter_dropdown_list_header_component', () => {
|
|
40
|
+
const { Text } = require('react-native')
|
|
41
|
+
return { FilterDropdownListHeaderComponent: () => <Text>Filter dropdown header</Text> }
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const configureAdapters = ({ menu }: { menu: boolean }) => {
|
|
45
|
+
ChatAdapters.configure({
|
|
46
|
+
clipboard: new ClipboardAdapter({
|
|
47
|
+
getStringAsync: async () => '',
|
|
48
|
+
setStringAsync: async () => {},
|
|
49
|
+
}),
|
|
50
|
+
audio: new AudioAdapter({ useAudio: () => ({}) as any }),
|
|
51
|
+
video: new VideoAdapter({ Player: (() => null) as any }),
|
|
52
|
+
imagePicker: new ImagePickerAdapter({
|
|
53
|
+
openCameraAsync: async () => ({ canceled: true, assets: null }),
|
|
54
|
+
openImageLibraryAsync: async () => ({ canceled: true, assets: null }),
|
|
55
|
+
}),
|
|
56
|
+
...(menu ? { menu: new MenuAdapter({ View: () => null }) } : {}),
|
|
57
|
+
})
|
|
58
|
+
}
|
|
31
59
|
|
|
32
60
|
const renderWithFeatures = (enabledFeatures: string[]) => {
|
|
33
61
|
mockGet.mockResolvedValue({
|
|
@@ -51,6 +79,10 @@ const renderWithFeatures = (enabledFeatures: string[]) => {
|
|
|
51
79
|
}
|
|
52
80
|
|
|
53
81
|
describe('ConversationsScreen header gating', () => {
|
|
82
|
+
beforeEach(() => {
|
|
83
|
+
configureAdapters({ menu: true })
|
|
84
|
+
})
|
|
85
|
+
|
|
54
86
|
it('shows the search header with only the QA flag on', async () => {
|
|
55
87
|
const view = renderWithFeatures(['QA_MOBILE_search'])
|
|
56
88
|
|
|
@@ -63,6 +95,26 @@ describe('ConversationsScreen header gating', () => {
|
|
|
63
95
|
expect(await view.findByText('Search header')).toBeTruthy()
|
|
64
96
|
})
|
|
65
97
|
|
|
98
|
+
it('shows the filter dropdown header over the search header when its flag is on', async () => {
|
|
99
|
+
const view = renderWithFeatures(['QA_MOBILE_filter_dropdown', 'ROLLOUT_MOBILE_search'])
|
|
100
|
+
|
|
101
|
+
expect(await view.findByText('Filter dropdown header')).toBeTruthy()
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('shows the filter dropdown header with only its rollout flag on', async () => {
|
|
105
|
+
const view = renderWithFeatures(['ROLLOUT_MOBILE_filter_dropdown'])
|
|
106
|
+
|
|
107
|
+
expect(await view.findByText('Filter dropdown header')).toBeTruthy()
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('falls back to the search header when the host has no menu adapter', async () => {
|
|
111
|
+
configureAdapters({ menu: false })
|
|
112
|
+
|
|
113
|
+
const view = renderWithFeatures(['QA_MOBILE_filter_dropdown', 'ROLLOUT_MOBILE_search'])
|
|
114
|
+
|
|
115
|
+
expect(await view.findByText('Search header')).toBeTruthy()
|
|
116
|
+
})
|
|
117
|
+
|
|
66
118
|
it('shows the default header with neither flag on', async () => {
|
|
67
119
|
const view = renderWithFeatures(['ROLLOUT_WEB_search'])
|
|
68
120
|
|