import {Alert} from 'react-native'; import AsyncStorage from '@react-native-community/async-storage'; import {INTEGRATED_SOCIAL_LIST} from '../constants'; import {isUserBlocked, loadSocialPosts, removeBadgesService} from '../services'; import { loadAllSocials, loadBlockedList, loadFriendsData, loadRecentlySearched, loadUserData, loadUserMoments, loadUserNotifications, logout, updateUserBadges, } from '../store/actions'; import {NO_SOCIAL_ACCOUNTS} from '../store/initialStates'; import {loadUserMomentCategories} from './../store/actions/momentCategories'; import {loadUserX} from './../store/actions/userX'; import {AppDispatch} from './../store/configureStore'; import {RootState} from './../store/rootReducer'; import { ProfileInfoType, ProfilePreviewType, ScreenType, UserType, UniversityBadge, } from './../types/types'; import ImagePicker from 'react-native-image-crop-picker'; import {patchEditProfile} from '../services'; const loadData = async (dispatch: AppDispatch, user: UserType) => { await Promise.all([ dispatch(loadUserData(user)), dispatch(loadFriendsData(user.userId)), dispatch(loadUserMomentCategories(user.userId)), dispatch(loadUserMoments(user.userId)), dispatch(loadUserNotifications()), dispatch(loadAllSocials(user.userId)), dispatch(loadBlockedList(user.userId)), dispatch(loadRecentlySearched()), ]); }; /** * This tries to log the user in present with the AsyncStorage if user.userId is empty * Else it tries to login the user passed in * @param dispatch This is the dispatch object from the redux store * @param user The user if at all any */ export const userLogin = async (dispatch: AppDispatch, user: UserType) => { try { let localUser = {...user}; if (!user.userId) { const [id, username, token] = await Promise.all([ AsyncStorage.getItem('userId'), AsyncStorage.getItem('username'), AsyncStorage.getItem('token'), ]); if (id && username && token) { localUser = {...localUser, userId: id, username: username}; } else { return; } } else { await Promise.all([ AsyncStorage.setItem('userId', user.userId), AsyncStorage.setItem('username', user.username), ]); } await loadData(dispatch, localUser); } catch (error) { console.log(error); } }; /** * This tries to load data userX passed in * @param dispatch This is the dispatch object from the redux store * @param user The user */ export const fetchUserX = async ( dispatch: AppDispatch, user: UserType, screenType: ScreenType, ) => { try { await Promise.all([dispatch(loadUserX(user, screenType))]); } catch (error) { console.log(error); } }; /** * This function checks if the userX slice of our store contains the given user for the provided Screen */ export const userXInStore = ( state: RootState, screen: ScreenType, userId: string, ) => { const userX = state.userX[screen]; return userX && userId in userX && userX[userId].user.userId; }; /** * Abstracted the code to laod all socials out. * @param userId userId for whom socials should be fetched */ export const loadAllSocialsForUser = async (userId: string, token?: string) => { if (!token) { token = (await AsyncStorage.getItem('token')) ?? ''; } let socials = NO_SOCIAL_ACCOUNTS; try { const fetchedSocials = await Promise.all( INTEGRATED_SOCIAL_LIST.map((socialType) => loadSocialPosts(userId, socialType, token).then((data) => ({ key: socialType, data, })), ), ); for (const fetchedSocial of fetchedSocials) { socials = {...socials, [fetchedSocial.key]: fetchedSocial.data}; } return socials; } catch (error) { console.log(error); } }; /** * Push the user out of system if token is not present in async storage * @param dispatch */ export const getTokenOrLogout = async (dispatch: Function): Promise => { const token = await AsyncStorage.getItem('token'); if (!token) { dispatch(logout()); return ''; } return token; }; /** * Creates ProfilePreviewType of a user using UserType && ProfileType * @param passedInUser This is the UserType of the user * @param passedInProfile This is the ProfileType of the user */ export const getUserAsProfilePreviewType = ( passedInUser: UserType, passedInProfile: ProfileInfoType, ): ProfilePreviewType => { const fullName = passedInProfile.name.split(' '); return { id: passedInUser.userId, username: passedInUser.username, first_name: fullName[0], last_name: fullName[1], }; }; export const checkIfUserIsBlocked = async ( userId: string, dispatch: Function, loggedInUser: UserType, ) => { const token = await AsyncStorage.getItem('token'); if (!token) { dispatch(logout()); return false; } return await isUserBlocked(userId, loggedInUser.userId, token); }; /** * Used to determine whether the logged-in user is able to view userX's private * information or not. * * @param state redux store's root state * @param userXId target userX's id * @param screenType current screen type * @returns true if abel to view private info, false otherwise */ export const canViewProfile = ( state: RootState, userXId: string | undefined, screenType: ScreenType, ) => { // own profile if (!userXId || state.user.user.userId === userXId) { return true; } // not private if (!(userXId && state.userX[screenType][userXId].profile.is_private)) { return true; } // is friend if ( userXId && state.userX[screenType][userXId].profile.friendship_status === 'friends' ) { return true; } return false; }; /* Function to call remove badge service, * remove selected badge from list passed in and * dispatch thunk action to update store */ export const removeUserBadge = async ( badges: UniversityBadge[], badgeName: string, userId: string, dispatch: AppDispatch, ) => { const success = await removeBadgesService([badgeName], userId); if (success === true) { badges = badges.filter((badge) => badge.name !== badgeName); dispatch(updateUserBadges(badges)); } }; export const navigateToProfile = async ( state: RootState, dispatch: any, navigation: any, screenType: ScreenType, user: UserType, ) => { const loggedInUserId = state.user.user.userId; const {userId, username} = user; if (!userXInStore(state, screenType, userId)) { await fetchUserX( dispatch, {userId: userId, username: username}, screenType, ); } navigation.push('Profile', { userXId: userId === loggedInUserId ? undefined : userId, screenType, }); }; /* Function to open imagepicker and * select images, which are sent to * the database to update the profile */ export const patchProfile = async ( title: 'profile' | 'header', userId: string, ) => { let imageSettings = {}; let screenTitle: string; let requestTitle: string; let fileName: string; switch (title) { case 'header': screenTitle = 'Select Header Picture'; requestTitle = 'largeProfilePicture'; fileName = 'large_profile_pic.jpg'; imageSettings = { smartAlbums: [ 'Favorites', 'RecentlyAdded', 'SelfPortraits', 'Screenshots', 'UserLibrary', ], width: 580, height: 580, cropping: true, cropperToolbarTitle: screenTitle, mediaType: 'photo', }; break; case 'profile': screenTitle = 'Select Profile Picture'; requestTitle = 'smallProfilePicture'; fileName = 'small_profile_pic.jpg'; imageSettings = { smartAlbums: [ 'Favorites', 'RecentlyAdded', 'SelfPortraits', 'Screenshots', 'UserLibrary', ], width: 580, height: 580, cropping: true, cropperToolbarTitle: screenTitle, mediaType: 'photo', cropperCircleOverlay: true, }; break; default: screenTitle = ''; requestTitle = ''; fileName = ''; } return await ImagePicker.openPicker(imageSettings) .then((picture) => { if ('path' in picture) { const request = new FormData(); request.append(requestTitle, { uri: picture.path, name: fileName, type: 'image/jpg', }); return patchEditProfile(request, userId) .then((_) => { return true; }) .catch((error) => { Alert.alert(error); return false; }); } }) .catch((_) => { return false; }); };