@planningcenter/chat-react-native 3.1.0 → 3.2.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/build/navigation/index.d.ts +1 -8
  2. package/build/navigation/index.d.ts.map +1 -1
  3. package/build/screens/conversation_filters/components/conversation_filters.d.ts +3 -0
  4. package/build/screens/conversation_filters/components/conversation_filters.d.ts.map +1 -0
  5. package/build/screens/conversation_filters/components/conversation_filters.js +173 -0
  6. package/build/screens/conversation_filters/components/conversation_filters.js.map +1 -0
  7. package/build/screens/conversation_filters/components/rows.d.ts +31 -0
  8. package/build/screens/conversation_filters/components/rows.d.ts.map +1 -0
  9. package/build/screens/conversation_filters/components/rows.js +111 -0
  10. package/build/screens/conversation_filters/components/rows.js.map +1 -0
  11. package/build/screens/conversation_filters/context/conversation_filter_context.d.ts +23 -0
  12. package/build/screens/conversation_filters/context/conversation_filter_context.d.ts.map +1 -0
  13. package/build/screens/conversation_filters/context/conversation_filter_context.js +51 -0
  14. package/build/screens/conversation_filters/context/conversation_filter_context.js.map +1 -0
  15. package/build/screens/conversation_filters/filter_types.d.ts +7 -0
  16. package/build/screens/conversation_filters/filter_types.d.ts.map +1 -0
  17. package/build/screens/conversation_filters/filter_types.js +8 -0
  18. package/build/screens/conversation_filters/filter_types.js.map +1 -0
  19. package/build/screens/conversation_filters/group_filters.d.ts +6 -0
  20. package/build/screens/conversation_filters/group_filters.d.ts.map +1 -0
  21. package/build/screens/conversation_filters/group_filters.js +15 -0
  22. package/build/screens/conversation_filters/group_filters.js.map +1 -0
  23. package/build/screens/conversation_filters/hooks/filters.d.ts +415 -0
  24. package/build/screens/conversation_filters/hooks/filters.d.ts.map +1 -0
  25. package/build/screens/conversation_filters/hooks/filters.js +41 -0
  26. package/build/screens/conversation_filters/hooks/filters.js.map +1 -0
  27. package/build/screens/conversation_filters/screen_props.d.ts +13 -0
  28. package/build/screens/conversation_filters/screen_props.d.ts.map +1 -0
  29. package/build/screens/conversation_filters/screen_props.js +2 -0
  30. package/build/screens/conversation_filters/screen_props.js.map +1 -0
  31. package/build/screens/conversation_filters/team_filters.d.ts +6 -0
  32. package/build/screens/conversation_filters/team_filters.d.ts.map +1 -0
  33. package/build/screens/conversation_filters/team_filters.js +15 -0
  34. package/build/screens/conversation_filters/team_filters.js.map +1 -0
  35. package/build/screens/conversation_filters_screen.d.ts +2 -9
  36. package/build/screens/conversation_filters_screen.d.ts.map +1 -1
  37. package/build/screens/conversation_filters_screen.js +81 -302
  38. package/build/screens/conversation_filters_screen.js.map +1 -1
  39. package/package.json +2 -2
  40. package/src/screens/conversation_filters/components/conversation_filters.tsx +233 -0
  41. package/src/screens/conversation_filters/components/rows.tsx +164 -0
  42. package/src/screens/conversation_filters/context/conversation_filter_context.tsx +65 -0
  43. package/src/screens/conversation_filters/filter_types.ts +6 -0
  44. package/src/screens/conversation_filters/group_filters.tsx +31 -0
  45. package/src/screens/conversation_filters/hooks/filters.ts +68 -0
  46. package/src/screens/conversation_filters/screen_props.ts +15 -0
  47. package/src/screens/conversation_filters/team_filters.tsx +31 -0
  48. package/src/screens/conversation_filters_screen.tsx +93 -429
@@ -0,0 +1,233 @@
1
+ import React, { useContext, useMemo } from 'react'
2
+ import { FlatList, RefreshControl, StyleSheet, View, ViewStyle } from 'react-native'
3
+ import { useSafeAreaInsets } from 'react-native-safe-area-context'
4
+ import { Heading } from '../../../components'
5
+ import { useTheme } from '../../../hooks'
6
+ import { FilterContext } from '../context/conversation_filter_context'
7
+ import { FilterTypes } from '../filter_types'
8
+ import { useGroupsToFilter, useTeamsToFilter } from '../hooks/filters'
9
+ import {
10
+ FilterProps,
11
+ FilterRow,
12
+ GroupRow,
13
+ GroupRowProps,
14
+ TeamRow,
15
+ TeamRowProps,
16
+ ViewMore,
17
+ ViewMoreRowProps,
18
+ } from './rows'
19
+
20
+ // =========================================
21
+ // ====== Factory Constants & Types ========
22
+ // =========================================
23
+
24
+ enum SectionTypes {
25
+ filter,
26
+ groups,
27
+ header,
28
+ hidden,
29
+ more,
30
+ teams,
31
+ }
32
+
33
+ type SectionListData = Array<
34
+ | DataItem<FilterProps, SectionTypes.filter>
35
+ | DataItem<GroupRowProps, SectionTypes.groups>
36
+ | DataItem<HeaderProps, SectionTypes.header>
37
+ | DataItem<any, SectionTypes.hidden>
38
+ | DataItem<ViewMoreRowProps, SectionTypes.more>
39
+ | DataItem<TeamRowProps, SectionTypes.teams>
40
+ >
41
+
42
+ interface DataItem<T, TName extends SectionTypes> {
43
+ type: TName
44
+ data: T
45
+ sectionStyle?: ViewStyle
46
+ }
47
+
48
+ // =================================
49
+ // ====== Components ===============
50
+ // =================================
51
+
52
+ export const ConversationFilters = () => {
53
+ const styles = useStyles()
54
+ const { setScrollOffset, params } = useContext(FilterContext)
55
+ const { chat_group_graph_id, group_source_app_name = '' } = params
56
+
57
+ const activeFilter: FilterTypes = useMemo(() => {
58
+ if (chat_group_graph_id) {
59
+ return FilterTypes.More
60
+ } else if (/groups/i.test(group_source_app_name)) {
61
+ return FilterTypes.Groups
62
+ } else if (/services/i.test(group_source_app_name)) {
63
+ return FilterTypes.Teams
64
+ }
65
+
66
+ return FilterTypes.All
67
+ }, [chat_group_graph_id, group_source_app_name])
68
+
69
+ const { groups = [] } = useGroupsToFilter()
70
+ const { teams = [] } = useTeamsToFilter()
71
+
72
+ const activeGroupId = chat_group_graph_id
73
+ const isExactGroupFilter = activeFilter === FilterTypes.More
74
+
75
+ const teamItems: DataItem<TeamRowProps, SectionTypes.teams>[] = teams.map(team => ({
76
+ type: SectionTypes.teams,
77
+ data: {
78
+ team,
79
+ isActive: isExactGroupFilter && team.id.toString() === activeGroupId,
80
+ },
81
+ }))
82
+
83
+ const groupItems: DataItem<GroupRowProps, SectionTypes.groups>[] = groups.map(group => ({
84
+ type: SectionTypes.groups,
85
+ data: {
86
+ group,
87
+ isActive: isExactGroupFilter && group?.id.toString() === activeGroupId,
88
+ },
89
+ }))
90
+ const groupItemData = selectActiveWithFirst(5)(groupItems)
91
+ const teamItemData = selectActiveWithFirst(5)(teamItems)
92
+
93
+ const hideAppFilters = groupItems.length < 1 || teamItems.length < 1
94
+
95
+ const listData: SectionListData = [
96
+ {
97
+ type: hideAppFilters ? SectionTypes.hidden : SectionTypes.header,
98
+ data: { title: 'General Filters' },
99
+ },
100
+ {
101
+ type: hideAppFilters ? SectionTypes.hidden : SectionTypes.filter,
102
+ data: { filter: FilterTypes.All, isActive: activeFilter === FilterTypes.All },
103
+ },
104
+ {
105
+ type: hideAppFilters ? SectionTypes.hidden : SectionTypes.filter,
106
+ data: {
107
+ filter: FilterTypes.Groups,
108
+ group_source_app_name: 'groups',
109
+ isActive: activeFilter === FilterTypes.Groups,
110
+ },
111
+ },
112
+ {
113
+ type: hideAppFilters ? SectionTypes.hidden : SectionTypes.filter,
114
+ data: {
115
+ filter: FilterTypes.Teams,
116
+ group_source_app_name: 'services',
117
+ isActive: activeFilter === FilterTypes.Teams,
118
+ },
119
+ },
120
+ {
121
+ type: groupItems.length ? SectionTypes.header : SectionTypes.hidden,
122
+ data: { title: 'Groups' },
123
+ },
124
+ ...groupItemData,
125
+ {
126
+ type: groupItems.length > 5 ? SectionTypes.more : SectionTypes.hidden,
127
+ data: { routeName: 'GroupFilters' },
128
+ },
129
+ {
130
+ type: teamItems.length ? SectionTypes.header : SectionTypes.hidden,
131
+ data: { title: 'Teams' },
132
+ },
133
+ ...teamItemData,
134
+ {
135
+ type: teamItems.length > 5 ? SectionTypes.more : SectionTypes.hidden,
136
+ data: { routeName: 'TeamFilters' },
137
+ },
138
+ ]
139
+
140
+ return (
141
+ <FlatList
142
+ data={listData}
143
+ contentContainerStyle={styles.flatlistContainer}
144
+ nestedScrollEnabled={true}
145
+ onScroll={e => setScrollOffset(e.nativeEvent.contentOffset.y)}
146
+ renderItem={({ item }) => {
147
+ switch (item.type) {
148
+ case SectionTypes.header:
149
+ return <Header {...item.data} />
150
+ case SectionTypes.filter:
151
+ return <FilterRow {...item.data} />
152
+ case SectionTypes.groups:
153
+ return <GroupRow {...item.data} />
154
+ case SectionTypes.teams:
155
+ return <TeamRow {...item.data} />
156
+ case SectionTypes.more:
157
+ return <ViewMore {...item.data} />
158
+ default:
159
+ return null
160
+ }
161
+ }}
162
+ />
163
+ )
164
+ }
165
+
166
+ const useStyles = () => {
167
+ const { bottom } = useSafeAreaInsets()
168
+ const theme = useTheme()
169
+
170
+ return StyleSheet.create({
171
+ flatlistContainer: {
172
+ paddingBottom: 64 + bottom,
173
+ },
174
+ section: {},
175
+ sectionHeader: {
176
+ flexDirection: 'row',
177
+ justifyContent: 'space-between',
178
+ paddingTop: 24,
179
+ paddingBottom: 8,
180
+ paddingHorizontal: 16,
181
+ },
182
+ selectTeamsButton: {},
183
+ filterBar: {
184
+ backgroundColor: theme.colors.fillColorNeutral100Inverted,
185
+ flexDirection: 'row',
186
+ justifyContent: 'space-between',
187
+ alignItems: 'center',
188
+ paddingHorizontal: 16,
189
+ paddingVertical: 16,
190
+ gap: 8,
191
+ },
192
+ filterBarScroll: {
193
+ elevation: 4,
194
+ boxShadow: '0px 4px 4px rgba(0, 0, 0, 0.1)',
195
+ },
196
+ filterBarActions: {
197
+ flexDirection: 'row',
198
+ gap: 8,
199
+ alignItems: 'center',
200
+ },
201
+ })
202
+ }
203
+
204
+ type HeaderProps = {
205
+ title: string
206
+ }
207
+
208
+ const Header = ({ title }: HeaderProps) => {
209
+ const styles = useStyles()
210
+
211
+ return (
212
+ <View style={styles.sectionHeader}>
213
+ <Heading variant="h3">{title}</Heading>
214
+ </View>
215
+ )
216
+ }
217
+
218
+ type GroupSectionType =
219
+ | DataItem<TeamRowProps, SectionTypes.teams>
220
+ | DataItem<GroupRowProps, SectionTypes.groups>
221
+
222
+ const selectActiveWithFirst =
223
+ (number: number) =>
224
+ <T extends GroupSectionType>(groups: T[]): T[] => {
225
+ const activeGroupIndex = groups.findIndex(group => group.data.isActive)
226
+ const activeGroup = groups[activeGroupIndex]
227
+
228
+ if (!activeGroup || activeGroupIndex <= number) {
229
+ return groups.slice(0, number)
230
+ }
231
+
232
+ return [activeGroup, ...groups].slice(0, number)
233
+ }
@@ -0,0 +1,164 @@
1
+ import { PlatformPressable } from '@react-navigation/elements'
2
+ import { NavigationProp, useNavigation } from '@react-navigation/native'
3
+ import React, { PropsWithChildren, useContext } from 'react'
4
+ import { StyleSheet, View, ViewStyle } from 'react-native'
5
+ import { Heading, Icon, Image, Text, TextButton } from '../../../components'
6
+ import { useTheme } from '../../../hooks'
7
+ import { useServicesTeamsMap } from '../../../hooks/use_services_team'
8
+ import { GroupResource } from '../../../types/resources/group_resource'
9
+ import { FilterContext } from '../context/conversation_filter_context'
10
+ import { FilterTypes } from '../filter_types'
11
+ import { FilteredGroup } from '../hooks/filters'
12
+ import { ConversationFilterStackParamList } from '../screen_props'
13
+
14
+ export type FilterProps = { group_source_app_name?: string; isActive: boolean; filter: FilterTypes }
15
+
16
+ export const FilterRow = ({ group_source_app_name, isActive, filter }: FilterProps) => {
17
+ const styles = useRowStyles()
18
+ const { setAppFilter } = useContext(FilterContext)
19
+
20
+ return (
21
+ <PressableRow style={styles.row} onPress={() => setAppFilter({ group_source_app_name })}>
22
+ <Text>{filter}</Text>
23
+ {isActive ? <Icon name="general.check" size={16} style={styles.rowIconRight} /> : null}
24
+ </PressableRow>
25
+ )
26
+ }
27
+
28
+ export type GroupRowProps = {
29
+ group: FilteredGroup
30
+ isActive: boolean
31
+ }
32
+
33
+ export const GroupRow = ({ group, isActive }: GroupRowProps) => {
34
+ const styles = useRowStyles()
35
+ const { setGroupFilter } = useContext(FilterContext)
36
+
37
+ const handleFilterByGroup = () => {
38
+ setGroupFilter({ chat_group_graph_id: group.id })
39
+ }
40
+
41
+ const { headerImage, membershipsCount } = group
42
+
43
+ return (
44
+ <PressableRow style={styles.row} onPress={handleFilterByGroup}>
45
+ {headerImage?.thumbnail && (
46
+ <Image
47
+ source={{ uri: headerImage?.thumbnail }}
48
+ resizeMode="cover"
49
+ style={styles.rowImage}
50
+ alt={`Image for ${group.name}`}
51
+ />
52
+ )}
53
+ <View>
54
+ <Heading variant="h3" style={styles.rowTitle}>
55
+ {group.name}
56
+ </Heading>
57
+ {membershipsCount && <Text>{group.membershipsCount} members</Text>}
58
+ </View>
59
+ {isActive ? <Icon name="general.check" size={16} style={styles.rowIconRight} /> : null}
60
+ </PressableRow>
61
+ )
62
+ }
63
+
64
+ export type TeamRowProps = {
65
+ team: GroupResource
66
+ isActive: boolean
67
+ }
68
+
69
+ export const TeamRow = ({ team }: TeamRowProps) => {
70
+ const styles = useRowStyles()
71
+ const { setGroupFilter } = useContext(FilterContext)
72
+ const servicesTeams = useServicesTeamsMap()
73
+ const { params } = useContext(FilterContext)
74
+ const { chat_group_graph_id } = params
75
+ const isActive = chat_group_graph_id === team.id.toString()
76
+
77
+ const handleFilterByGroup = () => {
78
+ setGroupFilter({ chat_group_graph_id: team.id })
79
+ }
80
+
81
+ const servicesTeam = servicesTeams[team.id]
82
+ const serviceTypeName = servicesTeam?.serviceTypeName || servicesTeam?.group
83
+
84
+ return (
85
+ <PressableRow style={styles.row} onPress={handleFilterByGroup}>
86
+ <View>
87
+ <Heading variant="h3" style={styles.rowTitle}>
88
+ {team.name}
89
+ </Heading>
90
+ <Text>{serviceTypeName}</Text>
91
+ </View>
92
+ {isActive ? <Icon name="general.check" size={16} style={styles.rowIconRight} /> : null}
93
+ </PressableRow>
94
+ )
95
+ }
96
+
97
+ export type ViewMoreRowProps = { routeName: keyof ConversationFilterStackParamList }
98
+
99
+ export const ViewMore = ({ routeName }: ViewMoreRowProps) => {
100
+ const styles = useRowStyles()
101
+ const navigation = useNavigation<NavigationProp<ConversationFilterStackParamList, 'Filters'>>()
102
+
103
+ return (
104
+ <View style={[styles.borderLessRow]}>
105
+ <TextButton onPress={() => navigation.navigate(routeName, {})}>View more</TextButton>
106
+ </View>
107
+ )
108
+ }
109
+
110
+ export const PressableRow = ({
111
+ children,
112
+ onPress,
113
+ style,
114
+ }: PropsWithChildren<{ onPress: () => void; style?: ViewStyle }>) => {
115
+ const styles = useRowStyles()
116
+ return (
117
+ <PlatformPressable style={styles.container} onPress={onPress}>
118
+ <View style={[styles.innerContainer, style]}>{children}</View>
119
+ </PlatformPressable>
120
+ )
121
+ }
122
+
123
+ const ASPECT_RATIO = 16 / 9
124
+ const THUMBNAIL_WIDTH = 80
125
+ const THUMBNAIL_HEIGHT = THUMBNAIL_WIDTH / ASPECT_RATIO
126
+
127
+ const useRowStyles = () => {
128
+ const theme = useTheme()
129
+ return StyleSheet.create({
130
+ container: {
131
+ paddingLeft: 16,
132
+ },
133
+ innerContainer: {
134
+ flexDirection: 'row',
135
+ alignItems: 'center',
136
+ gap: 12,
137
+ borderBottomWidth: 1,
138
+ borderBottomColor: theme.colors.fillColorNeutral050Base,
139
+ paddingVertical: 12,
140
+ paddingRight: 16,
141
+ },
142
+ borderLessRow: {
143
+ flexDirection: 'row',
144
+ alignItems: 'center',
145
+ gap: 12,
146
+ paddingVertical: 12,
147
+ paddingRight: 16,
148
+ paddingLeft: 16,
149
+ },
150
+ row: {},
151
+ rowImage: {
152
+ width: THUMBNAIL_WIDTH,
153
+ height: THUMBNAIL_HEIGHT,
154
+ borderRadius: 4,
155
+ },
156
+ rowIconRight: {
157
+ marginLeft: 'auto',
158
+ color: theme.colors.statusSuccessIcon,
159
+ },
160
+ rowTitle: {
161
+ fontSize: 16,
162
+ },
163
+ })
164
+ }
@@ -0,0 +1,65 @@
1
+ import React from 'react'
2
+ import { createContext, PropsWithChildren, useState } from 'react'
3
+ import { ConversationFiltersParams, ConversationFiltersScreenProps } from '../screen_props'
4
+ import { RouteProp, StackActions, useNavigation, useRoute } from '@react-navigation/native'
5
+
6
+ export const FilterContext = createContext<{
7
+ params: ConversationFiltersParams
8
+ scrollOffset: number
9
+ setScrollOffset: (offset: number) => void
10
+ resetFilter: () => void
11
+ setGroupFilter: (params: ConversationFiltersParams) => void
12
+ setAppFilter: (params: ConversationFiltersParams) => void
13
+ applyFilters: (params: ConversationFiltersParams) => void
14
+ }>({
15
+ params: {
16
+ chat_group_graph_id: undefined,
17
+ group_source_app_name: undefined,
18
+ },
19
+ scrollOffset: 0,
20
+ setScrollOffset: () => {},
21
+ resetFilter: () => {},
22
+ setGroupFilter: () => {},
23
+ setAppFilter: () => {},
24
+ applyFilters: () => {},
25
+ })
26
+
27
+ export const FilterProvider = ({ children }: PropsWithChildren) => {
28
+ const [scrollOffset, setScrollOffset] = useState(0)
29
+ const navigation = useNavigation()
30
+ const route = useRoute<RouteProp<ConversationFiltersScreenProps['route']>>()
31
+ const filterContextValue = {
32
+ params: route.params,
33
+ scrollOffset,
34
+ setScrollOffset,
35
+ resetFilter: () => {
36
+ navigation.dispatch(
37
+ StackActions.popTo('Conversations', {
38
+ chat_group_graph_id: undefined,
39
+ group_source_app_name: undefined,
40
+ })
41
+ )
42
+ },
43
+ setGroupFilter: (params: Omit<ConversationFiltersParams, 'group_source_app_name'>) => {
44
+ navigation.setParams({
45
+ chat_group_graph_id: params.chat_group_graph_id,
46
+ group_source_app_name: undefined,
47
+ })
48
+ },
49
+ setAppFilter: (params: Omit<ConversationFiltersParams, 'chat_group_graph_id'>) => {
50
+ navigation.setParams({
51
+ chat_group_graph_id: undefined,
52
+ group_source_app_name: params.group_source_app_name,
53
+ })
54
+ },
55
+ applyFilters: (params: ConversationFiltersParams) => {
56
+ navigation.dispatch(StackActions.popTo('Conversations', params))
57
+ },
58
+ }
59
+
60
+ return <FilterContext.Provider value={filterContextValue}>{children}</FilterContext.Provider>
61
+ }
62
+
63
+ export const useFilterContext = () => {
64
+ return React.useContext(FilterContext)
65
+ }
@@ -0,0 +1,6 @@
1
+ export enum FilterTypes {
2
+ All = 'All conversations',
3
+ Groups = 'All my groups',
4
+ Teams = 'All my teams and plans',
5
+ More = 'More',
6
+ }
@@ -0,0 +1,31 @@
1
+ import React from 'react'
2
+ import { FlatList, RefreshControl, View } from 'react-native'
3
+ import { Text } from '../../components/display'
4
+ import { ConversationFiltersParams } from './screen_props'
5
+ import { StaticScreenProps } from '@react-navigation/native'
6
+ import { useGroupsToFilter } from './hooks/filters'
7
+ import { GroupRow } from './components/rows'
8
+ import { useFilterContext } from './context/conversation_filter_context'
9
+
10
+ export type GroupFiltersScreenProps = StaticScreenProps<ConversationFiltersParams>
11
+
12
+ export const GroupFilters = () => {
13
+ const { groups, fetchNextPage, isRefetching, refetch } = useGroupsToFilter()
14
+ const { params } = useFilterContext()
15
+ const { chat_group_graph_id } = params
16
+
17
+ return (
18
+ <View>
19
+ <FlatList
20
+ data={groups}
21
+ renderItem={({ item }) => (
22
+ <GroupRow group={item} isActive={item?.id.toString() === chat_group_graph_id} />
23
+ )}
24
+ ListEmptyComponent={<Text>No groups available</Text>}
25
+ refreshing={isRefetching}
26
+ refreshControl={<RefreshControl refreshing={isRefetching} onRefresh={refetch} />}
27
+ onEndReached={() => fetchNextPage()}
28
+ />
29
+ </View>
30
+ )
31
+ }
@@ -0,0 +1,68 @@
1
+ import { useMemo } from 'react'
2
+ import { useGroups } from '../../../hooks/use_groups'
3
+ import { useGroupsGroups } from '../../../hooks/use_groups_groups'
4
+ import { useTeams } from '../../../hooks/use_teams'
5
+ import { GroupResource } from '../../../types/resources/group_resource'
6
+ import { GroupsGroupResource } from '../../../types'
7
+
8
+ export const useTeamsToFilter = () => {
9
+ const { data: teams = [], ...rest } = useTeams()
10
+
11
+ return { teams, ...rest }
12
+ }
13
+
14
+ export interface FilteredGroup
15
+ extends GroupResource,
16
+ Partial<Pick<GroupsGroupResource, 'headerImage' | 'membershipsCount'>> {}
17
+
18
+ export const useGroupsToFilter = () => {
19
+ const {
20
+ data: groups = [],
21
+ fetchNextPage: fetchNextGroups,
22
+ refetch: refetchGroups,
23
+ ...rest
24
+ } = useGroups({
25
+ source_app_name: 'Groups',
26
+ source_type: 'Group',
27
+ })
28
+
29
+ const {
30
+ data: groupsData = [],
31
+ fetchNextPage: fetchNextGroupsGroups,
32
+ refetch: refetchGroupsGroups,
33
+ } = useGroupsGroups()
34
+
35
+ const handleRefetch = () => {
36
+ refetchGroups()
37
+ refetchGroupsGroups()
38
+ }
39
+
40
+ const handleFetchNextPage = () => {
41
+ fetchNextGroups()
42
+ fetchNextGroupsGroups()
43
+ }
44
+
45
+ const filteredGroups: FilteredGroup[] = useMemo(
46
+ () =>
47
+ groups
48
+ .map(group => {
49
+ const groupsGroup = groupsData.find(g => group.id.includes(g.id))
50
+ const { membershipsCount, headerImage } = groupsGroup || {}
51
+
52
+ return {
53
+ ...group,
54
+ membershipsCount,
55
+ headerImage,
56
+ }
57
+ })
58
+ .filter(g => typeof g !== 'undefined'),
59
+ [groups, groupsData]
60
+ )
61
+
62
+ return {
63
+ groups: filteredGroups,
64
+ fetchNextPage: handleFetchNextPage,
65
+ refetch: handleRefetch,
66
+ ...rest,
67
+ }
68
+ }
@@ -0,0 +1,15 @@
1
+ import { StaticScreenProps } from '@react-navigation/native'
2
+ import { GraphId } from '../../types/resources/group_resource'
3
+
4
+ export type ConversationFiltersParams = {
5
+ chat_group_graph_id?: GraphId
6
+ group_source_app_name?: string
7
+ }
8
+
9
+ export type ConversationFiltersScreenProps = StaticScreenProps<ConversationFiltersParams>
10
+
11
+ export type ConversationFilterStackParamList = {
12
+ Filters: ConversationFiltersParams
13
+ GroupFilters: ConversationFiltersParams
14
+ TeamFilters: ConversationFiltersParams
15
+ }
@@ -0,0 +1,31 @@
1
+ import { StaticScreenProps } from '@react-navigation/native'
2
+ import React from 'react'
3
+ import { FlatList, RefreshControl, View } from 'react-native'
4
+ import { Text } from '../../components/display'
5
+ import { TeamRow } from './components/rows'
6
+ import { useFilterContext } from './context/conversation_filter_context'
7
+ import { useTeamsToFilter } from './hooks/filters'
8
+ import { ConversationFiltersParams } from './screen_props'
9
+
10
+ export type GroupFiltersScreenProps = StaticScreenProps<ConversationFiltersParams>
11
+
12
+ export const TeamFilters = () => {
13
+ const { teams, fetchNextPage, isRefetching, refetch } = useTeamsToFilter()
14
+ const { params } = useFilterContext()
15
+ const { chat_group_graph_id } = params
16
+
17
+ return (
18
+ <View>
19
+ <FlatList
20
+ data={teams}
21
+ renderItem={({ item }) => (
22
+ <TeamRow team={item} isActive={item?.id.toString() === chat_group_graph_id} />
23
+ )}
24
+ refreshing={isRefetching}
25
+ refreshControl={<RefreshControl refreshing={isRefetching} onRefresh={refetch} />}
26
+ ListEmptyComponent={<Text>No teams available</Text>}
27
+ onEndReached={() => fetchNextPage()}
28
+ />
29
+ </View>
30
+ )
31
+ }