diff options
author | Shravya Ramesh <37447613+shravyaramesh@users.noreply.github.com> | 2020-10-19 12:42:15 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-19 15:42:15 -0400 |
commit | 1b7fef188ec2aee0706fc1204432315db3d4fec6 (patch) | |
tree | 0f07d060f9f0f7343442f968d1a4be9b1ceff03f | |
parent | f5853b77ef9506df056029282c475e5628fb6ab0 (diff) |
Tma235/231 Individual view and horizontal view (#59)
* Implemented modal stack navigation for moment view, created a rough UI for individual moment view [incl: title, image(not displayed)]
* bare bones beginnning
* Created individual moment screen, moment tile for horizontal view
* Alert
* Fix initial route
Co-authored-by: Ashm Walia <ashmwalia@outlook.com>
Co-authored-by: Ashm Walia <40498934+ashmgarv@users.noreply.github.com>
-rw-r--r-- | package.json | 3 | ||||
-rw-r--r-- | src/components/profile/Content.tsx | 79 | ||||
-rw-r--r-- | src/components/profile/Moment.tsx | 41 | ||||
-rw-r--r-- | src/components/profile/MomentTile.tsx | 33 | ||||
-rw-r--r-- | src/constants/api.ts | 2 | ||||
-rw-r--r-- | src/routes/authentication/AuthProvider.tsx | 9 | ||||
-rw-r--r-- | src/routes/profile/MomentStack.tsx | 11 | ||||
-rw-r--r-- | src/routes/profile/MomentStackScreen.tsx | 46 | ||||
-rw-r--r-- | src/routes/profile/ProfileStack.tsx | 5 | ||||
-rw-r--r-- | src/routes/profile/index.ts | 2 | ||||
-rw-r--r-- | src/routes/tabs/NavigationBar.tsx | 11 | ||||
-rw-r--r-- | src/screens/profile/CaptionScreen.tsx | 14 | ||||
-rw-r--r-- | src/screens/profile/IndividualMoment.tsx | 162 | ||||
-rw-r--r-- | src/screens/profile/index.ts | 1 | ||||
-rw-r--r-- | src/types/types.ts | 8 |
15 files changed, 386 insertions, 41 deletions
diff --git a/package.json b/package.json index c95b8f3f..e21b52c7 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,9 @@ "@react-navigation/native": "^5.6.1", "@react-navigation/stack": "^5.6.2", "@types/react-native-vector-icons": "^6.4.5", - "moment": "^2.27.0", + "moment": "^2.29.1", "react": "16.13.1", + "react-moment": "^1.0.0", "react-native": "0.63.3", "react-native-animatable": "^1.3.3", "react-native-confirmation-code-field": "^6.5.0", diff --git a/src/components/profile/Content.tsx b/src/components/profile/Content.tsx index 8d368747..d52696a7 100644 --- a/src/components/profile/Content.tsx +++ b/src/components/profile/Content.tsx @@ -1,8 +1,11 @@ -import React, {useState} from 'react'; -import {LayoutChangeEvent, StyleSheet, View} from 'react-native'; -import {Text} from 'react-native-animatable'; +import AsyncStorage from '@react-native-community/async-storage'; +import React, {useCallback, useEffect, useState} from 'react'; +import {Alert, LayoutChangeEvent, StyleSheet, View} from 'react-native'; +<!-- import {Text} from 'react-native-animatable'; --> import Animated from 'react-native-reanimated'; -import {defaultMoments} from '../../constants'; +import {AuthContext} from '../../routes/authentication'; +import {MomentType, UserType} from 'src/types'; +import {defaultMoments, MOMENTS_ENDPOINT} from '../../constants'; import {SCREEN_HEIGHT} from '../../utils'; import TaggsBar from '../taggs/TaggsBar'; import Moment from './Moment'; @@ -14,12 +17,70 @@ interface ContentProps { y: Animated.Value<number>; isProfileView: boolean; } + const Content: React.FC<ContentProps> = ({y, isProfileView}) => { const [profileBodyHeight, setProfileBodyHeight] = useState(0); + const {newMomentsAvailable, updateMoments} = React.useContext(AuthContext); + + const [imagesList, setImagesList] = useState<MomentType[]>([]); + const [imagesMap, setImagesMap] = useState<Map<string, MomentType[]>>( + new Map(), + ); const onLayout = (e: LayoutChangeEvent) => { const {height} = e.nativeEvent.layout; setProfileBodyHeight(height); }; + + const {userId, username} = user; + + const createImagesMap = useCallback(() => { + var map = new Map(); + imagesList.forEach(function (imageObject) { + var moment_category = imageObject.moment_category; + if (map.has(moment_category)) { + map.get(moment_category).push(imageObject); + } else { + map.set(moment_category, [imageObject]); + } + }); + + setImagesMap(map); + console.log(map); + }, [imagesList]); + + useEffect(() => { + if (!userId) { + return; + } + + const retrieveMoments = async () => { + try { + const token = await AsyncStorage.getItem('token'); + const response = await fetch(MOMENTS_ENDPOINT + `${userId}/`, { + method: 'GET', + headers: { + Authorization: 'Token ' + token, + }, + }); + const status = response.status; + if (status === 200) { + const data = await response.json(); + setImagesList(data); + updateMoments(!newMomentsAvailable); + } else { + Alert.alert('Could not load moments!'); + } + } catch (err) { + Alert.alert('Could not load moments!'); + } + }; + + if (newMomentsAvailable) { + retrieveMoments(); + createImagesMap(); + } + }, [userId, createImagesMap, updateMoments, newMomentsAvailable]); + return ( <Animated.ScrollView style={styles.container} @@ -30,14 +91,16 @@ const Content: React.FC<ContentProps> = ({y, isProfileView}) => { <ProfileCutout> <ProfileHeader {...{isProfileView}} /> </ProfileCutout> + <ProfileBody {...{onLayout}} /> + <TaggsBar {...{y, profileBodyHeight}} /> <ProfileBody {...{onLayout, isProfileView}} /> <TaggsBar {...{y, profileBodyHeight, isProfileView}} /> {!isProfileView ? ( <View style={styles.momentsContainer}> - {defaultMoments.map((title, index) => ( - <Moment key={index} title={title} images={[]} /> - ))} - </View> + {defaultMoments.map((title, index) => ( + <Moment key={index} title={title} images={imagesMap.get(title)} /> + ))} + </View> ) : ( <React.Fragment /> )} diff --git a/src/components/profile/Moment.tsx b/src/components/profile/Moment.tsx index 6ae8d38e..be7cbfba 100644 --- a/src/components/profile/Moment.tsx +++ b/src/components/profile/Moment.tsx @@ -1,6 +1,6 @@ import {useNavigation} from '@react-navigation/native'; import React from 'react'; -import {StyleSheet, View} from 'react-native'; +import {Alert, StyleSheet, View} from 'react-native'; import {Text} from 'react-native-animatable'; import {ScrollView, TouchableOpacity} from 'react-native-gesture-handler'; import LinearGradient from 'react-native-linear-gradient'; @@ -9,10 +9,12 @@ import BigPlusIcon from '../../assets/icons/plus_icon-02.svg'; import {MOMENTS_TITLE_COLOR} from '../../constants'; import {SCREEN_WIDTH} from '../../utils'; import ImagePicker from 'react-native-image-crop-picker'; +import MomentTile from './MomentTile'; +import {MomentType} from 'src/types'; interface MomentProps { title: string; - images: Array<string>; + images: MomentType[] | undefined; } const Moment: React.FC<MomentProps> = ({title, images}) => { @@ -28,10 +30,13 @@ const Moment: React.FC<MomentProps> = ({title, images}) => { }) .then((picture) => { if ('path' in picture) { - navigation.navigate('CaptionScreen', {title: title, image: picture}); + navigation.navigate('CaptionScreen', { + title: title, + image: picture, + }); } }) - .catch(() => {}); + .catch((err) => {Alert.alert('Unable to upload moment!');}); }; return ( <View style={styles.container}> @@ -47,17 +52,23 @@ const Moment: React.FC<MomentProps> = ({title, images}) => { horizontal showsHorizontalScrollIndicator={false} style={styles.scrollContainer}> - <TouchableOpacity onPress={() => navigateToImagePicker()}> - <LinearGradient - colors={['rgba(105, 141, 211, 1)', 'rgba(105, 141, 211, 0.3)']}> - <View style={styles.defaultImage}> - <BigPlusIcon width={50} height={50} /> - <Text style={styles.defaultImageText}> - Add a moment of your {title.toLowerCase()}! - </Text> - </View> - </LinearGradient> - </TouchableOpacity> + {images && + images.map((imageObj: MomentType) => ( + <MomentTile key={imageObj.moment_id} moment={imageObj} /> + ))} + {(images === undefined || images.length === 0) && ( + <TouchableOpacity onPress={() => navigateToImagePicker()}> + <LinearGradient + colors={['rgba(105, 141, 211, 1)', 'rgba(105, 141, 211, 0.3)']}> + <View style={styles.defaultImage}> + <BigPlusIcon width={50} height={50} /> + <Text style={styles.defaultImageText}> + Add a moment of your {title.toLowerCase()}! + </Text> + </View> + </LinearGradient> + </TouchableOpacity> + )} </ScrollView> </View> ); diff --git a/src/components/profile/MomentTile.tsx b/src/components/profile/MomentTile.tsx new file mode 100644 index 00000000..70b20d40 --- /dev/null +++ b/src/components/profile/MomentTile.tsx @@ -0,0 +1,33 @@ +import {useNavigation} from '@react-navigation/native'; +import React from 'react'; +import {StyleSheet, View, Image, TouchableOpacity} from 'react-native'; +import {MomentType} from 'src/types'; + +interface MomentTileProps { + moment: MomentType; +} +const MomentTile: React.FC<MomentTileProps> = ({moment}) => { + const navigation = useNavigation(); + const {path_hash} = moment; + return ( + <TouchableOpacity + onPress={() => { + navigation.navigate('IndividualMoment', {moment}); + }}> + <View style={styles.image}> + <Image style={styles.image} source={{uri: path_hash}} /> + </View> + </TouchableOpacity> + ); +}; + +const styles = StyleSheet.create({ + image: { + aspectRatio: 1, + height: '100%', + alignItems: 'center', + justifyContent: 'center', + flexDirection: 'column', + }, +}); +export default MomentTile; diff --git a/src/constants/api.ts b/src/constants/api.ts index 8e935714..93a68d65 100644 --- a/src/constants/api.ts +++ b/src/constants/api.ts @@ -12,7 +12,7 @@ export const COVER_PHOTO_ENDPOINT: string = API_URL + 'large-profile-pic/'; export const AVATAR_PHOTO_ENDPOINT: string = API_URL + 'small-profile-pic/'; export const GET_IG_POSTS_ENDPOINT: string = API_URL + 'posts-ig/'; export const SEARCH_ENDPOINT: string = API_URL + 'search/'; -export const MOMENTS_UPLOAD_ENDPOINT: string = API_URL + 'moments/'; +export const MOMENTS_ENDPOINT: string = API_URL + 'moments/'; export const VERIFY_INVITATION_CODE_ENDPOUNT: string = API_URL + 'verify-code/'; // Social Link diff --git a/src/routes/authentication/AuthProvider.tsx b/src/routes/authentication/AuthProvider.tsx index 6f577a73..8dd9fd73 100644 --- a/src/routes/authentication/AuthProvider.tsx +++ b/src/routes/authentication/AuthProvider.tsx @@ -24,6 +24,8 @@ interface AuthContextProps { cover: string | null; instaPosts: Array<InstagramPostType>; recentSearches: Array<ProfilePreviewType>; + newMomentsAvailable: boolean; + updateMoments: (value: boolean) => void; } const NO_USER: UserType = { userId: '', @@ -43,6 +45,8 @@ export const AuthContext = createContext<AuthContextProps>({ cover: null, instaPosts: [], recentSearches: [], + newMomentsAvailable: true, + updateMoments: () => {}, }); /** @@ -57,6 +61,7 @@ const AuthProvider: React.FC = ({children}) => { const [recentSearches, setRecentSearches] = useState< Array<ProfilePreviewType> >([]); + const [newMomentsAvailable, setNewMomentsAvailable] = useState<boolean>(true); const {userId} = user; useEffect(() => { if (!userId) { @@ -90,6 +95,7 @@ const AuthProvider: React.FC = ({children}) => { avatar, cover, instaPosts, + newMomentsAvailable, login: (id, username) => { setUser({...user, userId: id, username}); }, @@ -105,6 +111,9 @@ const AuthProvider: React.FC = ({children}) => { } }, recentSearches, + updateMoments: (value) => { + setNewMomentsAvailable(value); + }, }}> {children} </AuthContext.Provider> diff --git a/src/routes/profile/MomentStack.tsx b/src/routes/profile/MomentStack.tsx new file mode 100644 index 00000000..83853c99 --- /dev/null +++ b/src/routes/profile/MomentStack.tsx @@ -0,0 +1,11 @@ +import {createStackNavigator} from '@react-navigation/stack'; +import {MomentType} from '../../types'; + +export type MomentStackParams = { + Profile: undefined; + IndividualMoment: { + moment: MomentType; + }; +}; + +export const MomentStack = createStackNavigator<MomentStackParams>(); diff --git a/src/routes/profile/MomentStackScreen.tsx b/src/routes/profile/MomentStackScreen.tsx new file mode 100644 index 00000000..8768199a --- /dev/null +++ b/src/routes/profile/MomentStackScreen.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import {IndividualMoment} from '../../screens'; +import {MomentStack} from './MomentStack'; +import Profile from './Profile'; + +const MomentStackScreen: React.FC = () => { + return ( + <MomentStack.Navigator + screenOptions={{ + headerShown: false, + cardStyle: {backgroundColor: 'transparent'}, + cardOverlayEnabled: true, + cardStyleInterpolator: ({current: {progress}}) => ({ + cardStyle: { + opacity: progress.interpolate({ + inputRange: [0, 0.5, 0.9, 1], + outputRange: [0, 0.25, 0.7, 1], + }), + }, + overlayStyle: { + backgroundColor: '#808080', + opacity: progress.interpolate({ + inputRange: [0, 1], + outputRange: [0, 0.9], + extrapolate: 'clamp', + }), + }, + }), + }} + initialRouteName="Profile" + mode="modal"> + <MomentStack.Screen + name="Profile" + component={Profile} + options={{headerShown: false}} + /> + <MomentStack.Screen + name="IndividualMoment" + component={IndividualMoment} + options={{headerShown: false}} + /> + </MomentStack.Navigator> + ); +}; + +export default MomentStackScreen; diff --git a/src/routes/profile/ProfileStack.tsx b/src/routes/profile/ProfileStack.tsx index 63ab9a10..df4d234f 100644 --- a/src/routes/profile/ProfileStack.tsx +++ b/src/routes/profile/ProfileStack.tsx @@ -10,7 +10,10 @@ export type ProfileStackParams = { socialMediaHandle: string; isProfileView: boolean; }; - CaptionScreen: {title: string; image: object}; + CaptionScreen: { + title: string; + image: object; + }; }; export const ProfileStack = createStackNavigator<ProfileStackParams>(); diff --git a/src/routes/profile/index.ts b/src/routes/profile/index.ts index 367f4cc6..1ab9cb7e 100644 --- a/src/routes/profile/index.ts +++ b/src/routes/profile/index.ts @@ -1,2 +1,4 @@ export * from './ProfileStack'; +export * from './MomentStack'; +export * from './MomentStackScreen'; export {default} from './Profile'; diff --git a/src/routes/tabs/NavigationBar.tsx b/src/routes/tabs/NavigationBar.tsx index 2852b565..f05a512b 100644 --- a/src/routes/tabs/NavigationBar.tsx +++ b/src/routes/tabs/NavigationBar.tsx @@ -3,6 +3,7 @@ import React from 'react'; import {NavigationIcon} from '../../components'; import {Home, Notifications, Upload} from '../../screens'; import Profile from '../profile'; +import MomentStackScreen from '../profile/MomentStackScreen'; const Tabs = createBottomTabNavigator(); @@ -35,7 +36,7 @@ const NavigationBar: React.FC = () => { ) : ( <NavigationIcon tab="Notifications" disabled={true} /> ); - } else if (route.name === 'Profile') { + } else if (route.name === 'MomentStackScreen') { return focused ? ( <NavigationIcon tab="Profile" disabled={false} /> ) : ( @@ -44,7 +45,7 @@ const NavigationBar: React.FC = () => { } }, })} - initialRouteName="Profile" + initialRouteName="MomentStackScreen" tabBarOptions={{ showLabel: false, style: { @@ -64,11 +65,7 @@ const NavigationBar: React.FC = () => { /> <Tabs.Screen name="Upload" component={Upload} /> <Tabs.Screen name="Notifications" component={Notifications} /> - <Tabs.Screen - name="Profile" - component={Profile} - initialParams={{isProfileView: false}} - /> + <Tabs.Screen name="MomentStackScreen" component={MomentStackScreen} /> </Tabs.Navigator> ); }; diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx index 53c47a6d..d65a8451 100644 --- a/src/screens/profile/CaptionScreen.tsx +++ b/src/screens/profile/CaptionScreen.tsx @@ -9,7 +9,7 @@ import {RouteProp} from '@react-navigation/native'; import {ProfileStackParams} from 'src/routes'; import {StackNavigationProp} from '@react-navigation/stack'; import {CaptionScreenHeader} from '../../components/profile'; -import {MOMENTS_UPLOAD_ENDPOINT} from '../../constants'; +import {MOMENTS_ENDPOINT} from '../../constants'; import {AuthContext} from '../../routes/authentication'; const NO_USER: UserType = { @@ -34,8 +34,8 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { const {title, image} = route.params; const { user: {userId}, + updateMoments, } = React.useContext(AuthContext); - const [user, setUser] = useState<UserType>(NO_USER); const [caption, setCaption] = React.useState(''); const handleCaptionUpdate = (caption: string) => { @@ -53,11 +53,6 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { const handleShare = async () => { try { - const token = await AsyncStorage.getItem('token'); - if (!token) { - setUser(NO_USER); - return; - } const request = new FormData(); const uri = image.path; const name = image.filename; @@ -69,7 +64,8 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { request.append('moment', title); request.append('user_id', userId); request.append('captions', JSON.stringify({image: caption})); - let response = await fetch(MOMENTS_UPLOAD_ENDPOINT, { + const token = await AsyncStorage.getItem('token'); + let response = await fetch(MOMENTS_ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'multipart/form-data', @@ -81,6 +77,7 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { let data = await response.json(); if (statusCode === 200 && checkImageUploadStatus(data)) { Alert.alert('The picture was uploaded successfully!'); + updateMoments(true); navigation.navigate('Profile'); } else { Alert.alert('An error occured while uploading. Please try again!'); @@ -89,6 +86,7 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { Alert.alert('An error occured during authenticaion. Please login again!'); } }; + return ( <SearchBackground> <View style={styles.contentContainer}> diff --git a/src/screens/profile/IndividualMoment.tsx b/src/screens/profile/IndividualMoment.tsx new file mode 100644 index 00000000..377898c1 --- /dev/null +++ b/src/screens/profile/IndividualMoment.tsx @@ -0,0 +1,162 @@ +import React, {useEffect, useState} from 'react'; +import {StyleSheet, View, Image} from 'react-native'; +import {Button} from 'react-native-elements'; +import {SCREEN_HEIGHT, SCREEN_WIDTH, StatusBarHeight} from '../../utils'; +import {UserType} from '../../types'; +import {RouteProp} from '@react-navigation/native'; +import {StackNavigationProp} from '@react-navigation/stack'; +import {CaptionScreenHeader} from '../../components/profile'; +import {AuthContext} from '../../routes/authentication'; +import {MomentStackParams} from 'src/routes/profile/MomentStack'; +import moment from 'moment'; +import Animated from 'react-native-reanimated'; + +const NO_USER: UserType = { + userId: '', + username: '', +}; + +/** + * Individual moment view opened when user clicks on a moment tile + */ +type IndividualMomentRouteProp = RouteProp< + MomentStackParams, + 'IndividualMoment' +>; +type IndividualMomentNavigationProp = StackNavigationProp< + MomentStackParams, + 'IndividualMoment' +>; +interface IndividualMomentProps { + route: IndividualMomentRouteProp; + navigation: IndividualMomentNavigationProp; +} + +const IndividualMoment: React.FC<IndividualMomentProps> = ({ + route, + navigation, +}) => { + const { + moment_category, + path_hash, + date_time, + moment_id, + } = route.params.moment; + const { + user: {userId}, + } = React.useContext(AuthContext); + const [user, setUser] = useState<UserType>(NO_USER); + const [caption, setCaption] = React.useState(route.params.moment.caption); + const [elapsedTime, setElapsedTime] = React.useState<string>(); + const handleCaptionUpdate = (caption: string) => { + setCaption(caption); + }; + + useEffect(() => { + if (!userId) { + setUser(NO_USER); + } + const timePeriod = async () => { + const datePosted = moment(date_time); + const now = moment(); + var time = date_time; + var difference = now.diff(datePosted, 'seconds'); + + //Creating elapsedTime string to display to user + // 0 to less than 1 minute + if (difference < 60) { + time = difference + 'seconds'; + } + // 1 minute to less than 1 hour + else if (difference >= 60 && difference < 60 * 60) { + difference = now.diff(datePosted, 'minutes'); + time = difference + (difference === 1 ? 'minute' : 'minutes'); + } + //1 hour to less than 1 day + else if (difference >= 60 * 60 && difference < 24 * 60 * 60) { + difference = now.diff(datePosted, 'hours'); + time = difference + (difference === 1 ? 'hour' : 'hours'); + } + //1 day to less than 7 days + else if (difference >= 24 * 60 * 60 && difference < 7 * 24 * 60 * 60) { + difference = now.diff(datePosted, 'days'); + time = difference + (difference === 1 ? 'day' : 'days'); + } + + setElapsedTime(time); + }; + timePeriod(); + }, [date_time, userId]); + + return ( + <View style={styles.contentContainer}> + <View style={styles.buttonsContainer}> + <Button + title="Close" + buttonStyle={styles.button} + onPress={() => { + navigation.navigate('Profile'); + }} + /> + </View> + <CaptionScreenHeader + style={styles.header} + {...{title: moment_category}} + /> + <Image + style={styles.image} + source={{uri: path_hash}} + resizeMode={'cover'} + /> + <View style={styles.bodyContainer}> + <Animated.Text style={styles.text}>{caption}</Animated.Text> + <Animated.Text style={styles.text}>{elapsedTime}</Animated.Text> + </View> + </View> + ); +}; +const styles = StyleSheet.create({ + contentContainer: { + width: SCREEN_WIDTH, + paddingTop: StatusBarHeight, + paddingBottom: SCREEN_HEIGHT, + backgroundColor: 'transparent', + }, + buttonsContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + marginLeft: '3%', + marginRight: '3%', + }, + button: { + backgroundColor: 'transparent', + }, + shareButtonTitle: { + fontWeight: 'bold', + color: '#6EE7E7', + }, + header: { + marginVertical: 20, + }, + image: { + position: 'relative', + width: SCREEN_WIDTH, + aspectRatio: 1, + marginBottom: '3%', + }, + bodyContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + marginLeft: '5%', + marginRight: '5%', + }, + text: { + position: 'relative', + paddingBottom: '1%', + paddingTop: '1%', + color: '#ffffff', + fontWeight: 'bold', + }, +}); + +export default IndividualMoment; diff --git a/src/screens/profile/index.ts b/src/screens/profile/index.ts index 5d2cde7c..6319c17d 100644 --- a/src/screens/profile/index.ts +++ b/src/screens/profile/index.ts @@ -1,3 +1,4 @@ export {default as ProfileScreen} from './ProfileScreen'; export {default as SocialMediaTaggs} from './SocialMediaTaggs'; export {default as CaptionScreen} from './CaptionScreen'; +export {default as IndividualMoment} from './IndividualMoment'; diff --git a/src/types/types.ts b/src/types/types.ts index d8dc95fe..4473878f 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -39,3 +39,11 @@ export interface PostType { export interface LinkerType { label: string; } + +export interface MomentType { + caption: string; + date_time: string; + moment_category: string; + path_hash: string; + moment_id: string; +} |