Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frontend/api/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,5 @@ export async function updateProfilePhoto(photo: string): Promise<void> {
throw new Error(errorData.error || 'failed to change profile pic');
}
}

export function searchUserData(userlist: string[]): User[] {}
1 change: 0 additions & 1 deletion frontend/app/(app)/(tabs)/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,6 @@ const Home = () => {
}}
*/
>

<View className="flex flex-row">
<View className="w-[48%] mr-[2%]">
{split(nearbyPosts?.pages.flatMap((page) => page) || [])[0].map(
Expand Down
15 changes: 14 additions & 1 deletion frontend/app/(app)/user/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';

import { Stack } from 'expo-router';
import { Stack, router } from 'expo-router';
import Arrow from '../../../components/arrow';

const Layout = () => {
return (
Expand All @@ -11,6 +12,18 @@ const Layout = () => {
headerShown: false,
}}
/>
<Stack.Screen
name="follower_following"
options={{
headerShown: true,
headerTitle: '',
headerTransparent: true,
gestureEnabled: false,
headerLeft: () => (
<Arrow direction="left" onPress={() => router.back()} />
),
}}
/>
</Stack>
);
};
Expand Down
21 changes: 19 additions & 2 deletions frontend/app/(app)/user/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { PROFILE_PHOTO } from '../../../../consts/profile';
import { formatNumber } from '../../../../utils/profile';
import FollowButton from './follow-button';
import useFollow from '../../../../hooks/following';
import { router } from 'expo-router';

const Header = ({ id }: { id: string }) => {
const { mongoDBId } = useAuthStore();
Expand Down Expand Up @@ -37,15 +38,31 @@ const Header = ({ id }: { id: string }) => {
image={data?.user.profilePicture || PROFILE_PHOTO}
/>
<View className="flex flex-row ml-[5%] justify-around w-3/4">
<TouchableOpacity className="flex-col justify-center items-center flex-1">
<TouchableOpacity
className="flex-col justify-center items-center flex-1"
onPress={() => {
router.push({
pathname: '/user/follower_following',
params: { id: id, option: 'followers' },
});
}}
>
<Text className="font-bold text-darkblue text-md sm:text-lg md:text-xl">
{formatNumber(data?.user.followers.length)}
</Text>
<Text className="text-darkblue text-xs sm:text-base">
Followers
</Text>
</TouchableOpacity>
<TouchableOpacity className="flex-col justify-center items-center flex-1">
<TouchableOpacity
className="flex-col justify-center items-center flex-1"
onPress={() => {
router.push({
pathname: '/user/follower_following',
params: { id: id, option: 'following' },
});
}}
>
<Text className="font-bold text-darkblue text-md sm:text-lg md:text-xl">
{formatNumber(data?.user.following.length)}
</Text>
Expand Down
115 changes: 115 additions & 0 deletions frontend/app/(app)/user/follower_following.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import React, { useEffect, useState } from 'react';
import { useLocalSearchParams } from 'expo-router';
import { useUserById } from '../../../hooks/user';
import { LinearGradient } from 'expo-linear-gradient';
import {
View,
Text,
Dimensions,
TouchableOpacity,
TextInput,
Image,
FlatList,
} from 'react-native';
import Animated, {
useAnimatedStyle,
withTiming,
Easing,
} from 'react-native-reanimated';
import { UserType } from '../../../types/userProfile';
import ProfileTags from '../../../components/profile_tag';

const FollowerFollowing = () => {
const { width } = Dimensions.get('window');
const { id, option } = useLocalSearchParams<{ id: string; option: string }>();
const { data, isError, isLoading } = useUserById(id);
const [category, setCategory] =
option == 'followers' ? useState('Followers') : useState('Following');
const [userData, setUserData] = useState<string[]>([]);

useEffect(() => {
const findData = () => {
if (category == 'Followers') {
setUserData(data?.user.followers);
} else {
setUserData(data?.user.following);
}
};
findData();
}, [category]);

const renderUser = ({ item }: { item: any }) => (
<View className="mb-2 w-[80vw]">
<ProfileTags id={item} />
</View>
);

const indicatorStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateX: withTiming(category === 'Followers' ? 0 : width / 2, {
duration: 300,
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
}),
},
],
};
}, [category]);

return (
<LinearGradient
colors={['#549ac7', '#ffffff', '#ffffff', '#ffffff']}
style={{ flex: 1 }}
>
<View className="flex flex-col items-center mt-[10vh] ">
<Text className="font-bold text-darkblue text-xl">
{category === 'Followers' ? 'Followers' : 'Following'}
</Text>
<Text className="font-bold text-darkblue text-md mt-[2vh]">
@{data?.user.username}
</Text>

<View className="flex w-100 flex-row pb-[5%] pt-[2vh]">
<Animated.View
className="bg-[#3788BE] absolute h-[1px] w-2/5 mx-5"
style={[
{
bottom: '30%',
},
indicatorStyle,
]}
/>
<TouchableOpacity
className="py-2 w-[50%] justify-center items-center"
onPress={() => setCategory('Followers')}
>
<Text
className={`font-bold text-base text-sm ${
category === 'Followers' ? 'text-[#3788BE]' : 'text-gray-400'
}`}
>
{data.user.followers.length} Followers
</Text>
</TouchableOpacity>
<TouchableOpacity
className="py-[3%] w-[50%] justify-center items-center"
onPress={() => setCategory('Following')}
>
<Text
className={`font-bold text-base text-sm ${
category === 'Following' ? 'text-[#3788BE]' : 'text-gray-400'
}`}
>
{data.user.following.length} Following
</Text>
</TouchableOpacity>
</View>

<FlatList data={userData} renderItem={renderUser}></FlatList>
</View>
</LinearGradient>
);
};

export default FollowerFollowing;
4 changes: 2 additions & 2 deletions frontend/assets/search.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions frontend/components/profile_tag.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { View, Text, TouchableOpacity } from 'react-native';
import Profile from './profile';
import { router } from 'expo-router';
import { PROFILE_PHOTO } from '../consts/profile';
import FollowButton from '../app/(app)/user/components/follow-button';
import useFollow from '../hooks/following';
import { UserType } from '../types/userProfile';
import { useUserById } from '../hooks/user';
import { useAuthStore } from '../auth/authStore';

interface ProfileTagProps {
id: string;
}
const ProfileTags: React.FC<ProfileTagProps> = ({ id }) => {
const { data, isLoading, isError } = useUserById(id);
const { handleFollowToggle, isFollowing, isPending } = useFollow(
data?.user._id,
);
const uri = data?.user.profilePicture
? data?.user.profilePicture
: PROFILE_PHOTO;
const { mongoDBId } = useAuthStore();
const isViewingOwnProfile = mongoDBId === id;
return (
<TouchableOpacity
onPress={() => {
router.push(`/user/${data?.user._id}`);
}}
className="flex w-full flex-row items-center h-[8vh] bg-white shadow-sm rounded-lg pl-2"
>
<Profile image={uri} size="md" />
<View className="flex flex-col pl-2">
<Text>{data?.user.username}</Text>
</View>

{isViewingOwnProfile ? (
<></>
) : (
<View className="absolute left-[58vw] w-[60vw]">
<FollowButton
onPress={handleFollowToggle}
isPending={isPending!}
isFollowing={isFollowing}
/>
</View>
)}
</TouchableOpacity>
);
};

export default ProfileTags;
6 changes: 6 additions & 0 deletions frontend/types/userProfile.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
export type UpdateProfileFields = {
profilePicture: string; //String to the s3
};

export type UserType = {
_id: string;
profilePicture: string;
username: string;
};
Loading