491 lines
16 KiB
TypeScript
491 lines
16 KiB
TypeScript
|
|
/**
|
|||
|
|
* 个人主页 ProfileScreen - 美化版(响应式适配)
|
|||
|
|
* 胡萝卜BBS - 当前用户个人主页
|
|||
|
|
* 采用现代卡片式设计,优化视觉层次和交互体验
|
|||
|
|
* 支持桌面端双栏布局
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
import React, { useState, useCallback, useMemo } from 'react';
|
|||
|
|
import {
|
|||
|
|
View,
|
|||
|
|
StyleSheet,
|
|||
|
|
RefreshControl,
|
|||
|
|
Animated,
|
|||
|
|
ScrollView,
|
|||
|
|
} from 'react-native';
|
|||
|
|
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
|
|||
|
|
import { useNavigation } from '@react-navigation/native';
|
|||
|
|
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
|||
|
|
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
|
|||
|
|
import { colors, spacing } from '../../theme';
|
|||
|
|
import { Post } from '../../types';
|
|||
|
|
import { useAuthStore, useUserStore } from '../../stores';
|
|||
|
|
import { postService } from '../../services';
|
|||
|
|
import { UserProfileHeader, PostCard, TabBar } from '../../components/business';
|
|||
|
|
import { Loading, EmptyState, Text } from '../../components/common';
|
|||
|
|
import { ResponsiveContainer } from '../../components/common';
|
|||
|
|
import { useResponsive } from '../../hooks';
|
|||
|
|
import { ProfileStackParamList, HomeStackParamList, RootStackParamList } from '../../navigation/types';
|
|||
|
|
|
|||
|
|
type NavigationProp = NativeStackNavigationProp<ProfileStackParamList, 'Profile'>;
|
|||
|
|
type HomeNavigationProp = NativeStackNavigationProp<HomeStackParamList>;
|
|||
|
|
type RootNavigationProp = NativeStackNavigationProp<RootStackParamList>;
|
|||
|
|
|
|||
|
|
const TABS = ['帖子', '收藏'];
|
|||
|
|
const TAB_ICONS = ['file-document-outline', 'bookmark-outline'];
|
|||
|
|
|
|||
|
|
export const ProfileScreen: React.FC = () => {
|
|||
|
|
const navigation = useNavigation<NavigationProp>();
|
|||
|
|
const insets = useSafeAreaInsets();
|
|||
|
|
const tabBarHeight = useBottomTabBarHeight();
|
|||
|
|
const homeNavigation = useNavigation<HomeNavigationProp>();
|
|||
|
|
// 使用 any 类型来访问根导航
|
|||
|
|
const rootNavigation = useNavigation<RootNavigationProp>();
|
|||
|
|
const { currentUser, updateUser, fetchCurrentUser } = useAuthStore();
|
|||
|
|
const { followUser, unfollowUser, likePost, unlikePost, favoritePost, unfavoritePost, posts: storePosts } = useUserStore();
|
|||
|
|
|
|||
|
|
// 响应式布局
|
|||
|
|
const { isDesktop, isTablet, width } = useResponsive();
|
|||
|
|
|
|||
|
|
// 页面滚动底部安全间距,避免内容被底部 TabBar 遮挡
|
|||
|
|
const scrollBottomInset = useMemo(() => {
|
|||
|
|
if (isDesktop) return spacing.lg;
|
|||
|
|
return tabBarHeight + insets.bottom + spacing.md;
|
|||
|
|
}, [isDesktop, tabBarHeight, insets.bottom]);
|
|||
|
|
|
|||
|
|
const [activeTab, setActiveTab] = useState(0);
|
|||
|
|
const [posts, setPosts] = useState<Post[]>([]);
|
|||
|
|
const [favorites, setFavorites] = useState<Post[]>([]);
|
|||
|
|
const [loading, setLoading] = useState(true);
|
|||
|
|
const [refreshing, setRefreshing] = useState(false);
|
|||
|
|
const scrollY = new Animated.Value(0);
|
|||
|
|
|
|||
|
|
// 跳转到关注列表
|
|||
|
|
const handleFollowingPress = useCallback(() => {
|
|||
|
|
if (currentUser) {
|
|||
|
|
(rootNavigation as any).navigate('FollowList', { userId: currentUser.id, type: 'following' });
|
|||
|
|
}
|
|||
|
|
}, [currentUser, rootNavigation]);
|
|||
|
|
|
|||
|
|
// 跳转到粉丝列表
|
|||
|
|
const handleFollowersPress = useCallback(() => {
|
|||
|
|
if (currentUser) {
|
|||
|
|
(rootNavigation as any).navigate('FollowList', { userId: currentUser.id, type: 'followers' });
|
|||
|
|
}
|
|||
|
|
}, [currentUser, rootNavigation]);
|
|||
|
|
|
|||
|
|
// 加载用户帖子
|
|||
|
|
const loadUserPosts = useCallback(async () => {
|
|||
|
|
if (currentUser) {
|
|||
|
|
try {
|
|||
|
|
const response = await postService.getUserPosts(currentUser.id);
|
|||
|
|
setPosts(response.list);
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('获取用户帖子失败:', error);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
setLoading(false);
|
|||
|
|
}, [currentUser]);
|
|||
|
|
|
|||
|
|
// 加载用户收藏
|
|||
|
|
const loadUserFavorites = useCallback(async () => {
|
|||
|
|
if (currentUser) {
|
|||
|
|
try {
|
|||
|
|
console.log('[ProfileScreen] load, userUserFavorites calledId:', currentUser.id);
|
|||
|
|
const response = await postService.getUserFavorites(currentUser.id);
|
|||
|
|
console.log('[ProfileScreen] getUserFavorites response:', response);
|
|||
|
|
setFavorites(response.list);
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('获取用户收藏失败:', error);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
setLoading(false);
|
|||
|
|
}, [currentUser]);
|
|||
|
|
|
|||
|
|
// 监听 tab 切换,只在数据为空时加载对应数据
|
|||
|
|
React.useEffect(() => {
|
|||
|
|
if (activeTab === 0 && posts.length === 0) {
|
|||
|
|
loadUserPosts();
|
|||
|
|
} else if (activeTab === 1 && favorites.length === 0) {
|
|||
|
|
loadUserFavorites();
|
|||
|
|
}
|
|||
|
|
}, [activeTab, loadUserPosts, loadUserFavorites, posts.length, favorites.length]);
|
|||
|
|
|
|||
|
|
// 初始加载
|
|||
|
|
React.useEffect(() => {
|
|||
|
|
loadUserPosts();
|
|||
|
|
}, [loadUserPosts]);
|
|||
|
|
|
|||
|
|
// 同步 store 中的帖子状态到本地(用于点赞、收藏等状态更新)
|
|||
|
|
React.useEffect(() => {
|
|||
|
|
// 同步帖子列表状态
|
|||
|
|
if (posts.length > 0) {
|
|||
|
|
let hasChanges = false;
|
|||
|
|
const updatedPosts = posts.map(localPost => {
|
|||
|
|
const storePost = storePosts.find(sp => sp.id === localPost.id);
|
|||
|
|
if (storePost && (
|
|||
|
|
storePost.is_liked !== localPost.is_liked ||
|
|||
|
|
storePost.is_favorited !== localPost.is_favorited ||
|
|||
|
|
storePost.likes_count !== localPost.likes_count ||
|
|||
|
|
storePost.favorites_count !== localPost.favorites_count
|
|||
|
|
)) {
|
|||
|
|
hasChanges = true;
|
|||
|
|
return {
|
|||
|
|
...localPost,
|
|||
|
|
is_liked: storePost.is_liked,
|
|||
|
|
is_favorited: storePost.is_favorited,
|
|||
|
|
likes_count: storePost.likes_count,
|
|||
|
|
favorites_count: storePost.favorites_count,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
return localPost;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (hasChanges) {
|
|||
|
|
setPosts(updatedPosts);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 同步收藏列表状态
|
|||
|
|
if (favorites.length > 0) {
|
|||
|
|
let hasChanges = false;
|
|||
|
|
const updatedFavorites = favorites.map(localPost => {
|
|||
|
|
const storePost = storePosts.find(sp => sp.id === localPost.id);
|
|||
|
|
if (storePost && (
|
|||
|
|
storePost.is_liked !== localPost.is_liked ||
|
|||
|
|
storePost.is_favorited !== localPost.is_favorited ||
|
|||
|
|
storePost.likes_count !== localPost.likes_count ||
|
|||
|
|
storePost.favorites_count !== localPost.favorites_count
|
|||
|
|
)) {
|
|||
|
|
hasChanges = true;
|
|||
|
|
return {
|
|||
|
|
...localPost,
|
|||
|
|
is_liked: storePost.is_liked,
|
|||
|
|
is_favorited: storePost.is_favorited,
|
|||
|
|
likes_count: storePost.likes_count,
|
|||
|
|
favorites_count: storePost.favorites_count,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
return localPost;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (hasChanges) {
|
|||
|
|
setFavorites(updatedFavorites);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}, [storePosts]);
|
|||
|
|
|
|||
|
|
// 下拉刷新
|
|||
|
|
const onRefresh = useCallback(async () => {
|
|||
|
|
setRefreshing(true);
|
|||
|
|
try {
|
|||
|
|
// 刷新用户信息
|
|||
|
|
await fetchCurrentUser();
|
|||
|
|
// 刷新帖子列表
|
|||
|
|
await loadUserPosts();
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('刷新失败:', error);
|
|||
|
|
} finally {
|
|||
|
|
setRefreshing(false);
|
|||
|
|
}
|
|||
|
|
}, [fetchCurrentUser, loadUserPosts]);
|
|||
|
|
|
|||
|
|
// 跳转到设置页
|
|||
|
|
const handleSettings = useCallback(() => {
|
|||
|
|
navigation.navigate('Settings');
|
|||
|
|
}, [navigation]);
|
|||
|
|
|
|||
|
|
// 跳转到编辑资料页
|
|||
|
|
const handleEditProfile = useCallback(() => {
|
|||
|
|
navigation.navigate('EditProfile');
|
|||
|
|
}, [navigation]);
|
|||
|
|
|
|||
|
|
// 关注/取消关注
|
|||
|
|
const handleFollow = useCallback(() => {
|
|||
|
|
if (!currentUser) return;
|
|||
|
|
if (currentUser.is_following) {
|
|||
|
|
unfollowUser(currentUser.id);
|
|||
|
|
} else {
|
|||
|
|
followUser(currentUser.id);
|
|||
|
|
}
|
|||
|
|
}, [currentUser, unfollowUser, followUser]);
|
|||
|
|
|
|||
|
|
// 跳转到帖子详情
|
|||
|
|
const handlePostPress = useCallback((postId: string, scrollToComments: boolean = false) => {
|
|||
|
|
homeNavigation.navigate('PostDetail', { postId, scrollToComments });
|
|||
|
|
}, [homeNavigation]);
|
|||
|
|
|
|||
|
|
// 跳转到用户主页(当前用户)
|
|||
|
|
const handleUserPress = useCallback((userId: string) => {
|
|||
|
|
// 个人主页点击自己的头像不跳转
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
// 删除帖子
|
|||
|
|
const handleDeletePost = useCallback(async (postId: string) => {
|
|||
|
|
try {
|
|||
|
|
const success = await postService.deletePost(postId);
|
|||
|
|
if (success) {
|
|||
|
|
// 从帖子列表中移除
|
|||
|
|
setPosts(prev => prev.filter(p => p.id !== postId));
|
|||
|
|
// 也从收藏列表中移除(如果存在)
|
|||
|
|
setFavorites(prev => prev.filter(p => p.id !== postId));
|
|||
|
|
} else {
|
|||
|
|
console.error('删除帖子失败');
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('删除帖子失败:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
// 渲染内容
|
|||
|
|
const renderContent = useCallback(() => {
|
|||
|
|
if (loading) return <Loading />;
|
|||
|
|
|
|||
|
|
if (activeTab === 0) {
|
|||
|
|
// 帖子
|
|||
|
|
if (posts.length === 0) {
|
|||
|
|
return (
|
|||
|
|
<EmptyState
|
|||
|
|
title="还没有帖子"
|
|||
|
|
description="分享你的想法,发布第一条帖子吧"
|
|||
|
|
icon="file-document-edit-outline"
|
|||
|
|
variant="modern"
|
|||
|
|
/>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<View style={styles.postsContainer}>
|
|||
|
|
{posts.map((post, index) => {
|
|||
|
|
const isPostAuthor = currentUser?.id === post.author?.id;
|
|||
|
|
return (
|
|||
|
|
<View key={post.id} style={[
|
|||
|
|
styles.postWrapper,
|
|||
|
|
index === posts.length - 1 && styles.lastPost,
|
|||
|
|
]}>
|
|||
|
|
<PostCard
|
|||
|
|
post={post}
|
|||
|
|
onPress={() => handlePostPress(post.id)}
|
|||
|
|
onUserPress={() => post.author ? handleUserPress(post.author.id) : () => {}}
|
|||
|
|
onLike={() => post.is_liked ? unlikePost(post.id) : likePost(post.id)}
|
|||
|
|
onComment={() => handlePostPress(post.id, true)}
|
|||
|
|
onBookmark={() => post.is_favorited ? unfavoritePost(post.id) : favoritePost(post.id)}
|
|||
|
|
onShare={() => {}}
|
|||
|
|
onDelete={() => handleDeletePost(post.id)}
|
|||
|
|
isPostAuthor={isPostAuthor}
|
|||
|
|
/>
|
|||
|
|
</View>
|
|||
|
|
);
|
|||
|
|
})}
|
|||
|
|
</View>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (activeTab === 1) {
|
|||
|
|
// 收藏
|
|||
|
|
if (favorites.length === 0) {
|
|||
|
|
return (
|
|||
|
|
<EmptyState
|
|||
|
|
title="还没有收藏"
|
|||
|
|
description="发现喜欢的内容,点击收藏按钮保存"
|
|||
|
|
icon="bookmark-heart-outline"
|
|||
|
|
variant="modern"
|
|||
|
|
/>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<View style={styles.postsContainer}>
|
|||
|
|
{favorites.map((post, index) => {
|
|||
|
|
const isPostAuthor = currentUser?.id === post.author?.id;
|
|||
|
|
return (
|
|||
|
|
<View key={post.id} style={[
|
|||
|
|
styles.postWrapper,
|
|||
|
|
index === favorites.length - 1 && styles.lastPost,
|
|||
|
|
]}>
|
|||
|
|
<PostCard
|
|||
|
|
post={post}
|
|||
|
|
onPress={() => handlePostPress(post.id)}
|
|||
|
|
onUserPress={() => post.author ? handleUserPress(post.author.id) : () => {}}
|
|||
|
|
onLike={() => post.is_liked ? unlikePost(post.id) : likePost(post.id)}
|
|||
|
|
onComment={() => handlePostPress(post.id, true)}
|
|||
|
|
onBookmark={() => post.is_favorited ? unfavoritePost(post.id) : favoritePost(post.id)}
|
|||
|
|
onShare={() => {}}
|
|||
|
|
onDelete={() => handleDeletePost(post.id)}
|
|||
|
|
isPostAuthor={isPostAuthor}
|
|||
|
|
/>
|
|||
|
|
</View>
|
|||
|
|
);
|
|||
|
|
})}
|
|||
|
|
</View>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return null;
|
|||
|
|
}, [loading, activeTab, posts, favorites, currentUser?.id, handlePostPress, handleUserPress, handleDeletePost, unlikePost, likePost, unfavoritePost, favoritePost]);
|
|||
|
|
|
|||
|
|
// 渲染用户信息头部 - 不随 tab 变化,使用 useMemo 缓存
|
|||
|
|
const renderUserHeader = useMemo(() => (
|
|||
|
|
<UserProfileHeader
|
|||
|
|
user={currentUser!}
|
|||
|
|
isCurrentUser={true}
|
|||
|
|
onFollow={handleFollow}
|
|||
|
|
onSettings={handleSettings}
|
|||
|
|
onEditProfile={handleEditProfile}
|
|||
|
|
onFollowingPress={handleFollowingPress}
|
|||
|
|
onFollowersPress={handleFollowersPress}
|
|||
|
|
/>
|
|||
|
|
), [currentUser, handleFollow, handleSettings, handleEditProfile, handleFollowingPress, handleFollowersPress]);
|
|||
|
|
|
|||
|
|
// 渲染 TabBar 和内容
|
|||
|
|
const renderTabBarAndContent = useMemo(() => (
|
|||
|
|
<>
|
|||
|
|
<View style={styles.tabBarContainer}>
|
|||
|
|
<TabBar
|
|||
|
|
tabs={TABS}
|
|||
|
|
activeIndex={activeTab}
|
|||
|
|
onTabChange={setActiveTab}
|
|||
|
|
variant="modern"
|
|||
|
|
icons={TAB_ICONS}
|
|||
|
|
/>
|
|||
|
|
</View>
|
|||
|
|
<View style={styles.contentContainer}>
|
|||
|
|
{renderContent()}
|
|||
|
|
</View>
|
|||
|
|
</>
|
|||
|
|
), [activeTab, renderContent]);
|
|||
|
|
|
|||
|
|
if (!currentUser) {
|
|||
|
|
return (
|
|||
|
|
<SafeAreaView style={styles.container}>
|
|||
|
|
<EmptyState
|
|||
|
|
title="未登录"
|
|||
|
|
description="请先登录"
|
|||
|
|
icon="account-off-outline"
|
|||
|
|
/>
|
|||
|
|
</SafeAreaView>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 桌面端使用双栏布局
|
|||
|
|
if (isDesktop || isTablet) {
|
|||
|
|
return (
|
|||
|
|
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
|
|||
|
|
<ResponsiveContainer maxWidth={1400}>
|
|||
|
|
<View style={styles.desktopContainer}>
|
|||
|
|
{/* 左侧:用户信息 */}
|
|||
|
|
<View style={styles.desktopSidebar}>
|
|||
|
|
<ScrollView
|
|||
|
|
showsVerticalScrollIndicator={false}
|
|||
|
|
contentContainerStyle={{ paddingBottom: scrollBottomInset }}
|
|||
|
|
refreshControl={
|
|||
|
|
<RefreshControl
|
|||
|
|
refreshing={refreshing}
|
|||
|
|
onRefresh={onRefresh}
|
|||
|
|
colors={[colors.primary.main]}
|
|||
|
|
tintColor={colors.primary.main}
|
|||
|
|
/>
|
|||
|
|
}
|
|||
|
|
>
|
|||
|
|
{renderUserHeader}
|
|||
|
|
</ScrollView>
|
|||
|
|
</View>
|
|||
|
|
|
|||
|
|
{/* 右侧:帖子列表 */}
|
|||
|
|
<View style={styles.desktopContent}>
|
|||
|
|
<ScrollView
|
|||
|
|
showsVerticalScrollIndicator={false}
|
|||
|
|
contentContainerStyle={[styles.desktopScrollContent, { paddingBottom: scrollBottomInset }]}
|
|||
|
|
>
|
|||
|
|
{renderTabBarAndContent}
|
|||
|
|
</ScrollView>
|
|||
|
|
</View>
|
|||
|
|
</View>
|
|||
|
|
</ResponsiveContainer>
|
|||
|
|
</SafeAreaView>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 移动端使用单栏布局
|
|||
|
|
return (
|
|||
|
|
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
|
|||
|
|
<ScrollView
|
|||
|
|
showsVerticalScrollIndicator={false}
|
|||
|
|
refreshControl={
|
|||
|
|
<RefreshControl
|
|||
|
|
refreshing={refreshing}
|
|||
|
|
onRefresh={onRefresh}
|
|||
|
|
colors={[colors.primary.main]}
|
|||
|
|
tintColor={colors.primary.main}
|
|||
|
|
/>
|
|||
|
|
}
|
|||
|
|
contentContainerStyle={[styles.scrollContent, { paddingBottom: scrollBottomInset }]}
|
|||
|
|
>
|
|||
|
|
{/* 用户信息头部 - 固定在顶部,不受 tab 切换影响 */}
|
|||
|
|
{renderUserHeader}
|
|||
|
|
|
|||
|
|
{/* TabBar - 分离出来,切换 tab 不会影响上面的用户信息 */}
|
|||
|
|
{renderTabBarAndContent}
|
|||
|
|
</ScrollView>
|
|||
|
|
</SafeAreaView>
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const styles = StyleSheet.create({
|
|||
|
|
container: {
|
|||
|
|
flex: 1,
|
|||
|
|
backgroundColor: colors.background.default,
|
|||
|
|
},
|
|||
|
|
scrollContent: {
|
|||
|
|
flexGrow: 1,
|
|||
|
|
},
|
|||
|
|
// 桌面端双栏布局
|
|||
|
|
desktopContainer: {
|
|||
|
|
flex: 1,
|
|||
|
|
flexDirection: 'row',
|
|||
|
|
gap: spacing.lg,
|
|||
|
|
padding: spacing.lg,
|
|||
|
|
},
|
|||
|
|
desktopSidebar: {
|
|||
|
|
width: 380,
|
|||
|
|
flexShrink: 0,
|
|||
|
|
},
|
|||
|
|
desktopContent: {
|
|||
|
|
flex: 1,
|
|||
|
|
minWidth: 0,
|
|||
|
|
},
|
|||
|
|
desktopScrollContent: {
|
|||
|
|
flexGrow: 1,
|
|||
|
|
},
|
|||
|
|
tabBarContainer: {
|
|||
|
|
marginTop: spacing.xs,
|
|||
|
|
marginBottom: 2,
|
|||
|
|
},
|
|||
|
|
contentContainer: {
|
|||
|
|
flex: 1,
|
|||
|
|
minHeight: 350,
|
|||
|
|
paddingTop: spacing.xs,
|
|||
|
|
},
|
|||
|
|
postsContainer: {
|
|||
|
|
paddingHorizontal: spacing.md,
|
|||
|
|
paddingTop: spacing.sm,
|
|||
|
|
},
|
|||
|
|
postWrapper: {
|
|||
|
|
marginBottom: spacing.md,
|
|||
|
|
backgroundColor: colors.background.paper,
|
|||
|
|
borderRadius: 16,
|
|||
|
|
overflow: 'hidden',
|
|||
|
|
shadowColor: '#000',
|
|||
|
|
shadowOffset: { width: 0, height: 2 },
|
|||
|
|
shadowOpacity: 0.06,
|
|||
|
|
shadowRadius: 8,
|
|||
|
|
elevation: 2,
|
|||
|
|
},
|
|||
|
|
lastPost: {
|
|||
|
|
marginBottom: spacing['2xl'],
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
export default ProfileScreen;
|