feat(ui): redesign system message item with card-style layout and add title validation
All checks were successful
Frontend CI / build-and-push-web (push) Successful in 2m39s
Frontend CI / ota-android (push) Successful in 12m4s
Frontend CI / build-android-apk (push) Successful in 44m0s

- Refactor SystemMessageItem with modern card design, gradient colors, and improved visual hierarchy
- Add unread indicator, status badges, and enhanced avatar badges for message types
- Update CreatePostScreen to require title input and improve placeholder text
- Add Android back button handling for notification screens in embedded mode
- Replace manual shadow styles with theme shadow utilities across message screens
This commit is contained in:
lafay
2026-03-21 12:08:45 +08:00
parent a8c78f0c3f
commit 37ddfa8ed6
4 changed files with 326 additions and 173 deletions

View File

@@ -337,6 +337,11 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
// 防止重复点击
if (posting) return;
if (!title.trim()) {
Alert.alert('错误', '请输入帖子标题');
return;
}
if (!content.trim()) {
Alert.alert('错误', '请输入帖子内容');
return;
@@ -365,7 +370,7 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
// 创建投票帖子
await voteService.createVotePost({
title: title.trim() || '无标题',
title: title.trim(),
content: content.trim(),
images: imageUrls,
vote_options: validOptions,
@@ -383,7 +388,7 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
} else {
if (isEditMode && editPostID) {
const updated = await postService.updatePost(editPostID, {
title: title.trim() || '无标题',
title: title.trim(),
content: content.trim(),
images: imageUrls,
});
@@ -403,7 +408,7 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
} else {
// 创建普通帖子
await postService.createPost({
title: title.trim() || '无标题',
title: title.trim(),
content: content.trim(),
images: imageUrls,
});
@@ -562,7 +567,7 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
]}
value={title}
onChangeText={setTitle}
placeholder="添加标题(可选"
placeholder="请输入标题(必填"
placeholderTextColor={colors.text.hint}
maxLength={MAX_TITLE_LENGTH}
/>

View File

@@ -22,6 +22,7 @@ import {
Animated,
TextInput,
Keyboard,
BackHandler,
} from 'react-native';
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
import { useNavigation, useIsFocused } from '@react-navigation/native';
@@ -283,6 +284,19 @@ export const MessageListScreen: React.FC = () => {
}
}, [isFocused]);
// 处理 Android 物理返回键 - 当显示通知页面时,返回键关闭通知页面
useEffect(() => {
const backHandler = BackHandler.addEventListener('hardwareBackPress', () => {
if (showNotifications) {
setShowNotifications(false);
return true; // 阻止默认行为
}
return false;
});
return () => backHandler.remove();
}, [showNotifications]);
// 下拉刷新
const onRefresh = useCallback(async () => {
setRefreshing(true);

View File

@@ -14,13 +14,14 @@ import {
RefreshControl,
ActivityIndicator,
Dimensions,
BackHandler,
} from 'react-native';
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
import { useIsFocused } from '@react-navigation/native';
import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { colors, spacing, borderRadius } from '../../theme';
import { colors, spacing, borderRadius, shadows } from '../../theme';
import { SystemMessageResponse } from '../../types/dto';
import { messageService } from '../../services/messageService';
import { commentService } from '../../services/commentService';
@@ -182,6 +183,21 @@ export const NotificationsScreen: React.FC<{ onBack?: () => void }> = ({ onBack
}
}, [isFocused, onBack]);
// 处理 Android 物理返回键 - 当 onBack 存在时(内嵌模式),拦截返回键
useEffect(() => {
if (!onBack) return;
const backHandler = BackHandler.addEventListener('hardwareBackPress', () => {
if (isFocused) {
onBack();
return true; // 阻止默认行为
}
return false;
});
return () => backHandler.remove();
}, [isFocused, onBack]);
// 筛选消息
const filteredMessages = activeType === 'all'
? displayMessages
@@ -558,12 +574,7 @@ const styles = StyleSheet.create({
paddingHorizontal: spacing.lg,
paddingVertical: spacing.md,
backgroundColor: colors.background.paper,
// 移除底部边框,使用更柔和的分隔方式
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 2,
elevation: 2,
...shadows.sm,
},
headerWide: {
paddingHorizontal: spacing.xl,
@@ -613,7 +624,6 @@ const styles = StyleSheet.create({
paddingHorizontal: spacing.md,
paddingVertical: spacing.sm,
backgroundColor: colors.background.paper,
// 移除底部边框,让界面更简洁
},
filterContainerWideWeb: {
paddingHorizontal: spacing.xl,
@@ -629,6 +639,8 @@ const styles = StyleSheet.create({
marginRight: spacing.xs,
borderRadius: borderRadius.lg,
backgroundColor: colors.background.default,
borderWidth: 1,
borderColor: 'transparent',
},
filterTagWide: {
paddingHorizontal: spacing.lg,
@@ -637,6 +649,7 @@ const styles = StyleSheet.create({
},
filterTagActive: {
backgroundColor: colors.primary.main,
borderColor: colors.primary.main,
},
filterTagTextActive: {
fontWeight: '600',
@@ -655,7 +668,8 @@ const styles = StyleSheet.create({
},
listContent: {
flexGrow: 1,
paddingTop: spacing.sm,
paddingTop: spacing.md,
paddingBottom: spacing.lg,
},
listContentWide: {
paddingHorizontal: spacing.xl,
@@ -679,6 +693,7 @@ const styles = StyleSheet.create({
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingTop: spacing['3xl'],
},
emptyContainerWeb: {
minHeight: 500,