diff --git a/native/chat/fullscreen-thread-media-gallery.react.js b/native/chat/fullscreen-thread-media-gallery.react.js --- a/native/chat/fullscreen-thread-media-gallery.react.js +++ b/native/chat/fullscreen-thread-media-gallery.react.js @@ -1,17 +1,57 @@ // @flow import * as React from 'react'; -import { Text } from 'react-native'; +import { Text, View, TouchableOpacity } from 'react-native'; import type { ThreadInfo } from 'lib/types/thread-types.js'; import type { ChatNavigationProp } from './chat.react.js'; +import ThreadSettingsMediaGallery from './settings/thread-settings-media-gallery.react.js'; import type { NavigationRoute } from '../navigation/route-names.js'; +import { useStyles } from '../themes/colors.js'; +import type { VerticalBounds } from '../types/layout-types.js'; export type FullScreenThreadMediaGalleryParams = { +threadInfo: ThreadInfo, + +verticalBounds: ?VerticalBounds, }; +const TABS = { + ALL: 'ALL', + IMAGES: 'IMAGES', + VIDEOS: 'VIDEOS', +}; + +type FilterBarProps = { + +setActiveTab: (tab: string) => void, + +activeTab: string, +}; + +function FilterBar(props: FilterBarProps): React.Node { + const styles = useStyles(unboundStyles); + const { setActiveTab, activeTab } = props; + + const filterBar = React.useMemo(() => { + return ( + + + {Object.values(TABS).map(tab => ( + setActiveTab(String(tab))} + > + {String(tab)} + + ))} + + + ); + }, [activeTab, setActiveTab, styles]); + + return filterBar; +} + type FullScreenThreadMediaGalleryProps = { +navigation: ChatNavigationProp<'FullScreenThreadMediaGallery'>, +route: NavigationRoute<'FullScreenThreadMediaGallery'>, @@ -20,8 +60,65 @@ function FullScreenThreadMediaGallery( props: FullScreenThreadMediaGalleryProps, ): React.Node { - const { id } = props.route.params.threadInfo; - return {id}; + const { threadInfo, verticalBounds } = props.route.params; + const { id } = threadInfo; + const styles = useStyles(unboundStyles); + + const [activeTab, setActiveTab] = React.useState(TABS.ALL); + + return ( + + + + + ); } +const unboundStyles = { + container: { + marginBottom: 120, + }, + filterBar: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + marginTop: 20, + marginBottom: 40, + }, + tabNavigator: { + display: 'flex', + flexDirection: 'row', + alignItems: 'flex-start', + position: 'absolute', + width: '90%', + padding: 0, + }, + tabActiveItem: { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + backgroundColor: 'floatingButtonBackground', + flex: 1, + height: 30, + borderRadius: 8, + }, + tabItem: { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + backgroundColor: 'tabBarInactiveBackground', + flex: 1, + height: 30, + }, + tabText: { + color: 'floatingButtonLabel', + }, +}; + export default FullScreenThreadMediaGallery; diff --git a/native/chat/settings/thread-settings-media-gallery.react.js b/native/chat/settings/thread-settings-media-gallery.react.js --- a/native/chat/settings/thread-settings-media-gallery.react.js +++ b/native/chat/settings/thread-settings-media-gallery.react.js @@ -28,6 +28,8 @@ +threadID: string, +limit: number, +verticalBounds: ?VerticalBounds, + +offset?: number, + +activeTab?: string, }; function ThreadSettingsMediaGallery( @@ -47,8 +49,7 @@ // E.g. 16px, media, galleryItemGap, media, galleryItemGap, media, 16px const galleryItemWidth = (width - 32 - (numColumns - 1) * galleryItemGap) / numColumns; - - const { threadID, limit, verticalBounds } = props; + const { threadID, limit, verticalBounds, offset, activeTab } = props; const [mediaInfos, setMediaInfos] = React.useState([]); const callFetchThreadMedia = useServerCall(fetchThreadMedia); @@ -175,12 +176,43 @@ ], ); + const filteredMediaInfos = React.useMemo(() => { + if (!activeTab || activeTab === 'ALL') { + return mediaInfos; + } else if (activeTab === 'IMAGES') { + return mediaInfos.filter(mediaInfo => mediaInfo.type === 'photo'); + } else if (activeTab === 'VIDEOS') { + return mediaInfos.filter(mediaInfo => mediaInfo.type === 'video'); + } + }, [activeTab, mediaInfos]); + + const onEndReached = React.useCallback(() => { + // Offset will be undefined if the media gallery is rendered in the + // thread settings (since no offset prop is passed in). If rendered in + // the full screen media gallery, offset will be defined and onEndReached + // will actually do something. + if (offset === undefined) { + return; + } + + // As the FlatList fetches more media, we set the offset to be the length + // of mediaInfos. This will ensure that the next set of media is retrieved + // from the starting point. + callFetchThreadMedia({ threadID, limit, offset: mediaInfos.length }).then( + result => { + setMediaInfos([...mediaInfos, ...result.media]); + }, + ); + }, [callFetchThreadMedia, mediaInfos, threadID, limit, offset]); + return ( ); diff --git a/native/chat/settings/thread-settings.react.js b/native/chat/settings/thread-settings.react.js --- a/native/chat/settings/thread-settings.react.js +++ b/native/chat/settings/thread-settings.react.js @@ -1046,6 +1046,7 @@ onPressSeeMoreMediaGallery = () => { this.props.navigation.navigate(FullScreenThreadMediaGalleryRouteName, { threadInfo: this.props.threadInfo, + verticalBounds: this.state.verticalBounds, }); }; } diff --git a/native/themes/colors.js b/native/themes/colors.js --- a/native/themes/colors.js +++ b/native/themes/colors.js @@ -74,6 +74,7 @@ tabBarAccent: '#7E57C2', tabBarBackground: '#F5F5F5', tabBarActiveTintColor: '#7E57C2', + tabBarInactiveBackground: '#1F1F1F', vibrantGreenButton: '#00C853', vibrantRedButton: '#F53100', tooltipBackground: '#E0E0E0', @@ -158,6 +159,7 @@ tabBarAccent: '#AE94DB', tabBarBackground: '#0A0A0A', tabBarActiveTintColor: '#AE94DB', + tabBarInactiveBackground: '#1F1F1F', vibrantGreenButton: '#00C853', vibrantRedButton: '#F53100', tooltipBackground: '#1F1F1F',