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,75 @@
// @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,
};
+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 tabStyles = React.useCallback(
+ (currentTab: string) => {
+ return currentTab === activeTab ? styles.tabActiveItem : styles.tabItem;
+ },
+ [activeTab, styles],
+ );
+
+ const filterBar = React.useMemo(() => {
+ return (
+
+
+ setActiveTab(Tabs.All)}
+ >
+ {Tabs.All}
+
+ setActiveTab(Tabs.Images)}
+ >
+ {Tabs.Images}
+
+ setActiveTab(Tabs.Videos)}
+ >
+ {Tabs.Videos}
+
+
+
+ );
+ }, [setActiveTab, tabStyles, styles]);
+
+ return filterBar;
+}
+
type FullScreenThreadMediaGalleryProps = {
+navigation: ChatNavigationProp<'FullScreenThreadMediaGallery'>,
+route: NavigationRoute<'FullScreenThreadMediaGallery'>,
@@ -20,8 +78,97 @@
function FullScreenThreadMediaGallery(
props: FullScreenThreadMediaGalleryProps,
): React.Node {
- const { id } = props.route.params.threadInfo;
- return {id};
+ const { threadInfo } = props.route.params;
+ const { id } = threadInfo;
+ const styles = useStyles(unboundStyles);
+
+ const [activeTab, setActiveTab] = React.useState(Tabs.All);
+ const flatListContainerRef = React.useRef>();
+ const [verticalBounds, setVerticalBounds] = React.useState(
+ null,
+ );
+
+ const onFlatListContainerLayout = React.useCallback(() => {
+ if (!flatListContainerRef.current) {
+ return;
+ }
+
+ flatListContainerRef.current.measure(
+ (x, y, width, height, pageX, pageY) => {
+ if (
+ height === null ||
+ height === undefined ||
+ pageY === null ||
+ pageY === undefined
+ ) {
+ return;
+ }
+ setVerticalBounds({ height, y: pageY });
+ },
+ );
+ }, [flatListContainerRef]);
+
+ return (
+
+
+
+
+ );
}
-export default FullScreenThreadMediaGallery;
+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',
+ },
+};
+
+const MemoizedFullScreenMediaGallery: React.ComponentType = React.memo(
+ FullScreenThreadMediaGallery,
+);
+
+export default MemoizedFullScreenMediaGallery;
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,44 @@
],
);
+ 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(async () => {
+ // 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.
+ const result = await callFetchThreadMedia({
+ threadID,
+ limit,
+ offset: mediaInfos.length,
+ });
+ setMediaInfos([...mediaInfos, ...result.media]);
+ }, [callFetchThreadMedia, mediaInfos, threadID, limit, offset]);
+
return (
);
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',