aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/components/profile/Content.tsx57
-rw-r--r--src/components/profile/FollowUnfollow.tsx40
-rw-r--r--src/components/profile/ProfileBody.tsx33
-rw-r--r--src/components/profile/ProfilePreview.tsx21
-rw-r--r--src/components/profile/ToggleButton.tsx44
-rw-r--r--src/constants/api.ts1
-rw-r--r--src/constants/constants.ts5
-rw-r--r--src/routes/authentication/AuthProvider.tsx42
-rw-r--r--src/services/BlockUserService.ts98
-rw-r--r--src/services/index.ts1
-rw-r--r--src/utils/common.ts15
-rw-r--r--src/utils/index.ts1
12 files changed, 303 insertions, 55 deletions
diff --git a/src/components/profile/Content.tsx b/src/components/profile/Content.tsx
index 7afc3fbc..13db60a5 100644
--- a/src/components/profile/Content.tsx
+++ b/src/components/profile/Content.tsx
@@ -11,7 +11,7 @@ import {Moment} from '../moments';
import ProfileBody from './ProfileBody';
import ProfileCutout from './ProfileCutout';
import ProfileHeader from './ProfileHeader';
-import {followOrUnfollowUser} from '../../services';
+import {followOrUnfollowUser, blockOrUnblockUser} from '../../services';
interface ContentProps {
y: Animated.Value<number>;
@@ -24,10 +24,12 @@ const Content: React.FC<ContentProps> = ({y, isProfileView}) => {
? React.useContext(ProfileContext)
: React.useContext(AuthContext);
- const {logout} = React.useContext(AuthContext);
const {
+ logout,
user: loggedInUser,
updateFollowers: updateLoggedInUserFollowers,
+ blockedUsers,
+ updateBlockedUsers,
} = React.useContext(AuthContext);
/**
@@ -36,7 +38,8 @@ const Content: React.FC<ContentProps> = ({y, isProfileView}) => {
const [imagesMap, setImagesMap] = useState<Map<string, MomentType[]>>(
new Map(),
);
- const [followed, setFollowed] = React.useState<boolean>(false);
+ const [isFollowed, setIsFollowed] = React.useState<boolean>(false);
+ const [isBlocked, setIsBlocked] = React.useState<boolean>(false);
/**
* If own profile is being viewed then do not show the follow button.
@@ -81,11 +84,24 @@ const Content: React.FC<ContentProps> = ({y, isProfileView}) => {
const isActuallyFollowed = followers.some(
(follower) => follower.username === loggedInUser.username,
);
- if (followed != isActuallyFollowed) {
- setFollowed(isActuallyFollowed);
+ if (isFollowed != isActuallyFollowed) {
+ setIsFollowed(isActuallyFollowed);
}
}, [followers]);
+ useEffect(() => {
+ if (!userId) {
+ return;
+ }
+
+ const isActuallyBlocked = blockedUsers.some(
+ (cur_user) => user.username === cur_user.username,
+ );
+ if (isBlocked != isActuallyBlocked) {
+ setIsBlocked(isActuallyBlocked);
+ }
+ }, [blockedUsers]);
+
/**
* Handles a click on the follow / unfollow button.
* updateFollowers and updateLoggedInUerFollowers to make sure that we update followers list / count for both the users in context.
@@ -100,10 +116,33 @@ const Content: React.FC<ContentProps> = ({y, isProfileView}) => {
loggedInUser.userId,
userId,
token,
- followed,
+ isFollowed,
+ );
+ if (isUpdatedSuccessful) {
+ setIsFollowed(!isFollowed);
+ updateFollowers(true);
+ updateLoggedInUserFollowers(true);
+ }
+ };
+
+ /**
+ * Handles a click on the block / unblock button.
+ */
+ const handleBlockUnblock = async () => {
+ const token = await AsyncStorage.getItem('token');
+ if (!token) {
+ logout();
+ return;
+ }
+ const isUpdatedSuccessful = await blockOrUnblockUser(
+ loggedInUser.userId,
+ userId,
+ token,
+ isBlocked,
);
if (isUpdatedSuccessful) {
- setFollowed(!followed);
+ setIsBlocked(!isBlocked);
+ updateBlockedUsers(true);
updateFollowers(true);
updateLoggedInUserFollowers(true);
}
@@ -126,8 +165,10 @@ const Content: React.FC<ContentProps> = ({y, isProfileView}) => {
onLayout,
isProfileView,
isOwnProfile,
- followed,
+ isFollowed,
handleFollowUnfollow,
+ isBlocked,
+ handleBlockUnblock,
}}
/>
<TaggsBar {...{y, profileBodyHeight, isProfileView}} />
diff --git a/src/components/profile/FollowUnfollow.tsx b/src/components/profile/FollowUnfollow.tsx
deleted file mode 100644
index fa224be3..00000000
--- a/src/components/profile/FollowUnfollow.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import * as React from 'react';
-import {StyleSheet, Text} from 'react-native';
-import {TouchableOpacity} from 'react-native-gesture-handler';
-
-type FollowUnfollowProps = {
- followed: boolean;
- handleFollowUnfollow: Function;
-};
-
-const FollowUnfollow: React.FC<FollowUnfollowProps> = ({
- followed,
- handleFollowUnfollow,
-}) => {
- return (
- <TouchableOpacity
- style={styles.button}
- onPress={() => handleFollowUnfollow()}>
- <Text style={styles.text}>{!followed ? 'Follow' : 'Unfollow'}</Text>
- </TouchableOpacity>
- );
-};
-
-const styles = StyleSheet.create({
- button: {
- justifyContent: 'center',
- alignItems: 'center',
- width: 110,
- height: 40,
- borderRadius: 8,
- marginTop: '5%',
- borderColor: '#698dd3',
- backgroundColor: 'white',
- borderWidth: 3,
- },
- text: {
- fontWeight: 'bold',
- color: '#698dd3',
- },
-});
-export default FollowUnfollow;
diff --git a/src/components/profile/ProfileBody.tsx b/src/components/profile/ProfileBody.tsx
index 7091a077..db8c6e0b 100644
--- a/src/components/profile/ProfileBody.tsx
+++ b/src/components/profile/ProfileBody.tsx
@@ -1,21 +1,26 @@
import React from 'react';
import {StyleSheet, View, Text, LayoutChangeEvent} from 'react-native';
+import {TOGGLE_BUTTON_TYPE} from '../../constants';
import {AuthContext, ProfileContext} from '../../routes/';
-import FollowUnfollow from './FollowUnfollow';
+import ToggleButton from './ToggleButton';
interface ProfileBodyProps {
onLayout: (event: LayoutChangeEvent) => void;
isProfileView: boolean;
- followed: boolean;
+ isFollowed: boolean;
+ isBlocked: boolean;
isOwnProfile: boolean;
handleFollowUnfollow: Function;
+ handleBlockUnblock: Function;
}
const ProfileBody: React.FC<ProfileBodyProps> = ({
onLayout,
isProfileView,
- followed,
+ isFollowed,
+ isBlocked,
isOwnProfile,
handleFollowUnfollow,
+ handleBlockUnblock,
}) => {
const {
profile,
@@ -32,10 +37,20 @@ const ProfileBody: React.FC<ProfileBodyProps> = ({
<Text style={styles.biography}>{`${biography}`}</Text>
<Text style={styles.website}>{`${website}`}</Text>
{isProfileView && !isOwnProfile ? (
- <FollowUnfollow
- followed={followed}
- handleFollowUnfollow={handleFollowUnfollow}
- />
+ <View style={styles.toggleButtonContainer}>
+ {!isBlocked && (
+ <ToggleButton
+ toggleState={isFollowed}
+ handleToggle={handleFollowUnfollow}
+ buttonType={TOGGLE_BUTTON_TYPE.FOLLOW_UNFOLLOW}
+ />
+ )}
+ <ToggleButton
+ toggleState={isBlocked}
+ handleToggle={handleBlockUnblock}
+ buttonType={TOGGLE_BUTTON_TYPE.BLOCK_UNBLOCK}
+ />
+ </View>
) : (
<></>
)}
@@ -44,6 +59,10 @@ const ProfileBody: React.FC<ProfileBodyProps> = ({
};
const styles = StyleSheet.create({
+ toggleButtonContainer: {
+ flexDirection: 'row',
+ flex: 1,
+ },
container: {
paddingVertical: 5,
paddingHorizontal: 20,
diff --git a/src/components/profile/ProfilePreview.tsx b/src/components/profile/ProfilePreview.tsx
index 9c953e7d..abc29422 100644
--- a/src/components/profile/ProfilePreview.tsx
+++ b/src/components/profile/ProfilePreview.tsx
@@ -7,12 +7,15 @@ import {
StyleSheet,
ViewProps,
TouchableOpacity,
+ Alert,
} from 'react-native';
import {useNavigation} from '@react-navigation/native';
import RNFetchBlob from 'rn-fetch-blob';
import AsyncStorage from '@react-native-community/async-storage';
import {AVATAR_PHOTO_ENDPOINT} from '../../constants';
import {UserType} from '../../types';
+import {AuthContext} from '../../routes/';
+import {isUserBlocked} from '../../services';
const NO_USER: UserType = {
userId: '',
username: '',
@@ -38,6 +41,7 @@ const ProfilePreview: React.FC<ProfilePreviewProps> = ({
style,
}) => {
const navigation = useNavigation();
+ const {user: loggedInUser, logout} = React.useContext(AuthContext);
const [avatarURI, setAvatarURI] = useState<string | null>(null);
const [user, setUser] = useState<UserType>(NO_USER);
useEffect(() => {
@@ -80,6 +84,16 @@ const ProfilePreview: React.FC<ProfilePreviewProps> = ({
* Cache maintains 10 recently searched users, popping off the oldest one if
* needed to make space.
*/
+
+ const checkIfUserIsBlocked = async (userId: string) => {
+ const token = await AsyncStorage.getItem('token');
+ if (!token) {
+ logout();
+ return false;
+ }
+ return await isUserBlocked(userId, loggedInUser.userId, token);
+ };
+
const addToRecentlyStoredAndNavigateToProfile = async () => {
let user: ProfilePreviewType = {
id,
@@ -87,7 +101,14 @@ const ProfilePreview: React.FC<ProfilePreviewProps> = ({
first_name,
last_name,
};
+
try {
+ //If the logged in user is blocked by the user being viewed, do not proceed.
+ const isUserBlocked = await checkIfUserIsBlocked(user.id);
+ if (isUserBlocked) {
+ Alert.alert('You cannot view this profile');
+ return;
+ }
if (!isComment) {
const jsonValue = await AsyncStorage.getItem(
'@recently_searched_users',
diff --git a/src/components/profile/ToggleButton.tsx b/src/components/profile/ToggleButton.tsx
new file mode 100644
index 00000000..ff14cdde
--- /dev/null
+++ b/src/components/profile/ToggleButton.tsx
@@ -0,0 +1,44 @@
+import * as React from 'react';
+import {StyleSheet, Text} from 'react-native';
+import {TouchableOpacity} from 'react-native-gesture-handler';
+import {getToggleButtonText, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+
+type ToggleButtonProps = {
+ toggleState: boolean;
+ handleToggle: Function;
+ buttonType: string;
+};
+
+const ToggleButton: React.FC<ToggleButtonProps> = ({
+ toggleState,
+ handleToggle,
+ buttonType,
+}) => {
+ return (
+ <TouchableOpacity style={styles.button} onPress={() => handleToggle()}>
+ <Text style={styles.text}>
+ {getToggleButtonText(buttonType, toggleState)}
+ </Text>
+ </TouchableOpacity>
+ );
+};
+
+const styles = StyleSheet.create({
+ button: {
+ justifyContent: 'center',
+ alignItems: 'center',
+ width: SCREEN_WIDTH * 0.25,
+ height: SCREEN_WIDTH * 0.1,
+ borderRadius: 8,
+ marginTop: '3%',
+ borderColor: '#698dd3',
+ backgroundColor: 'white',
+ borderWidth: 3,
+ marginHorizontal: '1%',
+ },
+ text: {
+ fontWeight: 'bold',
+ color: '#698dd3',
+ },
+});
+export default ToggleButton;
diff --git a/src/constants/api.ts b/src/constants/api.ts
index 4effc1d3..f69ef943 100644
--- a/src/constants/api.ts
+++ b/src/constants/api.ts
@@ -21,6 +21,7 @@ export const FOLLOW_USER_ENDPOINT: string = API_URL + 'follow/';
export const UNFOLLOW_USER_ENDPOINT: string = API_URL + 'unfollow/';
export const FOLLOWERS_ENDPOINT: string = API_URL + 'followers/';
export const FOLLOWING_ENDPOINT: string = API_URL + 'following/';
+export const BLOCK_USER_ENDPOINT: string = API_URL + 'block/';
// Register Social Link (Non-integrated)
export const LINK_SNAPCHAT_ENDPOINT: string = API_URL + 'link-sc/';
diff --git a/src/constants/constants.ts b/src/constants/constants.ts
index 123d0617..dbd79b45 100644
--- a/src/constants/constants.ts
+++ b/src/constants/constants.ts
@@ -69,6 +69,11 @@ export const SOCIAL_FONT_COLORS = {
YOUTUBE: YOUTUBE_FONT_COLOR,
};
+export const TOGGLE_BUTTON_TYPE = {
+ FOLLOW_UNFOLLOW: 'Follow',
+ BLOCK_UNBLOCK: 'Block',
+};
+
// Profile Moments
export const defaultMoments: Array<string> = [
'Early Life',
diff --git a/src/routes/authentication/AuthProvider.tsx b/src/routes/authentication/AuthProvider.tsx
index 383b4d5b..7da47b71 100644
--- a/src/routes/authentication/AuthProvider.tsx
+++ b/src/routes/authentication/AuthProvider.tsx
@@ -10,6 +10,7 @@ import {
loadProfileInfo,
loadRecentlySearchedUsers,
loadSocialPosts,
+ loadBlockedUsers,
} from '../../services';
import {
MomentType,
@@ -36,6 +37,9 @@ interface AuthContextProps {
following: ProfilePreviewType[];
followersNeedUpdate: boolean;
updateFollowers: (value: boolean) => void;
+ blockedUsers: ProfilePreviewType[];
+ blockedUsersNeedUpdate: boolean;
+ updateBlockedUsers: (value: boolean) => void;
}
const NO_USER: UserType = {
@@ -72,6 +76,9 @@ export const AuthContext = createContext<AuthContextProps>({
following: [],
followersNeedUpdate: true,
updateFollowers: () => {},
+ blockedUsers: [],
+ blockedUsersNeedUpdate: true,
+ updateBlockedUsers: () => {},
});
/**
@@ -97,6 +104,12 @@ const AuthProvider: React.FC = ({children}) => {
const [followers, setFollowers] = useState<Array<ProfilePreviewType>>([]);
const [following, setFollowing] = useState<Array<ProfilePreviewType>>([]);
const [followersNeedUpdate, setFollowersNeedUpdate] = useState<boolean>(true);
+ const [blockedUsers, setBlockedUsers] = useState<Array<ProfilePreviewType>>(
+ [],
+ );
+ const [blockedUsersNeedUpdate, setBlockedUsersNeedUpdate] = useState<boolean>(
+ true,
+ );
const {userId} = user;
useEffect(() => {
@@ -193,6 +206,30 @@ const AuthProvider: React.FC = ({children}) => {
}
}, [socialAccounts, socialsNeedUpdate, userId]);
+ useEffect(() => {
+ const loadNewBlockedUsers = async () => {
+ try {
+ const token = await AsyncStorage.getItem('token');
+ if (!token) {
+ setUser(NO_USER);
+ return;
+ }
+ loadBlockedUsers(userId, token, setBlockedUsers);
+ setBlockedUsersNeedUpdate(false);
+ } catch (error) {
+ console.log(error);
+ }
+ };
+ if (blockedUsersNeedUpdate && userId) {
+ loadNewBlockedUsers();
+ }
+ }, [
+ setBlockedUsersNeedUpdate,
+ blockedUsersNeedUpdate,
+ userId,
+ setBlockedUsers,
+ ]);
+
return (
<AuthContext.Provider
value={{
@@ -206,6 +243,8 @@ const AuthProvider: React.FC = ({children}) => {
followers,
following,
followersNeedUpdate,
+ blockedUsers,
+ blockedUsersNeedUpdate,
login: (id, username) => {
setUser({...user, userId: id, username});
},
@@ -232,6 +271,9 @@ const AuthProvider: React.FC = ({children}) => {
updateFollowers: (value) => {
setFollowersNeedUpdate(value);
},
+ updateBlockedUsers: (value) => {
+ setBlockedUsersNeedUpdate(value);
+ },
}}>
{children}
</AuthContext.Provider>
diff --git a/src/services/BlockUserService.ts b/src/services/BlockUserService.ts
new file mode 100644
index 00000000..56243729
--- /dev/null
+++ b/src/services/BlockUserService.ts
@@ -0,0 +1,98 @@
+//Abstracted common block user api calls out here
+
+import {Alert} from 'react-native';
+import {BLOCK_USER_ENDPOINT} from '../constants';
+
+export const loadBlockedUsers = async (
+ userId: string,
+ token: string,
+ callback: Function,
+) => {
+ try {
+ const response = await fetch(BLOCK_USER_ENDPOINT + `?user_id=${userId}`, {
+ method: 'GET',
+ headers: {
+ Authorization: 'Token ' + token,
+ },
+ });
+ if (response.status === 200) {
+ const body = await response.json();
+ callback(body);
+ } else {
+ throw new Error(await response.json());
+ }
+ } catch (error) {
+ console.log(error);
+ }
+};
+
+export const blockOrUnblockUser = async (
+ blocker: string,
+ blocked: string,
+ token: string,
+ isBlocked: boolean,
+) => {
+ try {
+ const endpoint = BLOCK_USER_ENDPOINT + (isBlocked ? `${blocker}/` : '');
+
+ const response = await fetch(endpoint, {
+ method: isBlocked ? 'DELETE' : 'POST',
+ headers: {
+ Authorization: 'Token ' + token,
+ },
+ body: JSON.stringify({
+ blocked,
+ }),
+ });
+ if (Math.floor(response.status / 100) === 2) {
+ return true;
+ } else {
+ console.log(await response.json());
+ Alert.alert(
+ 'Something went wrong! 😭',
+ "Would you believe me if I told you that I don't know what happened?",
+ );
+ return false;
+ }
+ } catch (error) {
+ console.log(error);
+ Alert.alert(
+ 'Something went wrong! 😭',
+ "Would you believe me if I told you that I don't know what happened?",
+ );
+ return false;
+ }
+};
+
+export const isUserBlocked = async (
+ blocker: string,
+ blocked: string,
+ token: string,
+) => {
+ try {
+ const ext = `${blocked}/?blocker=${blocker}`;
+
+ const response = await fetch(BLOCK_USER_ENDPOINT + ext, {
+ method: 'GET',
+ headers: {
+ Authorization: 'Token ' + token,
+ },
+ });
+
+ if (Math.floor(response.status / 100) === 2) {
+ const data = await response.json();
+ return data['is_blocked'];
+ } else {
+ console.log(await response.json());
+ Alert.alert(
+ 'Something went wrong! 😭',
+ "Would you believe me if I told you that I don't know what happened?",
+ );
+ }
+ } catch (error) {
+ Alert.alert(
+ 'Something went wrong! 😭',
+ "Would you believe me if I told you that I don't know what happened?",
+ );
+ }
+};
diff --git a/src/services/index.ts b/src/services/index.ts
index 6d0f4314..3d74290c 100644
--- a/src/services/index.ts
+++ b/src/services/index.ts
@@ -2,3 +2,4 @@ export * from './UserProfileService';
export * from './SocialLinkingService';
export * from './MomentServices';
export * from './UserFollowServices';
+export * from './BlockUserService';
diff --git a/src/utils/common.ts b/src/utils/common.ts
new file mode 100644
index 00000000..9e74ca33
--- /dev/null
+++ b/src/utils/common.ts
@@ -0,0 +1,15 @@
+import {TOGGLE_BUTTON_TYPE} from '../constants';
+
+export const getToggleButtonText: (
+ button_type: string,
+ state: boolean,
+) => string | null = (button_type, state) => {
+ switch (button_type) {
+ case TOGGLE_BUTTON_TYPE.FOLLOW_UNFOLLOW:
+ return state ? 'Unfollow' : 'Follow';
+ case TOGGLE_BUTTON_TYPE.BLOCK_UNBLOCK:
+ return state ? 'Unblock' : 'Block';
+ default:
+ return null;
+ }
+};
diff --git a/src/utils/index.ts b/src/utils/index.ts
index a7e45979..95449c54 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -1,3 +1,4 @@
export * from './screenDimensions';
export * from './statusBarHeight';
export * from './moments';
+export * from './common';