feat(Theme): enhance theming and UI consistency across components
All checks were successful
Frontend CI / build-and-push-web (push) Successful in 8m15s
Frontend CI / ota-android (push) Successful in 10m56s
Frontend CI / build-android-apk (push) Successful in 1h3m22s

- Updated app.json to set userInterfaceStyle to automatic for improved theme adaptability.
- Refactored layout components to utilize useAppColors for dynamic theming, ensuring consistent color usage.
- Introduced SystemChrome component to manage system UI background color based on theme.
- Enhanced TabsLayout, ProfileStackLayout, and other components to leverage new theming structure.
- Improved QRCodeScanner, SearchBar, and CommentItem styles to align with the updated theme system.
- Consolidated styles in SystemMessageItem and TabBar for better maintainability and visual coherence.
This commit is contained in:
lafay
2026-03-25 05:16:54 +08:00
parent 90d834695f
commit 4ee3079b9f
86 changed files with 6777 additions and 5890 deletions

View File

@@ -9,7 +9,7 @@ import {
} from 'react-native';
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { colors, spacing, fontSizes, borderRadius } from '../../theme';
import { spacing, fontSizes, borderRadius, useAppColors, type AppColors } from '../../theme';
import { Text, ResponsiveContainer } from '../../components/common';
import { useResponsive } from '../../hooks';
import { authService, resolveAuthApiError } from '../../services/authService';
@@ -17,6 +17,8 @@ import { showPrompt } from '../../services/promptService';
import { useAuthStore } from '../../stores';
export const AccountSecurityScreen: React.FC = () => {
const colors = useAppColors();
const styles = useMemo(() => createAccountSecurityStyles(colors), [colors]);
const { isWideScreen, isMobile } = useResponsive();
const insets = useSafeAreaInsets();
const currentUser = useAuthStore((state) => state.currentUser);
@@ -232,7 +234,7 @@ export const AccountSecurityScreen: React.FC = () => {
disabled={sendingCode || countdown > 0}
>
{sendingCode ? (
<ActivityIndicator size="small" color="#fff" />
<ActivityIndicator size="small" color={colors.text.inverse} />
) : (
<Text style={styles.sendCodeButtonText}>{countdown > 0 ? `${countdown}s` : '发送验证码'}</Text>
)}
@@ -244,7 +246,11 @@ export const AccountSecurityScreen: React.FC = () => {
onPress={handleVerifyEmail}
disabled={verifyingEmail}
>
{verifyingEmail ? <ActivityIndicator size="small" color="#fff" /> : <Text style={styles.primaryButtonText}></Text>}
{verifyingEmail ? (
<ActivityIndicator size="small" color={colors.text.inverse} />
) : (
<Text style={styles.primaryButtonText}></Text>
)}
</TouchableOpacity>
</View>
</View>
@@ -298,7 +304,7 @@ export const AccountSecurityScreen: React.FC = () => {
disabled={sendingChangePwdCode || changePwdCountdown > 0}
>
{sendingChangePwdCode ? (
<ActivityIndicator size="small" color="#fff" />
<ActivityIndicator size="small" color={colors.text.inverse} />
) : (
<Text style={styles.sendCodeButtonText}>
{changePwdCountdown > 0 ? `${changePwdCountdown}s` : '发送验证码'}
@@ -323,7 +329,11 @@ export const AccountSecurityScreen: React.FC = () => {
onPress={handleChangePassword}
disabled={updatingPassword}
>
{updatingPassword ? <ActivityIndicator size="small" color="#fff" /> : <Text style={styles.primaryButtonText}></Text>}
{updatingPassword ? (
<ActivityIndicator size="small" color={colors.text.inverse} />
) : (
<Text style={styles.primaryButtonText}></Text>
)}
</TouchableOpacity>
</View>
</View>
@@ -343,109 +353,117 @@ export const AccountSecurityScreen: React.FC = () => {
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background.default,
},
scrollContent: {
paddingVertical: spacing.md,
},
section: {
marginBottom: spacing.lg,
},
sectionHeader: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: spacing.lg,
marginBottom: spacing.sm,
gap: spacing.xs,
},
sectionTitle: {
fontWeight: '600',
},
card: {
backgroundColor: colors.background.paper,
marginHorizontal: spacing.lg,
borderRadius: borderRadius.lg,
padding: spacing.md,
},
statusRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: spacing.md,
},
statusBadge: {
paddingHorizontal: spacing.sm,
paddingVertical: 4,
borderRadius: borderRadius.sm,
},
statusVerified: {
backgroundColor: '#E8F5E9',
},
statusUnverified: {
backgroundColor: '#FFF3E0',
},
inputWrapper: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.background.default,
borderRadius: borderRadius.lg,
borderWidth: 1,
borderColor: colors.divider,
paddingHorizontal: spacing.md,
height: 48,
marginBottom: spacing.sm,
},
inputIcon: {
marginRight: spacing.sm,
},
input: {
flex: 1,
color: colors.text.primary,
fontSize: fontSizes.md,
},
codeRow: {
flexDirection: 'row',
alignItems: 'center',
gap: spacing.sm,
marginBottom: spacing.sm,
},
codeInput: {
flex: 1,
marginBottom: 0,
},
sendCodeButton: {
height: 48,
minWidth: 110,
borderRadius: borderRadius.lg,
backgroundColor: colors.primary.main,
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: spacing.sm,
},
sendCodeButtonText: {
color: '#fff',
fontSize: fontSizes.sm,
fontWeight: '600',
},
primaryButton: {
height: 48,
borderRadius: borderRadius.lg,
backgroundColor: colors.primary.main,
alignItems: 'center',
justifyContent: 'center',
marginTop: spacing.xs,
},
primaryButtonText: {
color: '#fff',
fontSize: fontSizes.md,
fontWeight: '700',
},
buttonDisabled: {
opacity: 0.6,
},
});
function createAccountSecurityStyles(colors: AppColors) {
return StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background.default,
},
scrollContent: {
paddingVertical: spacing.md,
},
section: {
marginBottom: spacing.lg,
},
sectionHeader: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: spacing.lg,
marginBottom: spacing.sm,
gap: spacing.xs,
},
sectionTitle: {
fontWeight: '600',
},
card: {
backgroundColor: colors.background.paper,
marginHorizontal: spacing.lg,
borderRadius: borderRadius.lg,
padding: spacing.md,
},
statusRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: spacing.md,
},
statusBadge: {
paddingHorizontal: spacing.sm,
paddingVertical: 4,
borderRadius: borderRadius.sm,
},
statusVerified: {
backgroundColor: colors.success.light + '40',
},
statusUnverified: {
backgroundColor: colors.warning.light + '40',
},
statusVerifiedText: {
color: colors.success.dark,
},
statusUnverifiedText: {
color: colors.warning.dark,
},
inputWrapper: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.background.default,
borderRadius: borderRadius.lg,
borderWidth: 1,
borderColor: colors.divider,
paddingHorizontal: spacing.md,
height: 48,
marginBottom: spacing.sm,
},
inputIcon: {
marginRight: spacing.sm,
},
input: {
flex: 1,
color: colors.text.primary,
fontSize: fontSizes.md,
},
codeRow: {
flexDirection: 'row',
alignItems: 'center',
gap: spacing.sm,
marginBottom: spacing.sm,
},
codeInput: {
flex: 1,
marginBottom: 0,
},
sendCodeButton: {
height: 48,
minWidth: 110,
borderRadius: borderRadius.lg,
backgroundColor: colors.primary.main,
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: spacing.sm,
},
sendCodeButtonText: {
color: colors.text.inverse,
fontSize: fontSizes.sm,
fontWeight: '600',
},
primaryButton: {
height: 48,
borderRadius: borderRadius.lg,
backgroundColor: colors.primary.main,
alignItems: 'center',
justifyContent: 'center',
marginTop: spacing.xs,
},
primaryButtonText: {
color: colors.text.inverse,
fontSize: fontSizes.md,
fontWeight: '700',
},
buttonDisabled: {
opacity: 0.6,
},
});
}
export default AccountSecurityScreen;

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import {
ActivityIndicator,
FlatList,
@@ -12,12 +12,52 @@ import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
import { useRouter } from 'expo-router';
import { Avatar, Button, EmptyState, ResponsiveContainer, Text } from '../../components/common';
import { authService } from '../../services';
import { colors, spacing, borderRadius, shadows } from '../../theme';
import { spacing, borderRadius, shadows, useAppColors, type AppColors } from '../../theme';
import { User } from '../../types';
import { useResponsive } from '../../hooks';
import * as hrefs from '../../navigation/hrefs';
function createBlockedUsersStyles(colors: AppColors) {
return StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background.default,
},
loadingWrap: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
listContent: {
padding: spacing.md,
gap: spacing.sm,
},
emptyContent: {
flexGrow: 1,
justifyContent: 'center',
},
item: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.background.paper,
borderRadius: borderRadius.lg,
padding: spacing.md,
...shadows.sm,
},
content: {
flex: 1,
marginLeft: spacing.md,
marginRight: spacing.sm,
},
nickname: {
fontWeight: '600',
},
});
}
export const BlockedUsersScreen: React.FC = () => {
const colors = useAppColors();
const styles = useMemo(() => createBlockedUsersStyles(colors), [colors]);
const router = useRouter();
const { isMobile } = useResponsive();
const insets = useSafeAreaInsets();
@@ -141,40 +181,4 @@ export const BlockedUsersScreen: React.FC = () => {
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background.default,
},
loadingWrap: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
listContent: {
padding: spacing.md,
gap: spacing.sm,
},
emptyContent: {
flexGrow: 1,
justifyContent: 'center',
},
item: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.background.paper,
borderRadius: borderRadius.lg,
padding: spacing.md,
...shadows.sm,
},
content: {
flex: 1,
marginLeft: spacing.md,
marginRight: spacing.sm,
},
nickname: {
fontWeight: '600',
},
});
export default BlockedUsersScreen;

View File

@@ -5,7 +5,7 @@
* 表单在宽屏下居中显示
*/
import React, { useState } from 'react';
import React, { useState, useMemo } from 'react';
import {
View,
ScrollView,
@@ -22,12 +22,274 @@ import { useNavigation } from '@react-navigation/native';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import * as ImagePicker from 'expo-image-picker';
import { LinearGradient } from 'expo-linear-gradient';
import { colors, spacing, fontSizes, borderRadius, shadows } from '../../theme';
import {
spacing,
fontSizes,
borderRadius,
shadows,
useAppColors,
type AppColors,
} from '../../theme';
import { useAuthStore } from '../../stores';
import { Avatar, Button, Text, ResponsiveContainer } from '../../components/common';
import { authService, uploadService } from '../../services';
import { useResponsive } from '../../hooks';
function createEditProfileStyles(colors: AppColors) {
return StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background.default,
},
keyboardView: {
flex: 1,
},
scrollContent: {
padding: spacing.lg,
},
// ===== 预览区域 - 与 UserProfileHeader 完全一致 =====
previewContainer: {
marginBottom: spacing.lg,
},
coverContainer: {
position: 'relative',
overflow: 'hidden',
borderRadius: borderRadius.lg,
},
coverTouchable: {
width: '100%',
height: '100%',
},
coverImage: {
width: '100%',
height: '100%',
},
gradient: {
width: '100%',
height: '100%',
},
coverUploadingOverlay: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'center',
alignItems: 'center',
},
coverEditOverlay: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'rgba(0, 0, 0, 0.3)',
justifyContent: 'center',
alignItems: 'center',
},
coverEditText: {
color: colors.text.inverse,
fontSize: fontSizes.sm,
marginTop: spacing.xs,
fontWeight: '500',
},
waveDecoration: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: 40,
},
wave: {
width: '100%',
height: '100%',
backgroundColor: colors.background.paper,
borderTopLeftRadius: 30,
borderTopRightRadius: 30,
},
// 用户信息卡片
profileCard: {
backgroundColor: colors.background.paper,
marginHorizontal: spacing.md,
marginTop: -50,
borderRadius: borderRadius.xl,
padding: spacing.lg,
...shadows.md,
},
avatarWrapper: {
alignItems: 'center',
marginTop: -60,
marginBottom: spacing.md,
},
avatarContainer: {
position: 'relative',
padding: 4,
backgroundColor: colors.background.paper,
borderRadius: 50,
},
avatarUploadingOverlay: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
borderRadius: 60,
justifyContent: 'center',
alignItems: 'center',
},
editAvatarButton: {
position: 'absolute',
bottom: 0,
right: 0,
width: 28,
height: 28,
borderRadius: 14,
backgroundColor: colors.primary.main,
justifyContent: 'center',
alignItems: 'center',
borderWidth: 2,
borderColor: colors.background.paper,
},
userInfo: {
alignItems: 'center',
marginBottom: spacing.md,
},
nickname: {
marginBottom: spacing.xs,
fontWeight: '700',
},
username: {
marginBottom: spacing.sm,
},
bio: {
textAlign: 'center',
marginTop: spacing.sm,
lineHeight: 20,
},
bioPlaceholder: {
textAlign: 'center',
marginTop: spacing.sm,
fontStyle: 'italic',
},
metaInfo: {
flexDirection: 'row',
justifyContent: 'center',
flexWrap: 'wrap',
marginBottom: spacing.md,
gap: spacing.sm,
},
metaTag: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.background.default,
paddingHorizontal: spacing.sm,
paddingVertical: spacing.xs,
borderRadius: borderRadius.md,
},
metaTagText: {
marginLeft: spacing.xs,
fontSize: fontSizes.xs,
},
editHint: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: colors.background.default,
borderRadius: borderRadius.md,
paddingVertical: spacing.sm,
gap: spacing.xs,
},
editHintText: {
fontSize: fontSizes.xs,
},
// ===== 表单区域 =====
formCard: {
backgroundColor: colors.background.paper,
borderRadius: borderRadius.lg,
padding: spacing.lg,
marginBottom: spacing.lg,
shadowColor: colors.text.primary,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.05,
shadowRadius: 8,
elevation: 2,
},
sectionTitle: {
marginBottom: spacing.lg,
},
formField: {
flexDirection: 'row',
alignItems: 'flex-start',
},
fieldIconContainer: {
width: 40,
height: 40,
borderRadius: borderRadius.md,
backgroundColor: colors.primary.light + '20',
justifyContent: 'center',
alignItems: 'center',
marginRight: spacing.md,
marginTop: spacing.xs,
},
fieldContent: {
flex: 1,
},
fieldLabel: {
marginBottom: spacing.xs,
fontWeight: '500',
},
fieldInput: {
fontSize: fontSizes.md,
color: colors.text.primary,
paddingVertical: spacing.sm,
borderBottomWidth: 1,
borderBottomColor: colors.divider,
},
textArea: {
height: 80,
textAlignVertical: 'top',
},
disabledInput: {
color: colors.text.disabled,
},
divider: {
height: 1,
backgroundColor: colors.divider,
marginVertical: spacing.md,
marginLeft: 56,
},
// 保存按钮
buttonContainer: {
marginTop: spacing.sm,
},
saveButton: {
backgroundColor: colors.primary.main,
borderRadius: borderRadius.lg,
paddingVertical: spacing.md,
shadowColor: colors.primary.main,
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 8,
elevation: 4,
},
saveButtonDisabled: {
opacity: 0.6,
},
saveButtonContent: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
buttonIcon: {
marginRight: spacing.sm,
},
saveButtonText: {
color: colors.text.inverse,
},
});
}
type EditProfileSheet = ReturnType<typeof createEditProfileStyles>;
// 表单输入项组件
interface FormFieldProps {
icon: string;
@@ -41,6 +303,8 @@ interface FormFieldProps {
autoCapitalize?: 'none' | 'sentences' | 'words' | 'characters';
keyboardType?: 'default' | 'email-address' | 'numeric' | 'phone-pad' | 'url';
editable?: boolean;
sheet: EditProfileSheet;
colors: AppColors;
}
const FormField: React.FC<FormFieldProps> = ({
@@ -55,19 +319,21 @@ const FormField: React.FC<FormFieldProps> = ({
autoCapitalize = 'sentences',
keyboardType = 'default',
editable = true,
sheet,
colors,
}) => {
return (
<View style={styles.formField}>
<View style={styles.fieldIconContainer}>
<View style={sheet.formField}>
<View style={sheet.fieldIconContainer}>
<MaterialCommunityIcons name={icon as any} size={22} color={colors.primary.main} />
</View>
<View style={styles.fieldContent}>
<Text variant="caption" style={styles.fieldLabel}>{label}</Text>
<View style={sheet.fieldContent}>
<Text variant="caption" style={sheet.fieldLabel}>{label}</Text>
<TextInput
style={[
styles.fieldInput,
multiline && styles.textArea,
!editable && styles.disabledInput
sheet.fieldInput,
multiline && sheet.textArea,
!editable && sheet.disabledInput
]}
value={value}
onChangeText={onChangeText}
@@ -86,6 +352,8 @@ const FormField: React.FC<FormFieldProps> = ({
};
export const EditProfileScreen: React.FC = () => {
const colors = useAppColors();
const styles = useMemo(() => createEditProfileStyles(colors), [colors]);
const navigation = useNavigation();
const { currentUser, updateUser } = useAuthStore();
const { isWideScreen, isMobile, width } = useResponsive();
@@ -401,6 +669,8 @@ export const EditProfileScreen: React.FC = () => {
onChangeText={setNickname}
placeholder="请输入昵称"
maxLength={20}
sheet={styles}
colors={colors}
/>
<View style={styles.divider} />
@@ -414,6 +684,8 @@ export const EditProfileScreen: React.FC = () => {
maxLength={100}
multiline
numberOfLines={3}
sheet={styles}
colors={colors}
/>
<View style={styles.divider} />
@@ -425,6 +697,8 @@ export const EditProfileScreen: React.FC = () => {
onChangeText={setLocation}
placeholder="你所在的城市"
maxLength={30}
sheet={styles}
colors={colors}
/>
<View style={styles.divider} />
@@ -438,6 +712,8 @@ export const EditProfileScreen: React.FC = () => {
maxLength={100}
autoCapitalize="none"
keyboardType="url"
sheet={styles}
colors={colors}
/>
</View>
@@ -455,6 +731,8 @@ export const EditProfileScreen: React.FC = () => {
placeholder="请输入手机号"
maxLength={11}
keyboardType="phone-pad"
sheet={styles}
colors={colors}
/>
<View style={styles.divider} />
@@ -468,6 +746,8 @@ export const EditProfileScreen: React.FC = () => {
maxLength={100}
autoCapitalize="none"
keyboardType="email-address"
sheet={styles}
colors={colors}
/>
</View>
@@ -535,254 +815,5 @@ export const EditProfileScreen: React.FC = () => {
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background.default,
},
keyboardView: {
flex: 1,
},
scrollContent: {
padding: spacing.lg,
},
// ===== 预览区域 - 与 UserProfileHeader 完全一致 =====
previewContainer: {
marginBottom: spacing.lg,
},
coverContainer: {
position: 'relative',
overflow: 'hidden',
borderRadius: borderRadius.lg,
},
coverTouchable: {
width: '100%',
height: '100%',
},
coverImage: {
width: '100%',
height: '100%',
},
gradient: {
width: '100%',
height: '100%',
},
coverUploadingOverlay: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'center',
alignItems: 'center',
},
coverEditOverlay: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'rgba(0, 0, 0, 0.3)',
justifyContent: 'center',
alignItems: 'center',
},
coverEditText: {
color: colors.text.inverse,
fontSize: fontSizes.sm,
marginTop: spacing.xs,
fontWeight: '500',
},
waveDecoration: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: 40,
},
wave: {
width: '100%',
height: '100%',
backgroundColor: colors.background.paper,
borderTopLeftRadius: 30,
borderTopRightRadius: 30,
},
// 用户信息卡片
profileCard: {
backgroundColor: colors.background.paper,
marginHorizontal: spacing.md,
marginTop: -50,
borderRadius: borderRadius.xl,
padding: spacing.lg,
...shadows.md,
},
avatarWrapper: {
alignItems: 'center',
marginTop: -60,
marginBottom: spacing.md,
},
avatarContainer: {
position: 'relative',
padding: 4,
backgroundColor: colors.background.paper,
borderRadius: 50,
},
avatarUploadingOverlay: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
borderRadius: 60,
justifyContent: 'center',
alignItems: 'center',
},
editAvatarButton: {
position: 'absolute',
bottom: 0,
right: 0,
width: 28,
height: 28,
borderRadius: 14,
backgroundColor: colors.primary.main,
justifyContent: 'center',
alignItems: 'center',
borderWidth: 2,
borderColor: colors.background.paper,
},
userInfo: {
alignItems: 'center',
marginBottom: spacing.md,
},
nickname: {
marginBottom: spacing.xs,
fontWeight: '700',
},
username: {
marginBottom: spacing.sm,
},
bio: {
textAlign: 'center',
marginTop: spacing.sm,
lineHeight: 20,
},
bioPlaceholder: {
textAlign: 'center',
marginTop: spacing.sm,
fontStyle: 'italic',
},
metaInfo: {
flexDirection: 'row',
justifyContent: 'center',
flexWrap: 'wrap',
marginBottom: spacing.md,
gap: spacing.sm,
},
metaTag: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.background.default,
paddingHorizontal: spacing.sm,
paddingVertical: spacing.xs,
borderRadius: borderRadius.md,
},
metaTagText: {
marginLeft: spacing.xs,
fontSize: fontSizes.xs,
},
editHint: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: colors.background.default,
borderRadius: borderRadius.md,
paddingVertical: spacing.sm,
gap: spacing.xs,
},
editHintText: {
fontSize: fontSizes.xs,
},
// ===== 表单区域 =====
formCard: {
backgroundColor: colors.background.paper,
borderRadius: borderRadius.lg,
padding: spacing.lg,
marginBottom: spacing.lg,
shadowColor: colors.text.primary,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.05,
shadowRadius: 8,
elevation: 2,
},
sectionTitle: {
marginBottom: spacing.lg,
},
formField: {
flexDirection: 'row',
alignItems: 'flex-start',
},
fieldIconContainer: {
width: 40,
height: 40,
borderRadius: borderRadius.md,
backgroundColor: colors.primary.light + '20',
justifyContent: 'center',
alignItems: 'center',
marginRight: spacing.md,
marginTop: spacing.xs,
},
fieldContent: {
flex: 1,
},
fieldLabel: {
marginBottom: spacing.xs,
fontWeight: '500',
},
fieldInput: {
fontSize: fontSizes.md,
color: colors.text.primary,
paddingVertical: spacing.sm,
borderBottomWidth: 1,
borderBottomColor: colors.divider,
},
textArea: {
height: 80,
textAlignVertical: 'top',
},
disabledInput: {
color: colors.text.disabled,
},
divider: {
height: 1,
backgroundColor: colors.divider,
marginVertical: spacing.md,
marginLeft: 56,
},
// 保存按钮
buttonContainer: {
marginTop: spacing.sm,
},
saveButton: {
backgroundColor: colors.primary.main,
borderRadius: borderRadius.lg,
paddingVertical: spacing.md,
shadowColor: colors.primary.main,
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 8,
elevation: 4,
},
saveButtonDisabled: {
opacity: 0.6,
},
saveButtonContent: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
buttonIcon: {
marginRight: spacing.sm,
},
saveButtonText: {
color: colors.text.inverse,
},
});
export default EditProfileScreen;

View File

@@ -5,7 +5,7 @@
* 在宽屏下使用网格布局
*/
import React, { useState, useEffect, useCallback } from 'react';
import React, { useState, useEffect, useCallback, useMemo } from 'react';
import {
View,
FlatList,
@@ -17,7 +17,7 @@ import {
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useRouter, useLocalSearchParams } from 'expo-router';
import { colors, spacing, borderRadius, shadows } from '../../theme';
import { spacing, borderRadius, shadows, useAppColors, type AppColors } from '../../theme';
import { User } from '../../types';
import { useAuthStore, useUserStore } from '../../stores';
import { authService } from '../../services';
@@ -26,6 +26,8 @@ import * as hrefs from '../../navigation/hrefs';
import { useResponsive, useColumnCount } from '../../hooks';
const FollowListScreen: React.FC = () => {
const colors = useAppColors();
const styles = useMemo(() => createFollowListStyles(colors), [colors]);
const router = useRouter();
const { userId = '', type: typeParam } = useLocalSearchParams<{ userId?: string; type?: string }>();
const type = typeParam === 'followers' ? 'followers' : 'following';
@@ -356,136 +358,138 @@ const FollowListScreen: React.FC = () => {
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background.default,
},
// 列表布局样式
listContent: {
flexGrow: 1,
paddingHorizontal: spacing.md,
paddingTop: spacing.md,
paddingBottom: spacing.lg,
},
headerCard: {
backgroundColor: colors.background.paper,
borderRadius: borderRadius.xl,
paddingHorizontal: spacing.lg,
paddingVertical: spacing.md,
marginBottom: spacing.md,
borderWidth: 1,
borderColor: colors.divider + '4A',
...shadows.sm,
},
headerAccent: {
width: 28,
height: 3,
borderRadius: 999,
backgroundColor: colors.primary.main + 'A6',
marginBottom: spacing.sm,
},
headerMainRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
headerTitle: {
fontWeight: '700',
},
headerCountPill: {
paddingHorizontal: spacing.sm,
paddingVertical: 4,
borderRadius: borderRadius.full,
backgroundColor: colors.background.default,
},
headerSubtitle: {
marginTop: spacing.xs,
},
userItem: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.background.paper,
borderRadius: borderRadius.lg,
padding: spacing.md,
marginBottom: spacing.sm,
borderWidth: 1,
borderColor: colors.divider + '45',
...shadows.sm,
},
userInfo: {
flex: 1,
marginLeft: spacing.md,
marginRight: spacing.sm,
},
nickname: {
fontWeight: '600',
marginBottom: 2,
},
bio: {
marginTop: 2,
},
followButton: {
minWidth: 82,
},
// 网格布局样式
gridContent: {
flexGrow: 1,
padding: spacing.lg,
},
emptyGridContent: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
gridRow: {
justifyContent: 'flex-start',
gap: spacing.md,
marginBottom: spacing.md,
},
userCard: {
flex: 1,
backgroundColor: colors.background.paper,
borderRadius: borderRadius.xl,
padding: spacing.md,
alignItems: 'center',
borderWidth: 1,
borderColor: colors.divider + '45',
...shadows.sm,
},
userCardHeader: {
marginBottom: spacing.sm,
},
userCardContent: {
alignItems: 'center',
width: '100%',
},
userCardNickname: {
fontWeight: '600',
marginBottom: 2,
textAlign: 'center',
},
userCardBio: {
marginTop: spacing.xs,
textAlign: 'center',
},
userCardFooter: {
marginTop: spacing.md,
width: '100%',
},
userCardFollowButton: {
width: '100%',
},
footerLoading: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingVertical: spacing.md,
gap: spacing.xs,
},
footerLoadingText: {
marginLeft: spacing.xs,
},
});
function createFollowListStyles(colors: AppColors) {
return StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background.default,
},
// 列表布局样式
listContent: {
flexGrow: 1,
paddingHorizontal: spacing.md,
paddingTop: spacing.md,
paddingBottom: spacing.lg,
},
headerCard: {
backgroundColor: colors.background.paper,
borderRadius: borderRadius.xl,
paddingHorizontal: spacing.lg,
paddingVertical: spacing.md,
marginBottom: spacing.md,
borderWidth: 1,
borderColor: colors.divider + '4A',
...shadows.sm,
},
headerAccent: {
width: 28,
height: 3,
borderRadius: 999,
backgroundColor: colors.primary.main + 'A6',
marginBottom: spacing.sm,
},
headerMainRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
headerTitle: {
fontWeight: '700',
},
headerCountPill: {
paddingHorizontal: spacing.sm,
paddingVertical: 4,
borderRadius: borderRadius.full,
backgroundColor: colors.background.default,
},
headerSubtitle: {
marginTop: spacing.xs,
},
userItem: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.background.paper,
borderRadius: borderRadius.lg,
padding: spacing.md,
marginBottom: spacing.sm,
borderWidth: 1,
borderColor: colors.divider + '45',
...shadows.sm,
},
userInfo: {
flex: 1,
marginLeft: spacing.md,
marginRight: spacing.sm,
},
nickname: {
fontWeight: '600',
marginBottom: 2,
},
bio: {
marginTop: 2,
},
followButton: {
minWidth: 82,
},
// 网格布局样式
gridContent: {
flexGrow: 1,
padding: spacing.lg,
},
emptyGridContent: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
gridRow: {
justifyContent: 'flex-start',
gap: spacing.md,
marginBottom: spacing.md,
},
userCard: {
flex: 1,
backgroundColor: colors.background.paper,
borderRadius: borderRadius.xl,
padding: spacing.md,
alignItems: 'center',
borderWidth: 1,
borderColor: colors.divider + '45',
...shadows.sm,
},
userCardHeader: {
marginBottom: spacing.sm,
},
userCardContent: {
alignItems: 'center',
width: '100%',
},
userCardNickname: {
fontWeight: '600',
marginBottom: 2,
textAlign: 'center',
},
userCardBio: {
marginTop: spacing.xs,
textAlign: 'center',
},
userCardFooter: {
marginTop: spacing.md,
width: '100%',
},
userCardFollowButton: {
width: '100%',
},
footerLoading: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingVertical: spacing.md,
gap: spacing.xs,
},
footerLoadingText: {
marginLeft: spacing.xs,
},
});
}
export default FollowListScreen;

View File

@@ -4,7 +4,7 @@
* 在宽屏下居中显示
*/
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useMemo } from 'react';
import {
View,
StyleSheet,
@@ -13,7 +13,7 @@ import {
} from 'react-native';
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { colors, spacing, fontSizes, borderRadius } from '../../theme';
import { spacing, borderRadius, useAppColors, type AppColors } from '../../theme';
import { Text, ResponsiveContainer } from '../../components/common';
import {
loadNotificationPreferences,
@@ -33,6 +33,8 @@ interface NotificationSettingItem {
}
export const NotificationSettingsScreen: React.FC = () => {
const colors = useAppColors();
const styles = useMemo(() => createNotificationSettingsStyles(colors), [colors]);
const [vibrationEnabled, setVibrationEnabledState] = useState(true);
const [pushEnabled, setPushEnabled] = useState(true);
const [soundEnabled, setSoundEnabled] = useState(true);
@@ -183,72 +185,74 @@ export const NotificationSettingsScreen: React.FC = () => {
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background.default,
},
scrollContent: {
paddingVertical: spacing.md,
},
section: {
marginBottom: spacing.lg,
},
sectionHeader: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: spacing.lg,
marginBottom: spacing.sm,
gap: spacing.xs,
},
sectionTitle: {
fontWeight: '600',
},
card: {
backgroundColor: colors.background.paper,
marginHorizontal: spacing.lg,
borderRadius: borderRadius.lg,
overflow: 'hidden',
},
settingItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: spacing.md,
paddingHorizontal: spacing.md,
},
iconContainer: {
width: 36,
height: 36,
borderRadius: borderRadius.md,
backgroundColor: colors.primary.light + '20',
justifyContent: 'center',
alignItems: 'center',
marginRight: spacing.md,
},
settingContent: {
flex: 1,
},
subtitle: {
marginTop: 2,
},
divider: {
height: 1,
backgroundColor: colors.divider,
marginLeft: 36 + spacing.md + spacing.md,
},
tipContainer: {
flexDirection: 'row',
alignItems: 'flex-start',
marginHorizontal: spacing.lg,
padding: spacing.md,
backgroundColor: colors.background.paper,
borderRadius: borderRadius.lg,
gap: spacing.sm,
},
tipText: {
flex: 1,
lineHeight: 20,
},
});
function createNotificationSettingsStyles(colors: AppColors) {
return StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background.default,
},
scrollContent: {
paddingVertical: spacing.md,
},
section: {
marginBottom: spacing.lg,
},
sectionHeader: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: spacing.lg,
marginBottom: spacing.sm,
gap: spacing.xs,
},
sectionTitle: {
fontWeight: '600',
},
card: {
backgroundColor: colors.background.paper,
marginHorizontal: spacing.lg,
borderRadius: borderRadius.lg,
overflow: 'hidden',
},
settingItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: spacing.md,
paddingHorizontal: spacing.md,
},
iconContainer: {
width: 36,
height: 36,
borderRadius: borderRadius.md,
backgroundColor: colors.primary.light + '20',
justifyContent: 'center',
alignItems: 'center',
marginRight: spacing.md,
},
settingContent: {
flex: 1,
},
subtitle: {
marginTop: 2,
},
divider: {
height: 1,
backgroundColor: colors.divider,
marginLeft: 36 + spacing.md + spacing.md,
},
tipContainer: {
flexDirection: 'row',
alignItems: 'flex-start',
marginHorizontal: spacing.lg,
padding: spacing.md,
backgroundColor: colors.background.paper,
borderRadius: borderRadius.lg,
gap: spacing.sm,
},
tipText: {
flex: 1,
lineHeight: 20,
},
});
}
export default NotificationSettingsScreen;

View File

@@ -4,7 +4,7 @@
* 在宽屏下居中显示,最大宽度限制
*/
import React from 'react';
import React, { useMemo } from 'react';
import {
View,
StyleSheet,
@@ -16,7 +16,15 @@ import { SafeAreaView } from 'react-native-safe-area-context';
import { useRouter } from 'expo-router';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import Constants from 'expo-constants';
import { colors, spacing, fontSizes, borderRadius } from '../../theme';
import {
useAppColors,
spacing,
fontSizes,
borderRadius,
useThemePreference,
useSetThemePreference,
type AppColors,
} from '../../theme';
import { useAuthStore } from '../../stores';
import { Text, ResponsiveContainer } from '../../components/common';
import { useResponsive } from '../../hooks';
@@ -34,11 +42,9 @@ interface SettingsItem {
subtitle?: string;
}
// 获取应用版本号
const APP_VERSION = Constants.expoConfig?.version || '1.0.0';
// 设置分组配置
const SETTINGS_GROUPS = [
const SETTINGS_GROUPS_BASE = [
{
title: '账号与安全',
icon: 'shield-check-outline',
@@ -66,19 +72,161 @@ const SETTINGS_GROUPS = [
},
];
function createSettingsStyles(colors: AppColors) {
return StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background.default,
},
scrollContent: {
paddingVertical: spacing.md,
},
groupContainer: {
marginBottom: spacing.lg,
},
groupHeader: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: spacing.lg,
marginBottom: spacing.sm,
gap: spacing.xs,
},
groupTitle: {
fontWeight: '600',
fontSize: fontSizes.sm,
},
card: {
backgroundColor: colors.background.paper,
marginHorizontal: spacing.lg,
borderRadius: borderRadius.lg,
overflow: 'hidden',
},
settingItem: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingVertical: spacing.md,
paddingHorizontal: spacing.md,
borderBottomWidth: 1,
borderBottomColor: colors.divider,
},
settingItemFirst: {
borderTopLeftRadius: borderRadius.lg,
borderTopRightRadius: borderRadius.lg,
},
settingItemLast: {
borderBottomLeftRadius: borderRadius.lg,
borderBottomRightRadius: borderRadius.lg,
borderBottomWidth: 0,
},
settingItemLeft: {
flexDirection: 'row',
alignItems: 'center',
flex: 1,
},
iconContainer: {
width: 36,
height: 36,
borderRadius: borderRadius.md,
backgroundColor: colors.primary.light + '20',
justifyContent: 'center',
alignItems: 'center',
marginRight: spacing.md,
},
dangerIconContainer: {
backgroundColor: colors.error.light + '20',
},
settingContent: {
flex: 1,
},
subtitle: {
marginTop: 2,
},
logoutButton: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: colors.background.paper,
marginHorizontal: spacing.lg,
marginTop: spacing.sm,
marginBottom: spacing.lg,
paddingVertical: spacing.md,
borderRadius: borderRadius.lg,
gap: spacing.sm,
},
logoutText: {
fontWeight: '500',
},
footer: {
alignItems: 'center',
marginTop: spacing.xl,
paddingBottom: spacing.xl,
},
copyright: {
marginTop: spacing.xs,
},
});
}
export const SettingsScreen: React.FC = () => {
const router = useRouter();
const { logout } = useAuthStore();
const { isWideScreen } = useResponsive();
const insets = useSafeAreaInsets();
const { isMobile } = useResponsive();
// 底部间距,避免被 TabBar 遮挡
const colors = useAppColors();
const styles = useMemo(() => createSettingsStyles(colors), [colors]);
const themePreference = useThemePreference();
const setThemePreference = useSetThemePreference();
const scrollBottomInset = isMobile ? 64 + 24 + insets.bottom + spacing.md : spacing.md;
// 处理设置项点击
const settingsGroups = useMemo(() => {
const themeSubtitle =
themePreference === 'system' ? '跟随系统' : themePreference === 'dark' ? '深色' : '浅色';
return [
{
title: '显示与外观',
icon: 'palette-outline',
items: [
{
key: 'theme',
title: '主题模式',
icon: 'theme-light-dark',
showArrow: true,
subtitle: themeSubtitle,
},
],
},
...SETTINGS_GROUPS_BASE,
];
}, [themePreference]);
const handleItemPress = (key: string) => {
switch (key) {
case 'theme':
Alert.alert('主题模式', '选择应用界面明暗', [
{ text: '取消', style: 'cancel' },
{
text: '跟随系统',
onPress: () => {
void setThemePreference('system');
},
},
{
text: '浅色',
onPress: () => {
void setThemePreference('light');
},
},
{
text: '深色',
onPress: () => {
void setThemePreference('dark');
},
},
]);
break;
case 'edit_profile':
router.push(hrefs.hrefProfileEdit());
break;
@@ -103,7 +251,7 @@ export const SettingsScreen: React.FC = () => {
onPress: async () => {
await logout();
router.replace(hrefs.hrefAuthLogin());
}
},
},
]
);
@@ -113,7 +261,6 @@ export const SettingsScreen: React.FC = () => {
}
};
// 渲染单个设置项
const renderSettingItem = (item: SettingsItem, index: number, total: number) => (
<TouchableOpacity
key={item.key}
@@ -122,14 +269,11 @@ export const SettingsScreen: React.FC = () => {
index === 0 && styles.settingItemFirst,
index === total - 1 && styles.settingItemLast,
]}
onPress={() => item.onPress ? item.onPress() : handleItemPress(item.key)}
onPress={() => (item.onPress ? item.onPress() : handleItemPress(item.key))}
activeOpacity={0.7}
>
<View style={styles.settingItemLeft}>
<View style={[
styles.iconContainer,
item.danger && styles.dangerIconContainer
]}>
<View style={[styles.iconContainer, item.danger && styles.dangerIconContainer]}>
<MaterialCommunityIcons
name={item.icon as any}
size={20}
@@ -137,10 +281,7 @@ export const SettingsScreen: React.FC = () => {
/>
</View>
<View style={styles.settingContent}>
<Text
variant="body"
color={item.danger ? colors.error.main : colors.text.primary}
>
<Text variant="body" color={item.danger ? colors.error.main : colors.text.primary}>
{item.title}
</Text>
{item.subtitle && (
@@ -156,8 +297,7 @@ export const SettingsScreen: React.FC = () => {
</TouchableOpacity>
);
// 渲染分组
const renderGroup = (group: typeof SETTINGS_GROUPS[0], groupIndex: number) => (
const renderGroup = (group: (typeof settingsGroups)[0]) => (
<View key={group.title} style={styles.groupContainer}>
<View style={styles.groupHeader}>
<MaterialCommunityIcons name={group.icon as any} size={16} color={colors.primary.main} />
@@ -166,14 +306,11 @@ export const SettingsScreen: React.FC = () => {
</Text>
</View>
<View style={styles.card}>
{group.items.map((item, index) =>
renderSettingItem(item, index, group.items.length)
)}
{group.items.map((item, index) => renderSettingItem(item, index, group.items.length))}
</View>
</View>
);
// 退出登录按钮
const renderLogoutButton = () => (
<TouchableOpacity
style={styles.logoutButton}
@@ -187,13 +324,11 @@ export const SettingsScreen: React.FC = () => {
</TouchableOpacity>
);
// 渲染内容
const renderContent = () => (
<>
{SETTINGS_GROUPS.map((group, index) => renderGroup(group, index))}
{settingsGroups.map((group) => renderGroup(group))}
{renderLogoutButton()}
{/* 底部版权信息 */}
<View style={styles.footer}>
<Text variant="caption" color={colors.text.hint}>
v{APP_VERSION}
@@ -222,98 +357,4 @@ export const SettingsScreen: React.FC = () => {
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background.default,
},
scrollContent: {
paddingVertical: spacing.md,
},
groupContainer: {
marginBottom: spacing.lg,
},
groupHeader: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: spacing.lg,
marginBottom: spacing.sm,
gap: spacing.xs,
},
groupTitle: {
fontWeight: '600',
fontSize: fontSizes.sm,
},
card: {
backgroundColor: colors.background.paper,
marginHorizontal: spacing.lg,
borderRadius: borderRadius.lg,
overflow: 'hidden',
},
settingItem: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingVertical: spacing.md,
paddingHorizontal: spacing.md,
borderBottomWidth: 1,
borderBottomColor: colors.divider,
},
settingItemFirst: {
borderTopLeftRadius: borderRadius.lg,
borderTopRightRadius: borderRadius.lg,
},
settingItemLast: {
borderBottomLeftRadius: borderRadius.lg,
borderBottomRightRadius: borderRadius.lg,
borderBottomWidth: 0,
},
settingItemLeft: {
flexDirection: 'row',
alignItems: 'center',
flex: 1,
},
iconContainer: {
width: 36,
height: 36,
borderRadius: borderRadius.md,
backgroundColor: colors.primary.light + '20',
justifyContent: 'center',
alignItems: 'center',
marginRight: spacing.md,
},
dangerIconContainer: {
backgroundColor: colors.error.light + '20',
},
settingContent: {
flex: 1,
},
subtitle: {
marginTop: 2,
},
logoutButton: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: colors.background.paper,
marginHorizontal: spacing.lg,
marginTop: spacing.sm,
marginBottom: spacing.lg,
paddingVertical: spacing.md,
borderRadius: borderRadius.lg,
gap: spacing.sm,
},
logoutText: {
fontWeight: '500',
},
footer: {
alignItems: 'center',
marginTop: spacing.xl,
paddingBottom: spacing.xl,
},
copyright: {
marginTop: spacing.xs,
},
});
export default SettingsScreen;

View File

@@ -12,12 +12,12 @@ import {
ScrollView,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { colors } from '../../theme';
import { useAppColors } from '../../theme';
import { Post } from '../../types';
import { PostCard, TabBar, UserProfileHeader } from '../../components/business';
import { Loading, EmptyState, ResponsiveContainer } from '../../components/common';
import { useResponsive } from '../../hooks';
import { useUserProfile, ProfileMode, TABS, TAB_ICONS, sharedStyles } from './useUserProfile';
import { useUserProfile, ProfileMode, TABS, TAB_ICONS, createSharedProfileStyles } from './useUserProfile';
interface UserProfileScreenProps {
mode: ProfileMode;
@@ -25,6 +25,8 @@ interface UserProfileScreenProps {
}
export const UserProfileScreen: React.FC<UserProfileScreenProps> = ({ mode, userId }) => {
const colors = useAppColors();
const sharedStyles = useMemo(() => createSharedProfileStyles(colors), [colors]);
const { isDesktop, isTablet } = useResponsive();
const {

View File

@@ -16,5 +16,5 @@ export { BlockedUsersScreen } from './BlockedUsersScreen';
export { AccountSecurityScreen } from './AccountSecurityScreen';
// 导出 Hook 供需要自定义的场景使用
export { useUserProfile, sharedStyles, TABS, TAB_ICONS } from './useUserProfile';
export { useUserProfile, createSharedProfileStyles, TABS, TAB_ICONS } from './useUserProfile';
export type { ProfileMode, UseUserProfileOptions, UseUserProfileReturn } from './useUserProfile';

View File

@@ -4,9 +4,10 @@
*/
import { useState, useEffect, useCallback, useMemo } from 'react';
import { StyleSheet } from 'react-native';
import { useRouter } from 'expo-router';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { colors, spacing } from '../../theme';
import { spacing, type AppColors } from '../../theme';
import { Post, User } from '../../types';
import { useUserStore } from '../../stores';
import { useCurrentUser } from '../../stores/authStore';
@@ -443,62 +444,61 @@ export const useUserProfile = (options: UseUserProfileOptions): UseUserProfileRe
};
};
// 共享的样式
import { StyleSheet } from 'react-native';
export const sharedStyles = 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 function createSharedProfileStyles(colors: AppColors) {
return 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: colors.chat.shadow,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.06,
shadowRadius: 8,
elevation: 2,
},
lastPost: {
marginBottom: spacing['2xl'],
},
});
}
// Tab 配置
export const TABS = ['帖子', '收藏'];