feat(ui): unify design system to Twitter/X style across screens and improve message handling
Some checks failed
Frontend CI / build-and-push-web (push) Failing after 3m9s
Frontend CI / ota-android (push) Successful in 10m29s
Frontend CI / build-android-apk (push) Successful in 43m4s

Redesign multiple screens and components from card-based responsive design to Twitter/X flat design:
- UserProfileHeader: adopt Twitter/X style with larger icons, simplified buttons, and block functionality
- AppBackButton: update to chevron-left icon with larger size, remove background styling
- Update SettingsScreen, EditProfileScreen, and UserProfileScreen with flat layout approach
- ConversationListRow: convert from bordered cards to flat list rows with updated typography

Improve message system with WebSocket notification handling:
- Add real-time system unread count updates via WebSocket
- Track lastSystemMessageAt for accurate system message timestamps
- Add notification deduplication and read-status filtering
- Combine system and conversation unread counts in background sync
- Clear system notifications from notification center on mark as read
This commit is contained in:
lafay
2026-04-25 00:39:40 +08:00
parent e0d28535f4
commit ad19bc2af7
26 changed files with 950 additions and 1121 deletions

View File

@@ -1,7 +1,7 @@
/**
* 统一的用户主页组件
* 威友 - 支持两种模式self当前用户和 other其他用户
* 采用现代卡片式设计,优化视觉层次和交互体验
* 统一的用户主页组件 - Twitter/X 风格
* 支持两种模式self当前用户和 other其他用户
* 采用 Twitter/X 扁平化设计,全宽布局,无边框卡片
* 支持桌面端双栏布局
*/
@@ -29,7 +29,7 @@ export const UserProfileScreen: React.FC<UserProfileScreenProps> = ({ mode, user
const colors = useAppColors();
const sharedStyles = useMemo(() => createSharedProfileStyles(colors), [colors]);
const { isDesktop, isTablet } = useResponsive();
const {
user,
posts,
@@ -45,14 +45,15 @@ export const UserProfileScreen: React.FC<UserProfileScreenProps> = ({ mode, user
handleFollowingPress,
handleFollowersPress,
handleMessage,
handleMore,
handleBlock,
isBlocked,
handleSettings,
handleEditProfile,
isCurrentUser,
currentUser,
} = useUserProfile({ mode, userId, isDesktop, isTablet });
// 渲染帖子列表
// 渲染帖子列表 - Twitter 风格无边框
const renderPostList = useCallback((postList: Post[], emptyTitle: string, emptyDesc: string) => {
if (postList.length === 0) {
return (
@@ -64,7 +65,7 @@ export const UserProfileScreen: React.FC<UserProfileScreenProps> = ({ mode, user
/>
);
}
return (
<View style={sharedStyles.postsContainer}>
{postList.map((post, index) => {
@@ -85,11 +86,11 @@ export const UserProfileScreen: React.FC<UserProfileScreenProps> = ({ mode, user
</View>
);
}, [currentUser?.id, handlePostAction, activeTab]);
// 渲染内容
const renderContent = useCallback(() => {
if (loading) return <Loading />;
if (activeTab === 0) {
return renderPostList(
posts,
@@ -97,7 +98,7 @@ export const UserProfileScreen: React.FC<UserProfileScreenProps> = ({ mode, user
mode === 'self' ? '分享你的想法,发布第一条帖子吧' : ''
);
}
if (activeTab === 1) {
return renderPostList(
favorites,
@@ -105,29 +106,30 @@ export const UserProfileScreen: React.FC<UserProfileScreenProps> = ({ mode, user
mode === 'self' ? '发现喜欢的内容,点击收藏按钮保存' : ''
);
}
return null;
}, [loading, activeTab, posts, favorites, mode, renderPostList]);
// 渲染用户信息头部
const renderUserHeader = useMemo(() => {
if (!user) return null;
return (
<UserProfileHeader
user={user}
isCurrentUser={isCurrentUser}
isBlocked={isBlocked}
onFollow={handleFollow}
onSettings={handleSettings}
onEditProfile={handleEditProfile}
onMessage={handleMessage}
onMore={handleMore}
onBlock={handleBlock}
onFollowingPress={handleFollowingPress}
onFollowersPress={handleFollowersPress}
/>
);
}, [user, isCurrentUser, handleFollow, handleSettings, handleEditProfile, handleMessage, handleMore, handleFollowingPress, handleFollowersPress]);
}, [user, isCurrentUser, isBlocked, handleFollow, handleSettings, handleEditProfile, handleMessage, handleBlock, handleFollowingPress, handleFollowersPress]);
// 渲染 TabBar 和内容
const renderTabBarAndContent = useMemo(() => (
<>
@@ -145,7 +147,7 @@ export const UserProfileScreen: React.FC<UserProfileScreenProps> = ({ mode, user
</View>
</>
), [activeTab, renderContent]);
// 未登录/用户不存在状态
if (mode === 'self' && !currentUser) {
return (
@@ -158,7 +160,7 @@ export const UserProfileScreen: React.FC<UserProfileScreenProps> = ({ mode, user
</SafeAreaView>
);
}
if (mode === 'other' && !user && !loading) {
return (
<SafeAreaView style={sharedStyles.container} edges={['bottom']}>
@@ -170,14 +172,14 @@ export const UserProfileScreen: React.FC<UserProfileScreenProps> = ({ mode, user
</SafeAreaView>
);
}
const safeAreaEdges = hasHeader ? ['bottom'] : (mode === 'self' ? ['top', 'bottom'] : ['bottom']);
// 桌面端使用双栏布局
if (isDesktop || isTablet) {
return (
<SafeAreaView style={sharedStyles.container} edges={safeAreaEdges as any}>
<ResponsiveContainer maxWidth={1400}>
<ResponsiveContainer maxWidth={1200}>
<View style={sharedStyles.desktopContainer}>
{/* 左侧:用户信息 */}
<View style={sharedStyles.desktopSidebar}>
@@ -196,7 +198,7 @@ export const UserProfileScreen: React.FC<UserProfileScreenProps> = ({ mode, user
{renderUserHeader}
</ScrollView>
</View>
{/* 右侧:帖子列表 */}
<View style={sharedStyles.desktopContent}>
<ScrollView
@@ -211,7 +213,7 @@ export const UserProfileScreen: React.FC<UserProfileScreenProps> = ({ mode, user
</SafeAreaView>
);
}
// 移动端使用单栏布局
return (
<SafeAreaView style={sharedStyles.container} edges={safeAreaEdges as any}>
@@ -234,4 +236,4 @@ export const UserProfileScreen: React.FC<UserProfileScreenProps> = ({ mode, user
);
};
export default UserProfileScreen;
export default UserProfileScreen;