diff options
author | Ivan Chen <ivan@thetaggid.com> | 2021-02-11 17:27:30 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-02-11 17:27:30 -0500 |
commit | d494b27509066f4d1b61078f1fe6457f20d5f449 (patch) | |
tree | 2418b7e9ff40fbb6d51124644065ead0a6f24e8b /src | |
parent | 2561d20e17a697726d6b77accf79c9da2d1f6ef6 (diff) | |
parent | eeac3efd296656a0ef0a1e5797fec7c9955c7a12 (diff) |
Merge pull request #239 from shravyaramesh/tma641-animation-tutorial
[TMA-641] Suggested People: Swipe Up Animation Tutorial
Diffstat (limited to 'src')
-rw-r--r-- | src/assets/gifs/swipe-animation.gif | bin | 0 -> 160536 bytes | |||
-rw-r--r-- | src/routes/main/MainStackNavigator.tsx | 3 | ||||
-rw-r--r-- | src/routes/main/MainStackScreen.tsx | 37 | ||||
-rw-r--r-- | src/screens/suggestedPeople/AnimatedTutorial.tsx | 92 | ||||
-rw-r--r-- | src/screens/suggestedPeople/SuggestedPeopleScreen.tsx | 40 | ||||
-rw-r--r-- | src/screens/suggestedPeople/index.ts | 1 | ||||
-rw-r--r-- | src/services/UserProfileService.ts | 33 | ||||
-rw-r--r-- | src/store/actions/user.ts | 29 | ||||
-rw-r--r-- | src/store/initialStates.ts | 1 | ||||
-rw-r--r-- | src/store/reducers/userReducer.ts | 7 | ||||
-rw-r--r-- | src/types/types.ts | 1 |
11 files changed, 229 insertions, 15 deletions
diff --git a/src/assets/gifs/swipe-animation.gif b/src/assets/gifs/swipe-animation.gif Binary files differnew file mode 100644 index 00000000..b3b203d0 --- /dev/null +++ b/src/assets/gifs/swipe-animation.gif diff --git a/src/routes/main/MainStackNavigator.tsx b/src/routes/main/MainStackNavigator.tsx index f3aa7fc6..9771c1e6 100644 --- a/src/routes/main/MainStackNavigator.tsx +++ b/src/routes/main/MainStackNavigator.tsx @@ -66,6 +66,9 @@ export type MainStackParams = { screenType: ScreenType; momentCategory: string; }; + AnimatedTutorial: { + screenType: ScreenType; + }; }; export const MainStack = createStackNavigator<MainStackParams>(); diff --git a/src/routes/main/MainStackScreen.tsx b/src/routes/main/MainStackScreen.tsx index 99446432..0b762dff 100644 --- a/src/routes/main/MainStackScreen.tsx +++ b/src/routes/main/MainStackScreen.tsx @@ -6,6 +6,7 @@ import {StyleSheet, Text} from 'react-native'; import {normalize} from 'react-native-elements'; import BackIcon from '../../assets/icons/back-arrow.svg'; import { + AnimatedTutorial, CaptionScreen, CategorySelection, CreateCustomCategory, @@ -70,6 +71,34 @@ const MainStackScreen: React.FC<MainStackProps> = ({route}) => { } })(); + const modalStyle: StackNavigationOptions = { + cardStyle: {backgroundColor: 'rgba(80,80,80,0.9)'}, + gestureDirection: 'vertical', + cardOverlayEnabled: true, + cardStyleInterpolator: ({current: {progress}}) => ({ + cardStyle: { + opacity: progress.interpolate({ + inputRange: [0, 0.5, 0.9, 1], + outputRange: [0, 0.25, 0.7, 1], + }), + }, + }), + }; + + const tutorialModalStyle: StackNavigationOptions = { + cardStyle: {backgroundColor: 'rgba(0, 0, 0, 0.5)'}, + gestureDirection: 'vertical', + cardOverlayEnabled: true, + cardStyleInterpolator: ({current: {progress}}) => ({ + cardStyle: { + opacity: progress.interpolate({ + inputRange: [0, 0.5, 0.9, 1], + outputRange: [0, 0.25, 0.7, 1], + }), + }, + }), + }; + return ( <MainStack.Navigator screenOptions={{ @@ -115,6 +144,14 @@ const MainStackScreen: React.FC<MainStackProps> = ({route}) => { /> )} <MainStack.Screen + name="AnimatedTutorial" + component={AnimatedTutorial} + options={{ + ...tutorialModalStyle, + }} + initialParams={{screenType}} + /> + <MainStack.Screen name="CaptionScreen" component={CaptionScreen} options={{ diff --git a/src/screens/suggestedPeople/AnimatedTutorial.tsx b/src/screens/suggestedPeople/AnimatedTutorial.tsx new file mode 100644 index 00000000..8ebdaea6 --- /dev/null +++ b/src/screens/suggestedPeople/AnimatedTutorial.tsx @@ -0,0 +1,92 @@ +import * as React from 'react'; +import CloseIcon from '../../assets/ionicons/close-outline.svg'; +import {StyleSheet, Text, View} from 'react-native'; +import {Image} from 'react-native-animatable'; +import {isIPhoneX, SCREEN_WIDTH} from '../../utils'; +import {SafeAreaView} from 'react-native-safe-area-context'; +import {useNavigation} from '@react-navigation/native'; +import {useDispatch, useSelector} from 'react-redux'; +import {RootState} from '../../store/rootReducer'; +import {updateSPSwipeTutorial} from '../../store/actions/user'; + +const AnimatedTutorial: React.FC = () => { + const navigation = useNavigation(); + const dispatch = useDispatch(); + const {user} = useSelector((state: RootState) => state.user); + + const handleCloseAnimationTutorial = async () => { + /* In user's store, check if profile.sp_swipe_tutorial === 0 + * Make call to edit profile endpoint with suggested people === 1 + */ + const data = 1; + dispatch(updateSPSwipeTutorial(user, data)); + navigation.pop(); + }; + return ( + <SafeAreaView> + <View style={styles.container}> + <CloseIcon + height={'10%'} + width={'10%'} + color={'white'} + style={styles.closeButton} + onPress={handleCloseAnimationTutorial} + /> + <View style={styles.textContainer}> + <Text style={styles.text}>{'Swipe up to discover more people!'}</Text> + </View> + <Image + source={require('../../assets/gifs/swipe-animation.gif')} + style={styles.swipeGif} + /> + </View> + </SafeAreaView> + ); +}; + +const styles = StyleSheet.create({ + container: { + flexDirection: 'column', + }, + closeButton: { + top: '2.55%', + left: '5%', + }, + text: { + justifyContent: 'center', + color: '#fff', + fontWeight: 'bold', + fontSize: 20, + textAlign: 'center', + position: 'relative', + top: '100%', + }, + textContainer: { + width: isIPhoneX() ? SCREEN_WIDTH * 0.5 : SCREEN_WIDTH * 0.6, + alignSelf: 'center', + top: isIPhoneX() ? '65%' : '45%', + }, + swipeGif: { + width: 333, + height: 250, + left: '22.5%', + top: isIPhoneX() ? '75%' : '45%', + }, + + //Styles to adjust moment container + momentScrollContainer: { + backgroundColor: 'transparent', + }, + momentContainer: { + top: '62%', + backgroundColor: 'transparent', + }, + momentHeaderText: { + paddingBottom: '5%', + }, + momentHeader: { + backgroundColor: 'transparent', + }, +}); + +export default AnimatedTutorial; diff --git a/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx b/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx index fffaf715..4d0a9bd5 100644 --- a/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx +++ b/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, {useCallback} from 'react'; import { StatusBar, StyleSheet, @@ -7,20 +7,15 @@ import { View, } from 'react-native'; import {Image} from 'react-native-animatable'; -import { - fetchUserX, - isIPhoneX, - SCREEN_HEIGHT, - SCREEN_WIDTH, - userXInStore, -} from '../../utils'; +import {isIPhoneX, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; import {TabsGradient, TaggsBar} from '../../components'; import {SafeAreaView} from 'react-native-safe-area-context'; import {normalize} from '../../utils'; import Animated from 'react-native-reanimated'; import {ScreenType} from '../../types'; -import {useDispatch, useStore} from 'react-redux'; +import {useSelector} from 'react-redux'; import {RootState} from '../../store/rootReducer'; +import {useFocusEffect, useNavigation} from '@react-navigation/native'; /** * Bare bones for suggested people consisting of: @@ -33,6 +28,25 @@ const SuggestedPeopleScreen: React.FC = () => { // Adviced to maintain username as a variable here to append @ symbol for maintainability const username = '@' + 'sarahmiller'; + const navigation = useNavigation(); + const screenType = ScreenType.SuggestedPeople; + const { + profile: {sp_swipe_tutorial}, + } = useSelector((state: RootState) => state.user); + + useFocusEffect( + useCallback(() => { + const navigateToAnimatedTutorial = () => { + /* In user's store, check if profile.sp_swipe_tutorial === 0 + * If, true show tutorial. + */ + if (sp_swipe_tutorial === 0) { + navigation.navigate('AnimatedTutorial'); + } + }; + navigateToAnimatedTutorial(); + }, [sp_swipe_tutorial, navigation]), + ); return ( <> @@ -46,7 +60,7 @@ const SuggestedPeopleScreen: React.FC = () => { <View style={styles.mainContainer}> <Text style={styles.title}>Suggested People</Text> <View style={styles.body}> - {/* Added first row contaning name, username, add button (w/o functionality) */} + {/* First row contaning name, username, add button (w/o functionality) */} <View style={styles.addUserContainer}> <View style={styles.nameInfoContainer}> <Text style={styles.firstName}>{firstName}</Text> @@ -54,18 +68,20 @@ const SuggestedPeopleScreen: React.FC = () => { </View> <TouchableOpacity activeOpacity={0.5} + // TODO: Call function to Add Friend onPress={() => console.log('Call add friend function')}> <View style={styles.addButton}> <Text style={styles.addButtonTitle}>{'Add Friend'}</Text> </View> </TouchableOpacity> </View> - {/* TODO: Add TaggsBar here */} + {/* Taggs Bar. Displays only linked profiles for user while viewing their own profile. */} <TaggsBar y={Animated.useValue(0)} + // :: For testing purposes, to view user's own profile, pass userXId='' userXId={''} profileBodyHeight={0} - screenType={ScreenType.SuggestedPeople} + screenType={screenType} /> {/* TODO: Add MutualFriends here */} </View> diff --git a/src/screens/suggestedPeople/index.ts b/src/screens/suggestedPeople/index.ts index a42d9c52..8c06d81e 100644 --- a/src/screens/suggestedPeople/index.ts +++ b/src/screens/suggestedPeople/index.ts @@ -1 +1,2 @@ export {default as SuggestedPeopleScreen} from './SuggestedPeopleScreen'; +export {default as AnimatedTutorial} from './AnimatedTutorial'; diff --git a/src/services/UserProfileService.ts b/src/services/UserProfileService.ts index d0610714..3bca66f3 100644 --- a/src/services/UserProfileService.ts +++ b/src/services/UserProfileService.ts @@ -2,7 +2,7 @@ import AsyncStorage from '@react-native-community/async-storage'; import moment from 'moment'; import {Alert} from 'react-native'; import RNFetchBlob from 'rn-fetch-blob'; -import {SocialAccountType} from '../types'; +import {SocialAccountType, UserType} from '../types'; import { PROFILE_PHOTO_ENDPOINT, HEADER_PHOTO_ENDPOINT, @@ -15,6 +15,7 @@ import { VERIFY_OTP_ENDPOINT, SEND_OTP_ENDPOINT, PROFILE_PHOTO_THUMBNAIL_ENDPOINT, + EDIT_PROFILE_ENDPOINT, } from '../constants'; import { ERROR_DOUBLE_CHECK_CONNECTION, @@ -49,10 +50,15 @@ export const loadProfileInfo = async (token: string, userId: string) => { tiktok, university_class, profile_completion_stage, + sp_swipe_tutorial, friendship_status, friendship_requester_id, } = info; birthday = birthday && moment(birthday).format('YYYY-MM-DD'); + console.log( + 'Suggested People loaded from backend for logged in user: ', + sp_swipe_tutorial, + ); return { name, biography, @@ -63,6 +69,7 @@ export const loadProfileInfo = async (token: string, userId: string) => { tiktok, university_class, profile_completion_stage, + sp_swipe_tutorial, friendship_status, friendship_requester_id, }; @@ -313,3 +320,27 @@ export const sendOtp = async (phone: string) => { return false; } }; + +export const editSPSwipeTutorial = async (user: UserType) => { + try { + const request = new FormData(); + request.append('sp_swipe_tutorial', 1); + const endpoint = EDIT_PROFILE_ENDPOINT + `${user.userId}/`; + const token = await AsyncStorage.getItem('token'); + let response = await fetch(endpoint, { + method: 'PATCH', + headers: { + 'Content-Type': 'multipart/form-data', + Authorization: 'Token ' + token, + }, + body: request, + }); + if (response.status === 200) { + return true; + } else { + return false; + } + } catch (error) { + console.log('Error updating animated tutorial close button press'); + } +}; diff --git a/src/store/actions/user.ts b/src/store/actions/user.ts index 589e6f0d..990f9260 100644 --- a/src/store/actions/user.ts +++ b/src/store/actions/user.ts @@ -1,5 +1,10 @@ import {Action, ThunkAction} from '@reduxjs/toolkit'; -import {loadAvatar, loadCover, loadProfileInfo} from '../../services'; +import { + editSPSwipeTutorial, + loadAvatar, + loadCover, + loadProfileInfo, +} from '../../services'; import {UserType} from '../../types/types'; import {getTokenOrLogout} from '../../utils'; import { @@ -12,6 +17,7 @@ import { userDetailsFetched, userLoggedIn, } from '../reducers'; +import {spSwipeTutorialUpdated} from '../reducers/userReducer'; import {RootState} from '../rootReducer'; import {CommentThreadType} from './../../types/types'; @@ -156,3 +162,24 @@ export const logout = (): ThunkAction< console.log(error); } }; + +export const updateSPSwipeTutorial = ( + user: UserType, + data: number, +): ThunkAction< + Promise<boolean | undefined>, + RootState, + unknown, + Action<string> +> => async (dispatch) => { + try { + // update store first, assume success + dispatch({ + type: spSwipeTutorialUpdated.type, + payload: {sp_swipe_tutorial: data}, + }); + return await editSPSwipeTutorial(user); + } catch (error) { + console.log('Error while updating suggested people linked state: ', error); + } +}; diff --git a/src/store/initialStates.ts b/src/store/initialStates.ts index 6ca133e0..93b1bc6e 100644 --- a/src/store/initialStates.ts +++ b/src/store/initialStates.ts @@ -21,6 +21,7 @@ export const NO_PROFILE: ProfileType = { //Default to an invalid value and ignore it gracefully while showing tutorials / popups. profile_completion_stage: -1, + sp_swipe_tutorial: 0, snapchat: '', tiktok: '', friendship_status: 'no_record', diff --git a/src/store/reducers/userReducer.ts b/src/store/reducers/userReducer.ts index 29ec38cc..773977db 100644 --- a/src/store/reducers/userReducer.ts +++ b/src/store/reducers/userReducer.ts @@ -1,4 +1,4 @@ -import {createSlice, Action} from '@reduxjs/toolkit'; +import {createSlice} from '@reduxjs/toolkit'; import {NO_USER_DATA} from '../initialStates'; /** @@ -46,6 +46,10 @@ const userDataSlice = createSlice({ state.profile.profile_completion_stage = action.payload.stage; }, + spSwipeTutorialUpdated: (state, action) => { + state.profile.sp_swipe_tutorial = action.payload.sp_swipe_tutorial; + }, + setIsOnboardedUser: (state, action) => { state.isOnboardedUser = action.payload.isOnboardedUser; }, @@ -73,5 +77,6 @@ export const { setNewVersionAvailable, setNewNotificationReceived, setReplyPosted, + spSwipeTutorialUpdated, } = userDataSlice.actions; export const userDataReducer = userDataSlice.reducer; diff --git a/src/types/types.ts b/src/types/types.ts index 7fccaa44..fe16fb8e 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -23,6 +23,7 @@ export interface ProfileType { gender: string; university_class: number; profile_completion_stage: number; + sp_swipe_tutorial: number; birthday: Date | undefined; snapchat: string; tiktok: string; |