@planningcenter/chat-react-native 4.4.1-qa-staging.17 → 4.4.1-qa-staging.19
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 +44 -35
- package/build/components/conversations/conversations.js.map +1 -1
- 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.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_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/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 +80 -13
- 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 +75 -49
- 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 +580 -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 +3 -0
- package/src/__tests__/screens/conversation_filters_screen.test.tsx +76 -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 +1 -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 +15 -3
- 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/index.ts +2 -0
- package/src/hooks/use_can_browse_archived_conversations.ts +17 -0
- package/src/hooks/use_clear_conversation_filters.ts +1 -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/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 +32 -2
- 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 +20 -4
- package/src/screens/conversations/__tests__/conversations_screen.test.tsx +52 -0
- package/src/screens/conversations/components/__tests__/filter_dropdown_list_header_component.test.tsx +259 -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 +286 -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,286 @@
|
|
|
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
|
+
{!show_archived && (
|
|
237
|
+
<View style={styles.markAllReadRow}>
|
|
238
|
+
<TextButton
|
|
239
|
+
onPress={alertMarkAllRead}
|
|
240
|
+
disabled={isPending}
|
|
241
|
+
maxFontSizeMultiplier={MAX_FONT_SIZE_MULTIPLIER_LANDMARK}
|
|
242
|
+
accessibilityShowsLargeContentViewer
|
|
243
|
+
accessibilityLargeContentTitle="Mark all read"
|
|
244
|
+
>
|
|
245
|
+
Mark all read
|
|
246
|
+
</TextButton>
|
|
247
|
+
</View>
|
|
248
|
+
)}
|
|
249
|
+
</View>
|
|
250
|
+
)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const useStyles = () => {
|
|
254
|
+
const theme = useTheme()
|
|
255
|
+
const atFontScaleBreakpoint = useAtFontScaleBreakpoint()
|
|
256
|
+
|
|
257
|
+
return StyleSheet.create({
|
|
258
|
+
listHeader: {
|
|
259
|
+
borderBottomWidth: 1,
|
|
260
|
+
borderBottomColor: theme.colors.fillColorNeutral050Base,
|
|
261
|
+
paddingBottom: 16,
|
|
262
|
+
gap: 8,
|
|
263
|
+
},
|
|
264
|
+
headingRow: {
|
|
265
|
+
gap: 12,
|
|
266
|
+
flexDirection: atFontScaleBreakpoint ? 'column' : 'row',
|
|
267
|
+
alignItems: atFontScaleBreakpoint ? 'flex-start' : 'center',
|
|
268
|
+
justifyContent: 'space-between',
|
|
269
|
+
paddingBottom: atFontScaleBreakpoint ? 4 : 0,
|
|
270
|
+
paddingHorizontal: ROW_PADDING_HORIZONTAL,
|
|
271
|
+
},
|
|
272
|
+
titleContainer: {
|
|
273
|
+
flexShrink: 1,
|
|
274
|
+
minWidth: 0,
|
|
275
|
+
},
|
|
276
|
+
actionsContainer: {
|
|
277
|
+
flexDirection: 'row',
|
|
278
|
+
gap: 8,
|
|
279
|
+
alignItems: 'center',
|
|
280
|
+
},
|
|
281
|
+
markAllReadRow: {
|
|
282
|
+
alignItems: 'flex-start',
|
|
283
|
+
paddingHorizontal: ROW_PADDING_HORIZONTAL,
|
|
284
|
+
},
|
|
285
|
+
})
|
|
286
|
+
}
|
|
@@ -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
|
|
@@ -44,6 +44,7 @@ export const SearchListHeaderComponent = () => {
|
|
|
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 SearchListHeaderComponent = () => {
|
|
|
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 SearchListHeaderComponent = () => {
|
|
|
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 SearchListHeaderComponent = () => {
|
|
|
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
|
},
|
|
@@ -123,8 +125,9 @@ export const SearchListHeaderComponent = () => {
|
|
|
123
125
|
chat_group_graph_id,
|
|
124
126
|
group_source_app_name,
|
|
125
127
|
show_direct_messages,
|
|
128
|
+
show_archived,
|
|
126
129
|
})
|
|
127
|
-
}, [navigation, chat_group_graph_id, group_source_app_name, show_direct_messages])
|
|
130
|
+
}, [navigation, chat_group_graph_id, group_source_app_name, show_direct_messages, show_archived])
|
|
128
131
|
|
|
129
132
|
const handleMarkAllRead = useCallback(() => {
|
|
130
133
|
currentPersonCache.update({ unreadCount: 0 })
|
|
@@ -165,15 +168,17 @@ export const SearchListHeaderComponent = () => {
|
|
|
165
168
|
<Heading numberOfLines={1} variant="h2">
|
|
166
169
|
Conversations
|
|
167
170
|
</Heading>
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
171
|
+
{!show_archived && (
|
|
172
|
+
<TextButton
|
|
173
|
+
onPress={alertMarkAllRead}
|
|
174
|
+
disabled={isPending}
|
|
175
|
+
maxFontSizeMultiplier={MAX_FONT_SIZE_MULTIPLIER_LANDMARK}
|
|
176
|
+
accessibilityShowsLargeContentViewer
|
|
177
|
+
accessibilityLargeContentTitle="Mark all read"
|
|
178
|
+
>
|
|
179
|
+
Mark all read
|
|
180
|
+
</TextButton>
|
|
181
|
+
)}
|
|
177
182
|
</View>
|
|
178
183
|
<View style={styles.actionsContainer}>
|
|
179
184
|
<ListHeaderActionButton
|
|
@@ -3,10 +3,13 @@ import React from 'react'
|
|
|
3
3
|
import { StyleSheet, View } from 'react-native'
|
|
4
4
|
import { Conversations } from '../../components'
|
|
5
5
|
import { ConversationsContextProvider } from '../../contexts/conversations_context'
|
|
6
|
+
import { useFilterDropdownEnabled } from '../../hooks/use_filter_dropdown_enabled'
|
|
6
7
|
import { analyticsEvents, usePublishProductAnalyticsEvent } from '../../hooks/use_product_analytics'
|
|
7
8
|
import { useSearchEnabled } from '../../hooks/use_search_enabled'
|
|
8
9
|
import { AppName } from '../../types/resources/app_name'
|
|
9
10
|
import { GraphId } from '../../types/resources/group_resource'
|
|
11
|
+
import { Menu } from '../../utils/native_adapters'
|
|
12
|
+
import { FilterDropdownListHeaderComponent } from './components/filter_dropdown_list_header_component'
|
|
10
13
|
import { ListHeaderComponent } from './components/list_header_component'
|
|
11
14
|
import { SearchListHeaderComponent } from './components/search_list_header_component'
|
|
12
15
|
|
|
@@ -15,12 +18,12 @@ export type ConversationsScreenProps = StaticScreenProps<{
|
|
|
15
18
|
chat_group_graph_id?: GraphId
|
|
16
19
|
group_source_app_name?: AppName
|
|
17
20
|
show_direct_messages?: boolean
|
|
21
|
+
show_archived?: boolean
|
|
18
22
|
}>
|
|
19
23
|
|
|
20
24
|
export function ConversationsScreen({ route }: ConversationsScreenProps) {
|
|
21
25
|
const styles = useStyles()
|
|
22
|
-
const
|
|
23
|
-
const Header = searchEnabled ? SearchListHeaderComponent : ListHeaderComponent
|
|
26
|
+
const Header = useListHeaderComponent()
|
|
24
27
|
|
|
25
28
|
usePublishProductAnalyticsEvent(analyticsEvents.conversation_index_opened)
|
|
26
29
|
|
|
@@ -33,6 +36,16 @@ export function ConversationsScreen({ route }: ConversationsScreenProps) {
|
|
|
33
36
|
)
|
|
34
37
|
}
|
|
35
38
|
|
|
39
|
+
const useListHeaderComponent = () => {
|
|
40
|
+
const filterDropdownEnabled = useFilterDropdownEnabled()
|
|
41
|
+
const searchEnabled = useSearchEnabled()
|
|
42
|
+
|
|
43
|
+
if (filterDropdownEnabled && Menu.isConfigured) return FilterDropdownListHeaderComponent
|
|
44
|
+
if (searchEnabled) return SearchListHeaderComponent
|
|
45
|
+
|
|
46
|
+
return ListHeaderComponent
|
|
47
|
+
}
|
|
48
|
+
|
|
36
49
|
const useStyles = () => {
|
|
37
50
|
return StyleSheet.create({
|
|
38
51
|
container: {
|
|
@@ -12,6 +12,7 @@ describe('getConversationsRequestArgs', () => {
|
|
|
12
12
|
],
|
|
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
|
+
['show_archived', { show_archived: true }, 'mine_or_not_empty,archived'],
|
|
15
16
|
])('%s uses filter: %s', (_label, args, expectedFilter) => {
|
|
16
17
|
expect(getConversationsRequestArgs(args).data.filter).toBe(expectedFilter)
|
|
17
18
|
})
|
|
@@ -16,4 +16,8 @@ describe('formatEventDateTime', () => {
|
|
|
16
16
|
it('includes the year for dates in other years', () => {
|
|
17
17
|
expect(formatEventDateTime(new Date(2025, 1, 3, 14, 18))).toBe('Feb 3, 2025 at 2:18pm')
|
|
18
18
|
})
|
|
19
|
+
|
|
20
|
+
it('drops the minutes on the hour', () => {
|
|
21
|
+
expect(formatEventDateTime(new Date(2026, 1, 3, 14, 0))).toBe('Feb 3 at 2pm')
|
|
22
|
+
})
|
|
19
23
|
})
|
package/src/utils/date.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { date as formatDate
|
|
1
|
+
import { date as formatDate } from '@planningcenter/datetime-fmt'
|
|
2
2
|
import dayjs from './dayjs'
|
|
3
3
|
|
|
4
4
|
export type DateProps = string | number | Date
|
|
@@ -64,7 +64,8 @@ export function relativeTime(date: DateProps): string {
|
|
|
64
64
|
|
|
65
65
|
export function formatEventDateTime(date: DateProps) {
|
|
66
66
|
const { isThisYear } = getRelativeDateStatus(date)
|
|
67
|
-
const
|
|
68
|
-
const day =
|
|
69
|
-
|
|
67
|
+
const local = dayjs(date)
|
|
68
|
+
const day = local.format(isThisYear ? 'MMM D' : 'MMM D, YYYY')
|
|
69
|
+
const time = local.format(local.minute() === 0 ? 'ha' : 'h:mma')
|
|
70
|
+
return `${day} at ${time}`
|
|
70
71
|
}
|
|
@@ -8,7 +8,9 @@ const actions = [{ id: 'all', title: 'All' }]
|
|
|
8
8
|
describe('MenuAdapter', () => {
|
|
9
9
|
it('renders the trigger and warns when the host configured no menu', () => {
|
|
10
10
|
const warn = jest.spyOn(console, 'warn').mockImplementation(() => {})
|
|
11
|
-
const { View } = new MenuAdapter()
|
|
11
|
+
const { View, isConfigured } = new MenuAdapter()
|
|
12
|
+
|
|
13
|
+
expect(isConfigured).toBe(false)
|
|
12
14
|
|
|
13
15
|
render(
|
|
14
16
|
<View actions={actions}>
|
|
@@ -28,7 +30,9 @@ describe('MenuAdapter', () => {
|
|
|
28
30
|
<Text>{hostActions.map(action => action.title).join(',')}</Text>
|
|
29
31
|
</>
|
|
30
32
|
)
|
|
31
|
-
const { View } = new MenuAdapter({ View: HostMenuView })
|
|
33
|
+
const { View, isConfigured } = new MenuAdapter({ View: HostMenuView })
|
|
34
|
+
|
|
35
|
+
expect(isConfigured).toBe(true)
|
|
32
36
|
|
|
33
37
|
render(
|
|
34
38
|
<View actions={actions}>
|
|
@@ -61,8 +61,10 @@ function UnconfiguredMenuView({ children, style, testID }: MenuViewProps) {
|
|
|
61
61
|
|
|
62
62
|
export class MenuAdapter {
|
|
63
63
|
View: MenuView
|
|
64
|
+
isConfigured: boolean
|
|
64
65
|
|
|
65
66
|
constructor(methods?: { View: MenuView }) {
|
|
66
67
|
this.View = methods?.View ?? UnconfiguredMenuView
|
|
68
|
+
this.isConfigured = !!methods?.View
|
|
67
69
|
}
|
|
68
70
|
}
|
|
@@ -6,6 +6,7 @@ export interface ConversationRequestArgs {
|
|
|
6
6
|
chat_group_graph_id?: GraphId
|
|
7
7
|
group_source_app_name?: string
|
|
8
8
|
show_direct_messages?: boolean
|
|
9
|
+
show_archived?: boolean
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
export const getConversationsRequestArgs = ({
|
|
@@ -13,10 +14,13 @@ export const getConversationsRequestArgs = ({
|
|
|
13
14
|
chat_group_graph_id,
|
|
14
15
|
group_source_app_name,
|
|
15
16
|
show_direct_messages,
|
|
17
|
+
show_archived,
|
|
16
18
|
}: Partial<ConversationRequestArgs> = {}): GetRequest => {
|
|
17
19
|
let filter: string
|
|
18
20
|
if (chat_group_graph_id) {
|
|
19
21
|
filter = 'mine_or_not_empty,group'
|
|
22
|
+
} else if (show_archived) {
|
|
23
|
+
filter = 'mine_or_not_empty,archived'
|
|
20
24
|
} else if (show_direct_messages) {
|
|
21
25
|
filter = 'mine_or_not_empty,direct_message'
|
|
22
26
|
} else if (group_source_app_name) {
|
|
@@ -608,3 +608,15 @@ export const faTag: IconDefinition = {
|
|
|
608
608
|
'M32.5 96l0 149.5c0 17 6.7 33.3 18.7 45.3l192 192c25 25 65.5 25 90.5 0L483.2 333.3c25-25 25-65.5 0-90.5l-192-192C279.2 38.7 263 32 246 32L96.5 32c-35.3 0-64 28.7-64 64zm112 16a32 32 0 1 1 0 64 32 32 0 1 1 0-64z',
|
|
609
609
|
],
|
|
610
610
|
}
|
|
611
|
+
|
|
612
|
+
export const faBoxArchive: IconDefinition = {
|
|
613
|
+
prefix: 'fas',
|
|
614
|
+
iconName: 'box-archive',
|
|
615
|
+
icon: [
|
|
616
|
+
512,
|
|
617
|
+
512,
|
|
618
|
+
['archive'],
|
|
619
|
+
'f187',
|
|
620
|
+
'M0 64C0 46.3 14.3 32 32 32l448 0c17.7 0 32 14.3 32 32l0 32c0 17.7-14.3 32-32 32L32 128C14.3 128 0 113.7 0 96L0 64zM32 176l448 0 0 240c0 35.3-28.7 64-64 64L96 480c-35.3 0-64-28.7-64-64l0-240zm152 64c-13.3 0-24 10.7-24 24s10.7 24 24 24l144 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-144 0z',
|
|
621
|
+
],
|
|
622
|
+
}
|