style(ui): apply QQ-style design to message screens
Remove decorative MaterialCommunityIcons and simplify UI to text-based lists across GroupInfoScreen, GroupMembersScreen, PrivateChatInfoScreen, and CommentItem components
This commit is contained in:
@@ -40,8 +40,6 @@ function createCommentItemStyles(colors: AppColors) {
|
|||||||
paddingBottom: spacing.md,
|
paddingBottom: spacing.md,
|
||||||
paddingHorizontal: spacing.md,
|
paddingHorizontal: spacing.md,
|
||||||
backgroundColor: colors.background.paper,
|
backgroundColor: colors.background.paper,
|
||||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
||||||
borderBottomColor: colors.divider,
|
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* GroupInfoScreen 群组信息/设置界面
|
* GroupInfoScreen 群组信息/设置界面
|
||||||
* 显示群组详细信息,提供群组管理功能
|
* 显示群组详细信息,提供群组管理功能
|
||||||
* 支持响应式布局
|
* 支持响应式布局
|
||||||
|
* QQ 风格:移除装饰性图标,使用简洁的文字列表
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
||||||
@@ -23,13 +24,11 @@ import {
|
|||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
import { useFocusEffect } from "expo-router/react-navigation";
|
import { useFocusEffect } from "expo-router/react-navigation";
|
||||||
import { useLocalSearchParams, useRouter } from 'expo-router';
|
import { useLocalSearchParams, useRouter } from 'expo-router';
|
||||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
|
||||||
import * as ImagePicker from 'expo-image-picker';
|
import * as ImagePicker from 'expo-image-picker';
|
||||||
import {
|
import {
|
||||||
spacing,
|
spacing,
|
||||||
fontSizes,
|
fontSizes,
|
||||||
borderRadius,
|
borderRadius,
|
||||||
shadows,
|
|
||||||
useAppColors,
|
useAppColors,
|
||||||
type AppColors,
|
type AppColors,
|
||||||
} from '../../theme';
|
} from '../../theme';
|
||||||
@@ -38,7 +37,7 @@ import { groupService } from '@/services/message';
|
|||||||
import { uploadService } from '@/services/upload';
|
import { uploadService } from '@/services/upload';
|
||||||
import { messageService } from '@/services/message';
|
import { messageService } from '@/services/message';
|
||||||
import { Avatar, Text, Button, Loading, Divider } from '../../components/common';
|
import { Avatar, Text, Button, Loading, Divider } from '../../components/common';
|
||||||
import { useResponsive, useBreakpointGTE } from '../../hooks';
|
import { useResponsive } from '../../hooks';
|
||||||
import {
|
import {
|
||||||
GroupResponse,
|
GroupResponse,
|
||||||
GroupMemberResponse,
|
GroupMemberResponse,
|
||||||
@@ -58,10 +57,10 @@ import { AppBackButton } from '../../components/common';
|
|||||||
const { width: SCREEN_WIDTH } = Dimensions.get('window');
|
const { width: SCREEN_WIDTH } = Dimensions.get('window');
|
||||||
|
|
||||||
// 加群方式选项
|
// 加群方式选项
|
||||||
const JOIN_TYPE_OPTIONS: { value: JoinType; label: string; icon: string; desc: string }[] = [
|
const JOIN_TYPE_OPTIONS: { value: JoinType; label: string; desc: string }[] = [
|
||||||
{ value: 0, label: '允许任何人加入', icon: 'earth', desc: '任何人都可以直接加入群聊' },
|
{ value: 0, label: '允许任何人加入', desc: '任何人都可以直接加入群聊' },
|
||||||
{ value: 1, label: '需要管理员审批', icon: 'shield-check', desc: '新成员需要管理员审核' },
|
{ value: 1, label: '需要管理员审批', desc: '新成员需要管理员审核' },
|
||||||
{ value: 2, label: '不允许任何人加入', icon: 'lock', desc: '只能通过邀请加入群聊' },
|
{ value: 2, label: '不允许任何人加入', desc: '只能通过邀请加入群聊' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const GroupInfoScreen: React.FC = () => {
|
const GroupInfoScreen: React.FC = () => {
|
||||||
@@ -77,16 +76,7 @@ const GroupInfoScreen: React.FC = () => {
|
|||||||
const { currentUser } = useAuthStore();
|
const { currentUser } = useAuthStore();
|
||||||
|
|
||||||
// 响应式布局
|
// 响应式布局
|
||||||
const { isDesktop, isTablet, width } = useResponsive();
|
const { width } = useResponsive();
|
||||||
const isWideScreen = useBreakpointGTE('lg');
|
|
||||||
|
|
||||||
// 计算成员网格列数
|
|
||||||
const memberColumns = useMemo(() => {
|
|
||||||
if (width >= 1200) return 6;
|
|
||||||
if (width >= 900) return 5;
|
|
||||||
if (width >= 768) return 4;
|
|
||||||
return 5; // 移动端默认
|
|
||||||
}, [width]);
|
|
||||||
|
|
||||||
// 群组信息状态
|
// 群组信息状态
|
||||||
const [group, setGroup] = useState<GroupResponse | null>(null);
|
const [group, setGroup] = useState<GroupResponse | null>(null);
|
||||||
@@ -512,12 +502,6 @@ const GroupInfoScreen: React.FC = () => {
|
|||||||
return option?.label || '未知';
|
return option?.label || '未知';
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取加群方式图标
|
|
||||||
const getJoinTypeIcon = (joinType: JoinType): string => {
|
|
||||||
const option = JOIN_TYPE_OPTIONS.find(o => o.value === joinType);
|
|
||||||
return option?.icon || 'help-circle';
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCopyGroupNo = () => {
|
const handleCopyGroupNo = () => {
|
||||||
if (!group) return;
|
if (!group) return;
|
||||||
Clipboard.setString(String(group.id));
|
Clipboard.setString(String(group.id));
|
||||||
@@ -530,47 +514,64 @@ const GroupInfoScreen: React.FC = () => {
|
|||||||
return `${raw.slice(0, 6)}...${raw.slice(-4)}`;
|
return `${raw.slice(0, 6)}...${raw.slice(-4)}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 渲染设置项
|
// 渲染设置项:QQ 风格,左侧标题,右侧值/箭头
|
||||||
const renderSettingItem = (
|
const renderSettingItem = (
|
||||||
icon: string,
|
|
||||||
title: string,
|
title: string,
|
||||||
value?: string,
|
value?: string,
|
||||||
onPress?: () => void,
|
onPress?: () => void,
|
||||||
danger: boolean = false
|
danger: boolean = false,
|
||||||
|
subtitle?: string
|
||||||
) => (
|
) => (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.settingItem}
|
style={styles.settingItem}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
disabled={!onPress}
|
disabled={!onPress}
|
||||||
activeOpacity={0.7}
|
activeOpacity={onPress ? 0.6 : 1}
|
||||||
>
|
>
|
||||||
<View style={[styles.settingIconContainer, danger && styles.settingIconDanger]}>
|
<View style={styles.settingItemLeft}>
|
||||||
<MaterialCommunityIcons
|
<Text variant="body" style={danger ? styles.dangerText : styles.settingItemTitle}>
|
||||||
name={icon as any}
|
|
||||||
size={20}
|
|
||||||
color={danger ? colors.error.main : colors.primary.main}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<View style={styles.settingContent}>
|
|
||||||
<Text variant="body" style={danger ? styles.dangerText : undefined}>
|
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
|
{subtitle && (
|
||||||
|
<Text variant="caption" color={colors.text.secondary} numberOfLines={2} style={styles.settingItemSubtitle}>
|
||||||
|
{subtitle}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
<View style={styles.settingValueRow}>
|
||||||
{value && (
|
{value && (
|
||||||
<Text variant="caption" color={colors.text.secondary} numberOfLines={1}>
|
<Text variant="body" color={colors.text.secondary} numberOfLines={1} style={styles.settingValueText}>
|
||||||
{value}
|
{value}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</View>
|
|
||||||
{onPress && (
|
{onPress && (
|
||||||
<MaterialCommunityIcons
|
<Text variant="body" color={colors.text.hint} style={styles.settingChevron}>
|
||||||
name="chevron-right"
|
›
|
||||||
size={22}
|
</Text>
|
||||||
color={colors.text.hint}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 渲染带开关的设置项
|
||||||
|
const renderSwitchItem = (
|
||||||
|
title: string,
|
||||||
|
value: boolean,
|
||||||
|
onValueChange: () => void,
|
||||||
|
disabled?: boolean
|
||||||
|
) => (
|
||||||
|
<View style={styles.settingItem}>
|
||||||
|
<Text variant="body" style={styles.settingItemTitle}>{title}</Text>
|
||||||
|
<Switch
|
||||||
|
value={value}
|
||||||
|
onValueChange={onValueChange}
|
||||||
|
disabled={disabled}
|
||||||
|
trackColor={{ false: colors.divider, true: colors.primary.light }}
|
||||||
|
thumbColor={value ? colors.primary.main : colors.background.paper}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
|
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
|
||||||
@@ -618,7 +619,7 @@ const GroupInfoScreen: React.FC = () => {
|
|||||||
<View style={styles.groupHeader}>
|
<View style={styles.groupHeader}>
|
||||||
<Avatar
|
<Avatar
|
||||||
source={group.avatar}
|
source={group.avatar}
|
||||||
size={80}
|
size={72}
|
||||||
name={group.name}
|
name={group.name}
|
||||||
/>
|
/>
|
||||||
<View style={styles.groupInfo}>
|
<View style={styles.groupInfo}>
|
||||||
@@ -626,12 +627,10 @@ const GroupInfoScreen: React.FC = () => {
|
|||||||
{group.name}
|
{group.name}
|
||||||
</Text>
|
</Text>
|
||||||
<View style={styles.groupMeta}>
|
<View style={styles.groupMeta}>
|
||||||
<MaterialCommunityIcons name="account-group" size={14} color={colors.text.secondary} />
|
|
||||||
<Text variant="caption" color={colors.text.secondary}>
|
<Text variant="caption" color={colors.text.secondary}>
|
||||||
{group.member_count} 人
|
{group.member_count} 人
|
||||||
</Text>
|
</Text>
|
||||||
<Text variant="caption" color={colors.text.hint}> · </Text>
|
<Text variant="caption" color={colors.text.hint}> · </Text>
|
||||||
<MaterialCommunityIcons name={getJoinTypeIcon(group.join_type) as any} size={14} color={colors.text.secondary} />
|
|
||||||
<Text variant="caption" color={colors.text.secondary} numberOfLines={1} style={{ flex: 1 }}>
|
<Text variant="caption" color={colors.text.secondary} numberOfLines={1} style={{ flex: 1 }}>
|
||||||
{getJoinTypeText(group.join_type)}
|
{getJoinTypeText(group.join_type)}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -641,7 +640,6 @@ const GroupInfoScreen: React.FC = () => {
|
|||||||
群号:{formatGroupNo(group.id)}
|
群号:{formatGroupNo(group.id)}
|
||||||
</Text>
|
</Text>
|
||||||
<TouchableOpacity style={styles.copyGroupNoBtn} onPress={handleCopyGroupNo} activeOpacity={0.7}>
|
<TouchableOpacity style={styles.copyGroupNoBtn} onPress={handleCopyGroupNo} activeOpacity={0.7}>
|
||||||
<MaterialCommunityIcons name="content-copy" size={14} color={colors.primary.main} />
|
|
||||||
<Text variant="caption" color={colors.primary.main} style={styles.copyGroupNoBtnText}>
|
<Text variant="caption" color={colors.primary.main} style={styles.copyGroupNoBtnText}>
|
||||||
复制
|
复制
|
||||||
</Text>
|
</Text>
|
||||||
@@ -651,14 +649,12 @@ const GroupInfoScreen: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
{group.description ? (
|
{group.description ? (
|
||||||
<View style={styles.descriptionContainer}>
|
<View style={styles.descriptionContainer}>
|
||||||
<MaterialCommunityIcons name="information-outline" size={16} color={colors.text.secondary} />
|
|
||||||
<Text variant="body" color={colors.text.secondary} style={styles.groupDesc}>
|
<Text variant="body" color={colors.text.secondary} style={styles.groupDesc}>
|
||||||
{group.description}
|
{group.description}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<View style={styles.descriptionContainer}>
|
<View style={styles.descriptionContainer}>
|
||||||
<MaterialCommunityIcons name="information-outline" size={16} color={colors.text.hint} />
|
|
||||||
<Text variant="body" color={colors.text.hint} style={styles.groupDesc}>
|
<Text variant="body" color={colors.text.hint} style={styles.groupDesc}>
|
||||||
暂无群描述
|
暂无群描述
|
||||||
</Text>
|
</Text>
|
||||||
@@ -669,37 +665,32 @@ const GroupInfoScreen: React.FC = () => {
|
|||||||
{/* 群公告 */}
|
{/* 群公告 */}
|
||||||
{announcements.length > 0 && (
|
{announcements.length > 0 && (
|
||||||
<View style={styles.card}>
|
<View style={styles.card}>
|
||||||
<View style={styles.cardHeader}>
|
<TouchableOpacity
|
||||||
<View style={styles.cardIconContainer}>
|
style={styles.announcementRow}
|
||||||
<MaterialCommunityIcons name="bullhorn" size={18} color={colors.warning.main} />
|
onPress={() => setAnnouncementModalVisible(true)}
|
||||||
|
activeOpacity={0.7}
|
||||||
|
>
|
||||||
|
<View style={styles.announcementHeader}>
|
||||||
|
<Text variant="body" style={styles.settingItemTitle}>群公告</Text>
|
||||||
|
<Text variant="body" color={colors.text.hint} style={styles.settingChevron}>›</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text variant="label" style={styles.cardTitle}>群公告</Text>
|
<Text variant="body" color={colors.text.secondary} numberOfLines={2} style={styles.announcementPreviewText}>
|
||||||
</View>
|
|
||||||
<View style={styles.announcementContent}>
|
|
||||||
<Text variant="body" style={styles.announcementText}>
|
|
||||||
{announcements[0].content}
|
{announcements[0].content}
|
||||||
</Text>
|
</Text>
|
||||||
<Text variant="caption" color={colors.text.hint} style={styles.announcementTime}>
|
</TouchableOpacity>
|
||||||
{(() => {
|
|
||||||
const d = new Date(announcements[0].created_at);
|
|
||||||
return Number.isNaN(d.getTime()) ? '' : d.toLocaleDateString();
|
|
||||||
})()}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 成员预览 */}
|
{/* 成员预览 */}
|
||||||
<View style={styles.card}>
|
<View style={styles.card}>
|
||||||
<TouchableOpacity style={styles.cardHeader} onPress={goToMembers} activeOpacity={0.7}>
|
<TouchableOpacity style={styles.cardHeader} onPress={goToMembers} activeOpacity={0.7}>
|
||||||
<View style={styles.cardIconContainer}>
|
<Text variant="body" style={styles.settingItemTitle}>群成员</Text>
|
||||||
<MaterialCommunityIcons name="account-multiple" size={18} color={colors.info.main} />
|
<View style={styles.cardValueRow}>
|
||||||
</View>
|
<Text variant="body" color={colors.text.secondary} style={styles.memberCount}>
|
||||||
<Text variant="label" style={styles.cardTitle}>群成员</Text>
|
|
||||||
<Text variant="caption" color={colors.text.secondary} style={styles.memberCount}>
|
|
||||||
{group.member_count}人
|
{group.member_count}人
|
||||||
</Text>
|
</Text>
|
||||||
<MaterialCommunityIcons name="chevron-right" size={20} color={colors.text.hint} />
|
<Text variant="body" color={colors.text.hint} style={styles.settingChevron}>›</Text>
|
||||||
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
<View style={styles.memberPreview}>
|
<View style={styles.memberPreview}>
|
||||||
@@ -740,108 +731,42 @@ const GroupInfoScreen: React.FC = () => {
|
|||||||
|
|
||||||
{/* 邀请成员按钮 */}
|
{/* 邀请成员按钮 */}
|
||||||
<TouchableOpacity style={styles.inviteButton} onPress={openInviteModal} activeOpacity={0.8}>
|
<TouchableOpacity style={styles.inviteButton} onPress={openInviteModal} activeOpacity={0.8}>
|
||||||
<View style={styles.inviteIconContainer}>
|
|
||||||
<MaterialCommunityIcons name="account-plus" size={20} color={colors.primary.main} />
|
|
||||||
</View>
|
|
||||||
<Text variant="body" color={colors.primary.main} style={styles.inviteButtonText}>
|
<Text variant="body" color={colors.primary.main} style={styles.inviteButtonText}>
|
||||||
邀请新成员
|
+ 邀请新成员
|
||||||
</Text>
|
</Text>
|
||||||
<MaterialCommunityIcons name="chevron-right" size={20} color={colors.primary.main} />
|
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{/* 群名称 / 群描述 */}
|
||||||
|
<View style={styles.card}>
|
||||||
|
<View style={styles.settingsList}>
|
||||||
|
{isAdmin && renderSettingItem('群名称', group.name, openEditModal)}
|
||||||
|
{isAdmin && <Divider style={styles.settingDivider} />}
|
||||||
|
{isOwner && renderSettingItem('加群方式', getJoinTypeText(group.join_type), () => setJoinTypeModalVisible(true))}
|
||||||
|
{isOwner && <Divider style={styles.settingDivider} />}
|
||||||
|
{isAdmin && renderSettingItem('群描述', group.description || '未设置', openEditModal)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
{/* 管理员设置 */}
|
{/* 管理员设置 */}
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<View style={styles.card}>
|
<View style={styles.card}>
|
||||||
<View style={styles.cardHeader}>
|
|
||||||
<View style={[styles.cardIconContainer, { backgroundColor: colors.primary.light + '20' }]}>
|
|
||||||
<MaterialCommunityIcons name="shield-crown" size={18} color={colors.primary.main} />
|
|
||||||
</View>
|
|
||||||
<Text variant="label" style={styles.cardTitle}>群管理</Text>
|
|
||||||
</View>
|
|
||||||
<View style={styles.settingsList}>
|
<View style={styles.settingsList}>
|
||||||
{renderSettingItem('pencil-outline', '修改群信息', undefined, openEditModal)}
|
{renderSettingItem('发布群公告', undefined, () => setAnnouncementModalVisible(true))}
|
||||||
<Divider style={styles.settingDivider} />
|
|
||||||
{isOwner && renderSettingItem('earth', '加群方式', getJoinTypeText(group.join_type), () => setJoinTypeModalVisible(true))}
|
|
||||||
{isOwner && <Divider style={styles.settingDivider} />}
|
{isOwner && <Divider style={styles.settingDivider} />}
|
||||||
{isOwner && (
|
{isOwner && renderSwitchItem('全员禁言', group.mute_all, handleToggleMuteAll)}
|
||||||
<View style={styles.settingItem}>
|
|
||||||
<View style={styles.settingIconContainer}>
|
|
||||||
<MaterialCommunityIcons name="microphone-off" size={20} color={colors.primary.main} />
|
|
||||||
</View>
|
|
||||||
<View style={styles.settingContent}>
|
|
||||||
<Text variant="body">全员禁言</Text>
|
|
||||||
<Text variant="caption" color={colors.text.secondary}>
|
|
||||||
{group.mute_all ? '已开启' : '已关闭'}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<Switch
|
|
||||||
value={group.mute_all}
|
|
||||||
onValueChange={handleToggleMuteAll}
|
|
||||||
trackColor={{ false: colors.divider, true: colors.primary.light }}
|
|
||||||
thumbColor={group.mute_all ? colors.primary.main : colors.background.paper}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
<Divider style={styles.settingDivider} />
|
|
||||||
{renderSettingItem('bullhorn-outline', '发布群公告', undefined, () => setAnnouncementModalVisible(true))}
|
|
||||||
{isOwner && <Divider style={styles.settingDivider} />}
|
{isOwner && <Divider style={styles.settingDivider} />}
|
||||||
{isOwner && renderSettingItem('account-switch', '转让群主', undefined, () => setTransferModalVisible(true))}
|
{isOwner && renderSettingItem('转让群主', undefined, () => setTransferModalVisible(true))}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 聊天设置 */}
|
{/* 聊天设置 */}
|
||||||
<View style={styles.card}>
|
<View style={styles.card}>
|
||||||
<View style={styles.cardHeader}>
|
|
||||||
<View style={[styles.cardIconContainer, { backgroundColor: colors.warning.light + '20' }]}>
|
|
||||||
<MaterialCommunityIcons name="chat-outline" size={18} color={colors.warning.main} />
|
|
||||||
</View>
|
|
||||||
<Text variant="label" style={styles.cardTitle}>聊天设置</Text>
|
|
||||||
</View>
|
|
||||||
<View style={styles.settingsList}>
|
<View style={styles.settingsList}>
|
||||||
<View style={styles.settingItem}>
|
|
||||||
<View style={styles.settingIconContainer}>
|
|
||||||
<MaterialCommunityIcons name="pin-outline" size={20} color={colors.primary.main} />
|
|
||||||
</View>
|
|
||||||
<View style={styles.settingContent}>
|
|
||||||
<Text variant="body">置顶群聊</Text>
|
|
||||||
<Text variant="caption" color={colors.text.secondary}>
|
|
||||||
{conversationId ? (isPinned ? '已置顶' : '未置顶') : '请从聊天页面进入后设置'}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<Switch
|
|
||||||
value={isPinned}
|
|
||||||
onValueChange={handleTogglePinned}
|
|
||||||
disabled={!conversationId || pinLoading}
|
|
||||||
trackColor={{ false: colors.divider, true: colors.primary.light }}
|
|
||||||
thumbColor={isPinned ? colors.primary.main : colors.background.paper}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<Divider style={styles.settingDivider} />
|
|
||||||
<View style={styles.settingItem}>
|
|
||||||
<View style={styles.settingIconContainer}>
|
|
||||||
<MaterialCommunityIcons name="bell-off-outline" size={20} color={colors.primary.main} />
|
|
||||||
</View>
|
|
||||||
<View style={styles.settingContent}>
|
|
||||||
<Text variant="body">消息免打扰</Text>
|
|
||||||
<Text variant="caption" color={colors.text.secondary}>
|
|
||||||
{conversationId ? (isNotificationMuted ? '已开启' : '未开启') : '请从聊天页面进入后设置'}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<Switch
|
|
||||||
value={isNotificationMuted}
|
|
||||||
onValueChange={handleToggleNotificationMuted}
|
|
||||||
disabled={!conversationId}
|
|
||||||
trackColor={{ false: colors.divider, true: colors.primary.light }}
|
|
||||||
thumbColor={isNotificationMuted ? colors.primary.main : colors.background.paper}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<Divider style={styles.settingDivider} />
|
|
||||||
{renderSettingItem(
|
{renderSettingItem(
|
||||||
'magnify',
|
|
||||||
'查找聊天记录',
|
'查找聊天记录',
|
||||||
undefined,
|
'图片、视频、文件等',
|
||||||
conversationId
|
conversationId
|
||||||
? () => router.push(hrefs.hrefMessageSearch({
|
? () => router.push(hrefs.hrefMessageSearch({
|
||||||
conversationId,
|
conversationId,
|
||||||
@@ -850,6 +775,10 @@ const GroupInfoScreen: React.FC = () => {
|
|||||||
}))
|
}))
|
||||||
: undefined,
|
: undefined,
|
||||||
)}
|
)}
|
||||||
|
<Divider style={styles.settingDivider} />
|
||||||
|
{renderSwitchItem('设为置顶', isPinned, handleTogglePinned, !conversationId || pinLoading)}
|
||||||
|
<Divider style={styles.settingDivider} />
|
||||||
|
{renderSwitchItem('消息免打扰', isNotificationMuted, handleToggleNotificationMuted, !conversationId)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@@ -861,7 +790,6 @@ const GroupInfoScreen: React.FC = () => {
|
|||||||
onPress={handleDissolveGroup}
|
onPress={handleDissolveGroup}
|
||||||
activeOpacity={0.7}
|
activeOpacity={0.7}
|
||||||
>
|
>
|
||||||
<MaterialCommunityIcons name="delete-forever" size={22} color={colors.error.main} />
|
|
||||||
<Text variant="body" color={colors.error.main} style={styles.dangerButtonText}>
|
<Text variant="body" color={colors.error.main} style={styles.dangerButtonText}>
|
||||||
解散群组
|
解散群组
|
||||||
</Text>
|
</Text>
|
||||||
@@ -872,7 +800,6 @@ const GroupInfoScreen: React.FC = () => {
|
|||||||
onPress={handleLeaveGroup}
|
onPress={handleLeaveGroup}
|
||||||
activeOpacity={0.7}
|
activeOpacity={0.7}
|
||||||
>
|
>
|
||||||
<MaterialCommunityIcons name="logout" size={22} color={colors.error.main} />
|
|
||||||
<Text variant="body" color={colors.error.main} style={styles.dangerButtonText}>
|
<Text variant="body" color={colors.error.main} style={styles.dangerButtonText}>
|
||||||
退出群聊
|
退出群聊
|
||||||
</Text>
|
</Text>
|
||||||
@@ -919,7 +846,7 @@ const GroupInfoScreen: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity style={styles.editAvatarButton} onPress={handleSelectGroupAvatar} disabled={uploadingAvatar}>
|
<TouchableOpacity style={styles.editAvatarButton} onPress={handleSelectGroupAvatar} disabled={uploadingAvatar}>
|
||||||
<MaterialCommunityIcons name="camera" size={16} color={colors.background.paper} />
|
<Text variant="caption" color={colors.background.paper}>更换</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@@ -1032,13 +959,6 @@ const GroupInfoScreen: React.FC = () => {
|
|||||||
onPress={() => handleSetJoinType(option.value)}
|
onPress={() => handleSetJoinType(option.value)}
|
||||||
activeOpacity={0.7}
|
activeOpacity={0.7}
|
||||||
>
|
>
|
||||||
<View style={[styles.joinTypeIcon, group.join_type === option.value && styles.joinTypeIconSelected]}>
|
|
||||||
<MaterialCommunityIcons
|
|
||||||
name={option.icon as any}
|
|
||||||
size={24}
|
|
||||||
color={group.join_type === option.value ? colors.primary.main : colors.text.secondary}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<View style={styles.joinTypeInfo}>
|
<View style={styles.joinTypeInfo}>
|
||||||
<Text
|
<Text
|
||||||
variant="body"
|
variant="body"
|
||||||
@@ -1051,7 +971,7 @@ const GroupInfoScreen: React.FC = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
{group.join_type === option.value && (
|
{group.join_type === option.value && (
|
||||||
<MaterialCommunityIcons name="check-circle" size={24} color={colors.primary.main} />
|
<Text variant="body" color={colors.primary.main} style={styles.joinTypeCheck}>✓</Text>
|
||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
))}
|
))}
|
||||||
@@ -1109,7 +1029,7 @@ const GroupInfoScreen: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
<View style={[styles.transferRadio, transferUserId === member.user_id && styles.transferRadioSelected]}>
|
<View style={[styles.transferRadio, transferUserId === member.user_id && styles.transferRadioSelected]}>
|
||||||
{transferUserId === member.user_id && (
|
{transferUserId === member.user_id && (
|
||||||
<MaterialCommunityIcons name="check" size={14} color={colors.background.paper} />
|
<Text variant="caption" color={colors.background.paper}>✓</Text>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
@@ -1145,16 +1065,17 @@ function createGroupInfoStyles(colors: AppColors) {
|
|||||||
return StyleSheet.create({
|
return StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: colors.background.paper,
|
backgroundColor: colors.background.default,
|
||||||
},
|
},
|
||||||
pageHeader: {
|
pageHeader: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
paddingHorizontal: spacing.lg,
|
paddingHorizontal: spacing.md,
|
||||||
paddingVertical: spacing.md,
|
paddingVertical: spacing.sm,
|
||||||
backgroundColor: colors.background.paper,
|
backgroundColor: colors.background.paper,
|
||||||
borderBottomWidth: 0,
|
borderBottomWidth: 0.5,
|
||||||
|
borderBottomColor: colors.divider + '60',
|
||||||
},
|
},
|
||||||
pageTitle: {
|
pageTitle: {
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
@@ -1168,7 +1089,6 @@ function createGroupInfoStyles(colors: AppColors) {
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
scrollContent: {
|
scrollContent: {
|
||||||
paddingHorizontal: spacing.lg,
|
|
||||||
paddingBottom: spacing.xl * 2,
|
paddingBottom: spacing.xl * 2,
|
||||||
},
|
},
|
||||||
errorContainer: {
|
errorContainer: {
|
||||||
@@ -1177,16 +1097,17 @@ function createGroupInfoStyles(colors: AppColors) {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
|
|
||||||
// 头部区域 - 扁平化无卡片
|
// 头部区域 - QQ 风格简洁白底
|
||||||
headerCard: {
|
headerCard: {
|
||||||
backgroundColor: colors.background.paper,
|
backgroundColor: colors.background.paper,
|
||||||
|
paddingHorizontal: spacing.lg,
|
||||||
paddingVertical: spacing.lg,
|
paddingVertical: spacing.lg,
|
||||||
marginBottom: spacing.md,
|
marginBottom: spacing.md,
|
||||||
},
|
},
|
||||||
groupHeader: {
|
groupHeader: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginBottom: spacing.lg,
|
marginBottom: spacing.md,
|
||||||
},
|
},
|
||||||
groupInfo: {
|
groupInfo: {
|
||||||
marginLeft: spacing.lg,
|
marginLeft: spacing.lg,
|
||||||
@@ -1194,7 +1115,7 @@ function createGroupInfoStyles(colors: AppColors) {
|
|||||||
},
|
},
|
||||||
groupName: {
|
groupName: {
|
||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
fontSize: fontSizes['2xl'],
|
fontSize: fontSizes.xl,
|
||||||
marginBottom: spacing.xs,
|
marginBottom: spacing.xs,
|
||||||
},
|
},
|
||||||
groupMeta: {
|
groupMeta: {
|
||||||
@@ -1209,86 +1130,68 @@ function createGroupInfoStyles(colors: AppColors) {
|
|||||||
gap: spacing.sm,
|
gap: spacing.sm,
|
||||||
},
|
},
|
||||||
copyGroupNoBtn: {
|
copyGroupNoBtn: {
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
paddingHorizontal: spacing.sm,
|
paddingHorizontal: spacing.sm,
|
||||||
paddingVertical: 4,
|
paddingVertical: 2,
|
||||||
borderRadius: borderRadius.md,
|
borderRadius: borderRadius.sm,
|
||||||
backgroundColor: colors.primary.light + '15',
|
backgroundColor: colors.background.default,
|
||||||
},
|
},
|
||||||
copyGroupNoBtnText: {
|
copyGroupNoBtnText: {
|
||||||
marginLeft: 4,
|
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
},
|
},
|
||||||
descriptionContainer: {
|
descriptionContainer: {
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'flex-start',
|
|
||||||
backgroundColor: colors.background.default,
|
backgroundColor: colors.background.default,
|
||||||
borderRadius: borderRadius.lg,
|
borderRadius: borderRadius.md,
|
||||||
padding: spacing.md,
|
padding: spacing.md,
|
||||||
},
|
},
|
||||||
groupDesc: {
|
groupDesc: {
|
||||||
marginLeft: spacing.sm,
|
|
||||||
flex: 1,
|
flex: 1,
|
||||||
lineHeight: 22,
|
lineHeight: 22,
|
||||||
fontWeight: '400',
|
fontWeight: '400',
|
||||||
},
|
},
|
||||||
|
|
||||||
// 通用卡片样式 - 扁平化:无阴影无圆角,使用分割线
|
// 通用卡片样式 - 白底分隔(QQ 风格)
|
||||||
card: {
|
card: {
|
||||||
backgroundColor: colors.background.paper,
|
backgroundColor: colors.background.paper,
|
||||||
marginBottom: spacing.md,
|
marginBottom: spacing.md,
|
||||||
|
paddingHorizontal: spacing.lg,
|
||||||
|
paddingVertical: spacing.sm,
|
||||||
},
|
},
|
||||||
cardHeader: {
|
cardHeader: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginBottom: spacing.md,
|
justifyContent: 'space-between',
|
||||||
paddingTop: spacing.sm,
|
paddingVertical: spacing.md,
|
||||||
},
|
},
|
||||||
cardIconContainer: {
|
cardValueRow: {
|
||||||
width: 36,
|
flexDirection: 'row',
|
||||||
height: 36,
|
|
||||||
borderRadius: borderRadius.md,
|
|
||||||
backgroundColor: colors.info.light + '15',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginRight: spacing.sm,
|
|
||||||
},
|
|
||||||
cardTitle: {
|
|
||||||
fontWeight: '600',
|
|
||||||
fontSize: fontSizes.md,
|
|
||||||
flex: 1,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 公告样式 - 扁平化
|
// 公告样式 - QQ 风格:标题行 + 内容预览
|
||||||
announcementContent: {
|
announcementRow: {
|
||||||
backgroundColor: colors.warning.light + '08',
|
paddingVertical: spacing.md,
|
||||||
borderRadius: borderRadius.lg,
|
|
||||||
padding: spacing.md,
|
|
||||||
borderLeftWidth: 3,
|
|
||||||
borderLeftColor: colors.warning.main,
|
|
||||||
},
|
},
|
||||||
announcementText: {
|
announcementHeader: {
|
||||||
lineHeight: 22,
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
marginBottom: spacing.sm,
|
marginBottom: spacing.sm,
|
||||||
fontWeight: '400',
|
|
||||||
},
|
},
|
||||||
announcementTime: {
|
announcementPreviewText: {
|
||||||
textAlign: 'right',
|
lineHeight: 22,
|
||||||
fontWeight: '500',
|
fontWeight: '400',
|
||||||
},
|
},
|
||||||
|
|
||||||
// 成员预览样式
|
// 成员预览样式
|
||||||
memberCount: {
|
memberCount: {
|
||||||
marginRight: spacing.xs,
|
marginRight: spacing.xs,
|
||||||
fontWeight: '600',
|
fontWeight: '500',
|
||||||
color: colors.text.secondary,
|
|
||||||
},
|
},
|
||||||
memberPreview: {
|
memberPreview: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
flexWrap: 'nowrap',
|
flexWrap: 'nowrap',
|
||||||
marginBottom: spacing.md,
|
paddingVertical: spacing.md,
|
||||||
paddingLeft: spacing.xs,
|
paddingLeft: spacing.xs,
|
||||||
},
|
},
|
||||||
memberAvatar: {
|
memberAvatar: {
|
||||||
@@ -1320,8 +1223,6 @@ function createGroupInfoStyles(colors: AppColors) {
|
|||||||
backgroundColor: colors.background.default,
|
backgroundColor: colors.background.default,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: colors.divider + '60',
|
|
||||||
marginLeft: -8,
|
marginLeft: -8,
|
||||||
paddingHorizontal: spacing.xs,
|
paddingHorizontal: spacing.xs,
|
||||||
},
|
},
|
||||||
@@ -1331,88 +1232,81 @@ function createGroupInfoStyles(colors: AppColors) {
|
|||||||
fontSize: fontSizes.sm,
|
fontSize: fontSizes.sm,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 邀请按钮样式 - 扁平化
|
// 邀请按钮样式
|
||||||
inviteButton: {
|
inviteButton: {
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
paddingVertical: spacing.md,
|
paddingVertical: spacing.md,
|
||||||
borderRadius: borderRadius.lg,
|
borderRadius: borderRadius.md,
|
||||||
backgroundColor: colors.primary.light + '10',
|
backgroundColor: colors.background.default,
|
||||||
borderWidth: 1.5,
|
marginTop: spacing.sm,
|
||||||
borderColor: colors.primary.light + '60',
|
|
||||||
borderStyle: 'dashed',
|
|
||||||
},
|
|
||||||
inviteIconContainer: {
|
|
||||||
width: 32,
|
|
||||||
height: 32,
|
|
||||||
borderRadius: 16,
|
|
||||||
backgroundColor: colors.primary.light + '20',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
marginRight: spacing.sm,
|
|
||||||
},
|
},
|
||||||
inviteButtonText: {
|
inviteButtonText: {
|
||||||
fontWeight: '700',
|
fontWeight: '600',
|
||||||
marginRight: spacing.xs,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 设置项样式 - 扁平化:无圆角背景,简洁分割线
|
// 设置项样式 - QQ 风格:无图标,标题在左,值/箭头在右
|
||||||
settingsList: {
|
settingsList: {
|
||||||
marginTop: 0,
|
marginTop: 0,
|
||||||
},
|
},
|
||||||
settingItem: {
|
settingItem: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
paddingVertical: spacing.md,
|
paddingVertical: spacing.md,
|
||||||
paddingHorizontal: 0,
|
|
||||||
marginLeft: 0,
|
|
||||||
marginRight: 0,
|
|
||||||
},
|
},
|
||||||
settingIconContainer: {
|
settingItemLeft: {
|
||||||
width: 36,
|
|
||||||
height: 36,
|
|
||||||
borderRadius: borderRadius.md,
|
|
||||||
backgroundColor: colors.background.default,
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
marginRight: spacing.md,
|
|
||||||
},
|
|
||||||
settingIconDanger: {
|
|
||||||
backgroundColor: colors.error.light + '15',
|
|
||||||
},
|
|
||||||
settingContent: {
|
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
settingItemTitle: {
|
||||||
|
fontSize: fontSizes.md,
|
||||||
|
fontWeight: '500',
|
||||||
|
color: colors.text.primary,
|
||||||
|
},
|
||||||
|
settingItemSubtitle: {
|
||||||
|
marginTop: spacing.xs,
|
||||||
|
fontWeight: '400',
|
||||||
|
lineHeight: 20,
|
||||||
|
},
|
||||||
|
settingValueRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginLeft: spacing.md,
|
||||||
|
},
|
||||||
|
settingValueText: {
|
||||||
|
maxWidth: 180,
|
||||||
|
},
|
||||||
|
settingChevron: {
|
||||||
|
fontSize: fontSizes.xl,
|
||||||
|
marginLeft: spacing.xs,
|
||||||
|
fontWeight: '300',
|
||||||
},
|
},
|
||||||
dangerText: {
|
dangerText: {
|
||||||
color: colors.error.main,
|
color: colors.error.main,
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
},
|
},
|
||||||
settingDivider: {
|
settingDivider: {
|
||||||
marginLeft: 52,
|
|
||||||
width: 'auto',
|
|
||||||
marginVertical: 0,
|
marginVertical: 0,
|
||||||
opacity: 0.5,
|
opacity: 0.5,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 危险操作区域 - 扁平化
|
// 危险操作区域
|
||||||
dangerCard: {
|
dangerCard: {
|
||||||
padding: 0,
|
padding: 0,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
marginTop: spacing.sm,
|
marginTop: 0,
|
||||||
},
|
},
|
||||||
dangerButton: {
|
dangerButton: {
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
paddingVertical: spacing.lg,
|
paddingVertical: spacing.lg,
|
||||||
gap: spacing.sm,
|
|
||||||
},
|
},
|
||||||
dangerButtonText: {
|
dangerButtonText: {
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
},
|
},
|
||||||
|
|
||||||
// 模态框样式 - 扁平化
|
// 模态框样式
|
||||||
modalOverlay: {
|
modalOverlay: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.45)',
|
backgroundColor: 'rgba(0, 0, 0, 0.45)',
|
||||||
@@ -1436,10 +1330,6 @@ function createGroupInfoStyles(colors: AppColors) {
|
|||||||
borderBottomWidth: 0.5,
|
borderBottomWidth: 0.5,
|
||||||
borderBottomColor: colors.divider + '60',
|
borderBottomColor: colors.divider + '60',
|
||||||
},
|
},
|
||||||
modalHeaderButton: {
|
|
||||||
paddingVertical: spacing.sm,
|
|
||||||
minWidth: 60,
|
|
||||||
},
|
|
||||||
modalTitle: {
|
modalTitle: {
|
||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
fontSize: fontSizes.xl,
|
fontSize: fontSizes.xl,
|
||||||
@@ -1447,12 +1337,8 @@ function createGroupInfoStyles(colors: AppColors) {
|
|||||||
saveButton: {
|
saveButton: {
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
},
|
},
|
||||||
confirmButton: {
|
|
||||||
fontWeight: '600',
|
|
||||||
textAlign: 'right',
|
|
||||||
},
|
|
||||||
|
|
||||||
// 编辑表单样式 - 扁平化
|
// 编辑表单样式
|
||||||
editForm: {
|
editForm: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
@@ -1481,11 +1367,9 @@ function createGroupInfoStyles(colors: AppColors) {
|
|||||||
bottom: 0,
|
bottom: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
backgroundColor: colors.primary.main,
|
backgroundColor: colors.primary.main,
|
||||||
width: 32,
|
paddingHorizontal: spacing.sm,
|
||||||
height: 32,
|
paddingVertical: 4,
|
||||||
borderRadius: 16,
|
borderRadius: borderRadius.md,
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
borderWidth: 3,
|
borderWidth: 3,
|
||||||
borderColor: colors.background.paper,
|
borderColor: colors.background.paper,
|
||||||
},
|
},
|
||||||
@@ -1542,36 +1426,25 @@ function createGroupInfoStyles(colors: AppColors) {
|
|||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
},
|
},
|
||||||
|
|
||||||
// 加群方式样式 - 更现代的选中状态
|
// 加群方式样式
|
||||||
joinTypeList: {
|
joinTypeList: {
|
||||||
gap: spacing.md,
|
gap: spacing.md,
|
||||||
},
|
},
|
||||||
joinTypeItem: {
|
joinTypeItem: {
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
padding: spacing.md,
|
padding: spacing.md,
|
||||||
borderRadius: borderRadius.lg,
|
borderRadius: borderRadius.lg,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: colors.divider + '80',
|
borderColor: colors.divider + '80',
|
||||||
backgroundColor: colors.background.default,
|
backgroundColor: colors.background.default,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
},
|
},
|
||||||
joinTypeItemSelected: {
|
joinTypeItemSelected: {
|
||||||
borderColor: colors.primary.main,
|
borderColor: colors.primary.main,
|
||||||
backgroundColor: colors.primary.light + '12',
|
backgroundColor: colors.primary.light + '12',
|
||||||
borderWidth: 1.5,
|
borderWidth: 1.5,
|
||||||
},
|
},
|
||||||
joinTypeIcon: {
|
|
||||||
width: 48,
|
|
||||||
height: 48,
|
|
||||||
borderRadius: borderRadius.lg,
|
|
||||||
backgroundColor: colors.background.paper,
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
marginRight: spacing.md,
|
|
||||||
},
|
|
||||||
joinTypeIconSelected: {
|
|
||||||
backgroundColor: colors.primary.light + '25',
|
|
||||||
},
|
|
||||||
joinTypeInfo: {
|
joinTypeInfo: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
@@ -1579,6 +1452,10 @@ function createGroupInfoStyles(colors: AppColors) {
|
|||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
color: colors.primary.main,
|
color: colors.primary.main,
|
||||||
},
|
},
|
||||||
|
joinTypeCheck: {
|
||||||
|
fontSize: fontSizes.lg,
|
||||||
|
fontWeight: '700',
|
||||||
|
},
|
||||||
|
|
||||||
// 转让群主样式
|
// 转让群主样式
|
||||||
transferDesc: {
|
transferDesc: {
|
||||||
@@ -1632,79 +1509,6 @@ function createGroupInfoStyles(colors: AppColors) {
|
|||||||
transferButton: {
|
transferButton: {
|
||||||
marginTop: spacing.sm,
|
marginTop: spacing.sm,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 邀请成员模态框样式
|
|
||||||
tabContainer: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
backgroundColor: colors.background.default,
|
|
||||||
borderRadius: borderRadius.lg,
|
|
||||||
padding: spacing.xs,
|
|
||||||
marginBottom: spacing.md,
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: colors.divider + '60',
|
|
||||||
},
|
|
||||||
tab: {
|
|
||||||
flex: 1,
|
|
||||||
paddingVertical: spacing.sm,
|
|
||||||
alignItems: 'center',
|
|
||||||
borderRadius: borderRadius.md,
|
|
||||||
},
|
|
||||||
tabActive: {
|
|
||||||
backgroundColor: colors.background.paper,
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: colors.divider + '60',
|
|
||||||
},
|
|
||||||
tabTextActive: {
|
|
||||||
fontWeight: '700',
|
|
||||||
},
|
|
||||||
selectedBadge: {
|
|
||||||
backgroundColor: colors.primary.light + '20',
|
|
||||||
paddingHorizontal: spacing.md,
|
|
||||||
paddingVertical: spacing.xs,
|
|
||||||
borderRadius: borderRadius.sm,
|
|
||||||
alignSelf: 'flex-start',
|
|
||||||
marginBottom: spacing.md,
|
|
||||||
},
|
|
||||||
friendListContent: {
|
|
||||||
paddingBottom: spacing.xl,
|
|
||||||
},
|
|
||||||
friendItem: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
paddingVertical: spacing.md,
|
|
||||||
paddingHorizontal: spacing.sm,
|
|
||||||
borderRadius: borderRadius.lg,
|
|
||||||
marginBottom: spacing.sm,
|
|
||||||
backgroundColor: colors.background.default,
|
|
||||||
},
|
|
||||||
friendItemSelected: {
|
|
||||||
backgroundColor: colors.primary.light + '12',
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: colors.primary.light + '60',
|
|
||||||
},
|
|
||||||
friendInfo: {
|
|
||||||
flex: 1,
|
|
||||||
marginLeft: spacing.md,
|
|
||||||
marginRight: spacing.sm,
|
|
||||||
},
|
|
||||||
nickname: {
|
|
||||||
fontWeight: '600',
|
|
||||||
marginBottom: 2,
|
|
||||||
},
|
|
||||||
checkbox: {
|
|
||||||
width: 26,
|
|
||||||
height: 26,
|
|
||||||
borderRadius: 13,
|
|
||||||
borderWidth: 2,
|
|
||||||
borderColor: colors.divider,
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
backgroundColor: colors.background.paper,
|
|
||||||
},
|
|
||||||
checkboxSelected: {
|
|
||||||
backgroundColor: colors.primary.main,
|
|
||||||
borderColor: colors.primary.main,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,10 @@
|
|||||||
* 显示群成员列表,支持管理员管理成员
|
* 显示群成员列表,支持管理员管理成员
|
||||||
* 支持响应式网格布局
|
* 支持响应式网格布局
|
||||||
* 使用游标分页
|
* 使用游标分页
|
||||||
|
* QQ 风格:移除装饰性图标,使用简洁的文字列表
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
@@ -22,7 +23,6 @@ import {
|
|||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
import { useLocalSearchParams, useRouter } from 'expo-router';
|
import { useLocalSearchParams, useRouter } from 'expo-router';
|
||||||
import { hrefUserProfile } from '../../navigation/hrefs';
|
import { hrefUserProfile } from '../../navigation/hrefs';
|
||||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
|
||||||
import { blurActiveElement } from '../../infrastructure/platform';
|
import { blurActiveElement } from '../../infrastructure/platform';
|
||||||
import { spacing, fontSizes, borderRadius, useAppColors, type AppColors } from '../../theme';
|
import { spacing, fontSizes, borderRadius, useAppColors, type AppColors } from '../../theme';
|
||||||
import { useAuthStore } from '../../stores';
|
import { useAuthStore } from '../../stores';
|
||||||
@@ -33,8 +33,8 @@ import {
|
|||||||
ICursorGroupMemberListPagedSource,
|
ICursorGroupMemberListPagedSource,
|
||||||
} from '../../stores/group';
|
} from '../../stores/group';
|
||||||
import { enrichGroupMembersWithUserProfiles } from '../../stores/group';
|
import { enrichGroupMembersWithUserProfiles } from '../../stores/group';
|
||||||
import { Avatar, Text, Button, Loading, EmptyState, Divider, ResponsiveContainer, AppBackButton } from '../../components/common';
|
import { Avatar, Text, Button, Loading, EmptyState, Divider, AppBackButton } from '../../components/common';
|
||||||
import { useResponsive, useBreakpointGTE } from '../../hooks';
|
import { useResponsive } from '../../hooks';
|
||||||
import { useCursorPagination } from '../../hooks/useCursorPagination';
|
import { useCursorPagination } from '../../hooks/useCursorPagination';
|
||||||
import {
|
import {
|
||||||
GroupMemberResponse,
|
GroupMemberResponse,
|
||||||
@@ -44,13 +44,6 @@ import { firstRouteParam } from '../../navigation/paramUtils';
|
|||||||
const { width: SCREEN_WIDTH } = Dimensions.get('window');
|
const { width: SCREEN_WIDTH } = Dimensions.get('window');
|
||||||
const GROUP_MEMBER_REMOTE_LIST_KIND: GroupMemberListSourceKind = 'cursor';
|
const GROUP_MEMBER_REMOTE_LIST_KIND: GroupMemberListSourceKind = 'cursor';
|
||||||
|
|
||||||
// 网格布局配置
|
|
||||||
const GRID_CONFIG = {
|
|
||||||
mobile: { columns: 1, itemWidth: '100%' },
|
|
||||||
tablet: { columns: 2, itemWidth: '48%' },
|
|
||||||
desktop: { columns: 3, itemWidth: '31%' },
|
|
||||||
};
|
|
||||||
|
|
||||||
// 成员分组
|
// 成员分组
|
||||||
interface MemberGroup {
|
interface MemberGroup {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -66,15 +59,7 @@ const GroupMembersScreen: React.FC = () => {
|
|||||||
const { currentUser } = useAuthStore();
|
const { currentUser } = useAuthStore();
|
||||||
|
|
||||||
// 响应式布局
|
// 响应式布局
|
||||||
const { isDesktop, isTablet, width } = useResponsive();
|
const { width } = useResponsive();
|
||||||
const isWideScreen = useBreakpointGTE('lg');
|
|
||||||
|
|
||||||
// 计算网格列数
|
|
||||||
const gridConfig = useMemo(() => {
|
|
||||||
if (width >= 1024) return GRID_CONFIG.desktop;
|
|
||||||
if (width >= 768) return GRID_CONFIG.tablet;
|
|
||||||
return GRID_CONFIG.mobile;
|
|
||||||
}, [width]);
|
|
||||||
|
|
||||||
const memberListSource = useMemo<ICursorGroupMemberListPagedSource>(() => {
|
const memberListSource = useMemo<ICursorGroupMemberListPagedSource>(() => {
|
||||||
return createCursorGroupMemberListSource(GROUP_MEMBER_REMOTE_LIST_KIND, groupId, 50);
|
return createCursorGroupMemberListSource(GROUP_MEMBER_REMOTE_LIST_KIND, groupId, 50);
|
||||||
@@ -431,17 +416,17 @@ const GroupMembersScreen: React.FC = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
{item.muted && (
|
{item.muted && (
|
||||||
<View style={styles.mutedBadge}>
|
<View style={styles.mutedBadge}>
|
||||||
<MaterialCommunityIcons name="microphone-off" size={12} color={colors.error.main} />
|
|
||||||
<Text variant="label" color={colors.error.main}>禁言中</Text>
|
<Text variant="label" color={colors.error.main}>禁言中</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
{canManage && (
|
{canManage && (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
|
style={styles.moreButton}
|
||||||
onPress={() => openActionModal(item)}
|
onPress={() => openActionModal(item)}
|
||||||
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
||||||
>
|
>
|
||||||
<MaterialCommunityIcons name="dots-vertical" size={20} color={colors.text.hint} />
|
<Text variant="body" color={colors.text.hint} style={styles.moreButtonText}>···</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
@@ -468,7 +453,6 @@ const GroupMembersScreen: React.FC = () => {
|
|||||||
<EmptyState
|
<EmptyState
|
||||||
title="暂无成员"
|
title="暂无成员"
|
||||||
description="群组还没有成员"
|
description="群组还没有成员"
|
||||||
icon="account-group-outline"
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -509,11 +493,6 @@ const GroupMembersScreen: React.FC = () => {
|
|||||||
onPress={handleToggleAdmin}
|
onPress={handleToggleAdmin}
|
||||||
disabled={actionLoading}
|
disabled={actionLoading}
|
||||||
>
|
>
|
||||||
<MaterialCommunityIcons
|
|
||||||
name={selectedMember.role === 'admin' ? 'account-remove' : 'account-plus'}
|
|
||||||
size={22}
|
|
||||||
color={colors.text.primary}
|
|
||||||
/>
|
|
||||||
<Text variant="body" style={styles.actionText}>
|
<Text variant="body" style={styles.actionText}>
|
||||||
{selectedMember.role === 'admin' ? '取消管理员' : '设为管理员'}
|
{selectedMember.role === 'admin' ? '取消管理员' : '设为管理员'}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -526,11 +505,6 @@ const GroupMembersScreen: React.FC = () => {
|
|||||||
onPress={handleToggleMute}
|
onPress={handleToggleMute}
|
||||||
disabled={actionLoading}
|
disabled={actionLoading}
|
||||||
>
|
>
|
||||||
<MaterialCommunityIcons
|
|
||||||
name={selectedMember.muted ? 'microphone' : 'microphone-off'}
|
|
||||||
size={22}
|
|
||||||
color={selectedMember.muted ? colors.success.main : colors.error.main}
|
|
||||||
/>
|
|
||||||
<Text
|
<Text
|
||||||
variant="body"
|
variant="body"
|
||||||
color={selectedMember.muted ? colors.success.main : colors.error.main}
|
color={selectedMember.muted ? colors.success.main : colors.error.main}
|
||||||
@@ -546,7 +520,6 @@ const GroupMembersScreen: React.FC = () => {
|
|||||||
onPress={handleRemoveMember}
|
onPress={handleRemoveMember}
|
||||||
disabled={actionLoading}
|
disabled={actionLoading}
|
||||||
>
|
>
|
||||||
<MaterialCommunityIcons name="account-remove" size={22} color={colors.error.main} />
|
|
||||||
<Text variant="body" color={colors.error.main} style={styles.actionText}>
|
<Text variant="body" color={colors.error.main} style={styles.actionText}>
|
||||||
移除成员
|
移除成员
|
||||||
</Text>
|
</Text>
|
||||||
@@ -690,21 +663,20 @@ function createGroupMembersStyles(colors: AppColors) {
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: colors.background.default,
|
backgroundColor: colors.background.default,
|
||||||
},
|
},
|
||||||
// Header 样式 - 扁平化
|
// Header 样式
|
||||||
pageHeader: {
|
pageHeader: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
paddingHorizontal: spacing.md,
|
paddingHorizontal: spacing.md,
|
||||||
paddingVertical: spacing.md,
|
paddingVertical: spacing.sm,
|
||||||
backgroundColor: colors.background.paper,
|
backgroundColor: colors.background.paper,
|
||||||
borderBottomWidth: 0.5,
|
borderBottomWidth: 0.5,
|
||||||
borderBottomColor: colors.divider + '60',
|
borderBottomColor: colors.divider + '60',
|
||||||
},
|
},
|
||||||
pageTitle: {
|
pageTitle: {
|
||||||
fontWeight: '800',
|
fontWeight: '600',
|
||||||
fontSize: fontSizes.xl + 1,
|
fontSize: fontSizes.xl,
|
||||||
letterSpacing: 0.5,
|
|
||||||
},
|
},
|
||||||
pageHeaderPlaceholder: {
|
pageHeaderPlaceholder: {
|
||||||
width: 40,
|
width: 40,
|
||||||
@@ -712,6 +684,7 @@ function createGroupMembersStyles(colors: AppColors) {
|
|||||||
},
|
},
|
||||||
section: {
|
section: {
|
||||||
marginBottom: spacing.md,
|
marginBottom: spacing.md,
|
||||||
|
backgroundColor: colors.background.paper,
|
||||||
},
|
},
|
||||||
sectionHeader: {
|
sectionHeader: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
@@ -720,8 +693,6 @@ function createGroupMembersStyles(colors: AppColors) {
|
|||||||
paddingHorizontal: spacing.lg,
|
paddingHorizontal: spacing.lg,
|
||||||
paddingVertical: spacing.md,
|
paddingVertical: spacing.md,
|
||||||
backgroundColor: colors.background.default,
|
backgroundColor: colors.background.default,
|
||||||
borderTopWidth: 0.5,
|
|
||||||
borderTopColor: colors.divider + '40',
|
|
||||||
},
|
},
|
||||||
memberItem: {
|
memberItem: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
@@ -742,25 +713,31 @@ function createGroupMembersStyles(colors: AppColors) {
|
|||||||
marginBottom: 2,
|
marginBottom: 2,
|
||||||
},
|
},
|
||||||
memberName: {
|
memberName: {
|
||||||
fontWeight: '700',
|
fontWeight: '500',
|
||||||
fontSize: fontSizes.md + 1,
|
fontSize: fontSizes.md,
|
||||||
letterSpacing: 0.3,
|
|
||||||
},
|
},
|
||||||
roleBadge: {
|
roleBadge: {
|
||||||
paddingHorizontal: spacing.sm,
|
paddingHorizontal: spacing.sm,
|
||||||
paddingVertical: 2,
|
paddingVertical: 2,
|
||||||
borderRadius: borderRadius.sm,
|
borderRadius: borderRadius.sm,
|
||||||
marginLeft: spacing.sm,
|
marginLeft: spacing.sm,
|
||||||
fontWeight: '800',
|
|
||||||
fontSize: fontSizes.xs,
|
|
||||||
color: colors.background.paper,
|
|
||||||
},
|
},
|
||||||
mutedBadge: {
|
mutedBadge: {
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
marginTop: 2,
|
marginTop: 2,
|
||||||
},
|
},
|
||||||
// 模态框样式 - 更现代
|
moreButton: {
|
||||||
|
paddingHorizontal: spacing.sm,
|
||||||
|
paddingVertical: spacing.xs,
|
||||||
|
minWidth: 36,
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
moreButtonText: {
|
||||||
|
fontSize: fontSizes['2xl'],
|
||||||
|
fontWeight: '700',
|
||||||
|
lineHeight: fontSizes['2xl'],
|
||||||
|
color: colors.text.hint,
|
||||||
|
},
|
||||||
|
// 模态框样式
|
||||||
modalOverlay: {
|
modalOverlay: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.45)',
|
backgroundColor: 'rgba(0, 0, 0, 0.45)',
|
||||||
@@ -781,32 +758,25 @@ function createGroupMembersStyles(colors: AppColors) {
|
|||||||
borderBottomColor: colors.divider + '60',
|
borderBottomColor: colors.divider + '60',
|
||||||
},
|
},
|
||||||
modalTitle: {
|
modalTitle: {
|
||||||
fontWeight: '800',
|
fontWeight: '700',
|
||||||
fontSize: fontSizes.xl + 1,
|
fontSize: fontSizes.xl,
|
||||||
marginTop: spacing.sm,
|
marginTop: spacing.sm,
|
||||||
marginBottom: spacing.xs,
|
marginBottom: spacing.xs,
|
||||||
letterSpacing: 0.5,
|
|
||||||
},
|
},
|
||||||
actionItem: {
|
actionItem: {
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
paddingVertical: spacing.md,
|
paddingVertical: spacing.md,
|
||||||
paddingHorizontal: spacing.sm,
|
|
||||||
marginLeft: -spacing.sm,
|
|
||||||
marginRight: -spacing.sm,
|
|
||||||
borderRadius: borderRadius.md,
|
|
||||||
borderBottomWidth: 0.5,
|
borderBottomWidth: 0.5,
|
||||||
borderBottomColor: colors.divider + '40',
|
borderBottomColor: colors.divider + '40',
|
||||||
},
|
},
|
||||||
actionText: {
|
actionText: {
|
||||||
marginLeft: spacing.md,
|
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
fontSize: fontSizes.md,
|
fontSize: fontSizes.md,
|
||||||
},
|
},
|
||||||
inputLabel: {
|
inputLabel: {
|
||||||
marginBottom: spacing.xs,
|
marginBottom: spacing.xs,
|
||||||
fontWeight: '700',
|
fontWeight: '600',
|
||||||
fontSize: fontSizes.sm + 1,
|
fontSize: fontSizes.sm,
|
||||||
},
|
},
|
||||||
input: {
|
input: {
|
||||||
backgroundColor: colors.background.default,
|
backgroundColor: colors.background.default,
|
||||||
@@ -815,7 +785,7 @@ function createGroupMembersStyles(colors: AppColors) {
|
|||||||
paddingVertical: spacing.md,
|
paddingVertical: spacing.md,
|
||||||
fontSize: fontSizes.md,
|
fontSize: fontSizes.md,
|
||||||
color: colors.text.primary,
|
color: colors.text.primary,
|
||||||
borderWidth: 1.5,
|
borderWidth: 1,
|
||||||
borderColor: colors.divider + '80',
|
borderColor: colors.divider + '80',
|
||||||
marginBottom: spacing.md,
|
marginBottom: spacing.md,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ import {
|
|||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
import { useRouter, useLocalSearchParams } from 'expo-router';
|
import { useRouter, useLocalSearchParams } from 'expo-router';
|
||||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
import { spacing, fontSizes, useAppColors, type AppColors } from '../../theme';
|
||||||
import { spacing, borderRadius, shadows, fontSizes, useAppColors, type AppColors } from '../../theme';
|
|
||||||
import { useAuthStore } from '../../stores';
|
import { useAuthStore } from '../../stores';
|
||||||
import { authService } from '@/services/auth';
|
import { authService } from '@/services/auth';
|
||||||
import { messageService } from '@/services/message';
|
import { messageService } from '@/services/message';
|
||||||
@@ -262,34 +261,28 @@ const PrivateChatInfoScreen: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 渲染设置项(带开关)
|
// 渲染开关设置项 - QQ 风格:左侧标题,右侧开关
|
||||||
const renderSwitchItem = (
|
const renderSwitchItem = (
|
||||||
icon: string,
|
|
||||||
title: string,
|
title: string,
|
||||||
value: boolean,
|
value: boolean,
|
||||||
onValueChange: () => void
|
onValueChange: () => void
|
||||||
) => (
|
) => (
|
||||||
<View style={styles.settingItem}>
|
<View style={styles.settingItem}>
|
||||||
<View style={styles.settingIconContainer}>
|
<Text variant="body" style={styles.settingTitle}>{title}</Text>
|
||||||
<MaterialCommunityIcons name={icon as any} size={20} color={colors.primary.main} />
|
|
||||||
</View>
|
|
||||||
<Text variant="body" style={styles.settingTitle}>
|
|
||||||
{title}
|
|
||||||
</Text>
|
|
||||||
<Switch
|
<Switch
|
||||||
value={value}
|
value={value}
|
||||||
onValueChange={onValueChange}
|
onValueChange={onValueChange}
|
||||||
trackColor={{ false: '#E0E0E0', true: colors.primary.light }}
|
trackColor={{ false: colors.divider, true: colors.primary.light }}
|
||||||
thumbColor={value ? colors.primary.main : colors.background.default}
|
thumbColor={value ? colors.primary.main : colors.background.paper}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
// 渲染设置项(带箭头)
|
// 渲染操作项 - QQ 风格:左侧标题,右侧值/箭头
|
||||||
const renderActionItem = (
|
const renderActionItem = (
|
||||||
icon: string,
|
|
||||||
title: string,
|
title: string,
|
||||||
onPress: () => void,
|
onPress: () => void,
|
||||||
|
value?: string,
|
||||||
danger: boolean = false
|
danger: boolean = false
|
||||||
) => (
|
) => (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
@@ -297,17 +290,17 @@ const PrivateChatInfoScreen: React.FC = () => {
|
|||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
activeOpacity={0.7}
|
activeOpacity={0.7}
|
||||||
>
|
>
|
||||||
<View style={[styles.settingIconContainer, danger && styles.settingIconDanger]}>
|
<Text variant="body" style={danger ? styles.dangerText : styles.settingTitle}>
|
||||||
<MaterialCommunityIcons
|
|
||||||
name={icon as any}
|
|
||||||
size={20}
|
|
||||||
color={danger ? colors.error.main : colors.primary.main}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<Text variant="body" style={danger ? [styles.settingTitle, styles.dangerText] : styles.settingTitle}>
|
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
<MaterialCommunityIcons name="chevron-right" size={22} color={colors.text.hint} />
|
<View style={styles.settingValueRow}>
|
||||||
|
{value && (
|
||||||
|
<Text variant="body" color={colors.text.secondary} numberOfLines={1} style={styles.settingValueText}>
|
||||||
|
{value}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
<Text variant="body" color={colors.text.hint} style={styles.settingChevron}>›</Text>
|
||||||
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -351,7 +344,7 @@ const PrivateChatInfoScreen: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
source={displayUser.avatar}
|
source={displayUser.avatar}
|
||||||
size={80}
|
size={72}
|
||||||
name={displayUser.nickname || ''}
|
name={displayUser.nickname || ''}
|
||||||
/>
|
/>
|
||||||
<View style={styles.userInfo}>
|
<View style={styles.userInfo}>
|
||||||
@@ -364,59 +357,53 @@ const PrivateChatInfoScreen: React.FC = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
<MaterialCommunityIcons name="chevron-right" size={24} color={colors.text.hint} />
|
<Text variant="body" color={colors.text.hint} style={styles.settingChevron}>›</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 聊天设置 */}
|
{/* 聊天设置 */}
|
||||||
<View style={styles.section}>
|
|
||||||
<Text variant="caption" color={colors.text.secondary} style={styles.sectionTitle}>
|
|
||||||
聊天设置
|
|
||||||
</Text>
|
|
||||||
<View style={styles.card}>
|
<View style={styles.card}>
|
||||||
{renderSwitchItem('bell-off-outline', '消息免打扰', isMuted, toggleMute)}
|
<View style={styles.settingsList}>
|
||||||
|
{renderSwitchItem('消息免打扰', isMuted, toggleMute)}
|
||||||
<Divider style={styles.divider} />
|
<Divider style={styles.divider} />
|
||||||
{renderSwitchItem('pin-outline', '置顶聊天', isPinned, togglePin)}
|
{renderSwitchItem('置顶聊天', isPinned, togglePin)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 聊天记录管理 */}
|
{/* 聊天记录管理 */}
|
||||||
<View style={styles.section}>
|
|
||||||
<Text variant="caption" color={colors.text.secondary} style={styles.sectionTitle}>
|
|
||||||
聊天记录
|
|
||||||
</Text>
|
|
||||||
<View style={styles.card}>
|
<View style={styles.card}>
|
||||||
{renderActionItem('magnify', '查找聊天记录', handleSearchMessages)}
|
<View style={styles.settingsList}>
|
||||||
|
{renderActionItem('查找聊天记录', handleSearchMessages)}
|
||||||
<Divider style={styles.divider} />
|
<Divider style={styles.divider} />
|
||||||
{renderActionItem('delete-sweep-outline', '清空聊天记录', handleClearHistory, true)}
|
{renderActionItem('清空聊天记录', handleClearHistory, undefined, true)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 其他操作 */}
|
{/* 其他操作 */}
|
||||||
<View style={styles.section}>
|
|
||||||
<Text variant="caption" color={colors.text.secondary} style={styles.sectionTitle}>
|
|
||||||
其他
|
|
||||||
</Text>
|
|
||||||
<View style={styles.card}>
|
<View style={styles.card}>
|
||||||
{renderActionItem('shield-alert-outline', '投诉', handleReport, true)}
|
<View style={styles.settingsList}>
|
||||||
|
{renderActionItem('投诉', handleReport, undefined, true)}
|
||||||
<Divider style={styles.divider} />
|
<Divider style={styles.divider} />
|
||||||
{renderActionItem(
|
{renderActionItem(
|
||||||
isBlocked ? 'account-check-outline' : 'account-cancel-outline',
|
|
||||||
isBlocked ? '取消拉黑' : '拉黑用户',
|
isBlocked ? '取消拉黑' : '拉黑用户',
|
||||||
handleBlockUser,
|
handleBlockUser,
|
||||||
|
undefined,
|
||||||
true
|
true
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 删除聊天按钮 */}
|
{/* 删除聊天按钮 */}
|
||||||
<View style={styles.section}>
|
<View style={[styles.card, styles.dangerCard]}>
|
||||||
<Button
|
<TouchableOpacity
|
||||||
title="删除并退出聊天"
|
style={styles.dangerButton}
|
||||||
onPress={handleDeleteAndExit}
|
onPress={handleDeleteAndExit}
|
||||||
variant="danger"
|
activeOpacity={0.7}
|
||||||
style={styles.deleteButton}
|
>
|
||||||
/>
|
<Text variant="body" color={colors.error.main} style={styles.dangerButtonText}>
|
||||||
|
删除并退出聊天
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
@@ -427,16 +414,17 @@ function createPrivateChatInfoStyles(colors: AppColors) {
|
|||||||
return StyleSheet.create({
|
return StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: colors.background.paper,
|
backgroundColor: colors.background.default,
|
||||||
},
|
},
|
||||||
pageHeader: {
|
pageHeader: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
paddingHorizontal: spacing.lg,
|
paddingHorizontal: spacing.md,
|
||||||
paddingVertical: spacing.md,
|
paddingVertical: spacing.sm,
|
||||||
backgroundColor: colors.background.paper,
|
backgroundColor: colors.background.paper,
|
||||||
borderBottomWidth: 0,
|
borderBottomWidth: 0.5,
|
||||||
|
borderBottomColor: colors.divider + '60',
|
||||||
},
|
},
|
||||||
pageTitle: {
|
pageTitle: {
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
@@ -450,11 +438,11 @@ function createPrivateChatInfoStyles(colors: AppColors) {
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
scrollContent: {
|
scrollContent: {
|
||||||
paddingHorizontal: spacing.lg,
|
paddingBottom: spacing.xl * 2,
|
||||||
paddingBottom: spacing.xl,
|
|
||||||
},
|
},
|
||||||
headerCard: {
|
headerCard: {
|
||||||
backgroundColor: colors.background.paper,
|
backgroundColor: colors.background.paper,
|
||||||
|
paddingHorizontal: spacing.lg,
|
||||||
paddingVertical: spacing.lg,
|
paddingVertical: spacing.lg,
|
||||||
marginBottom: spacing.md,
|
marginBottom: spacing.md,
|
||||||
},
|
},
|
||||||
@@ -468,57 +456,62 @@ function createPrivateChatInfoStyles(colors: AppColors) {
|
|||||||
},
|
},
|
||||||
userName: {
|
userName: {
|
||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
fontSize: fontSizes['2xl'],
|
fontSize: fontSizes.xl,
|
||||||
marginBottom: spacing.xs,
|
marginBottom: spacing.xs,
|
||||||
},
|
},
|
||||||
section: {
|
|
||||||
marginTop: spacing.lg,
|
|
||||||
},
|
|
||||||
sectionTitle: {
|
|
||||||
marginBottom: spacing.sm,
|
|
||||||
marginLeft: spacing.sm,
|
|
||||||
fontWeight: '500',
|
|
||||||
fontSize: fontSizes.sm,
|
|
||||||
color: colors.text.secondary,
|
|
||||||
},
|
|
||||||
card: {
|
card: {
|
||||||
backgroundColor: colors.background.paper,
|
backgroundColor: colors.background.paper,
|
||||||
overflow: 'hidden',
|
marginBottom: spacing.md,
|
||||||
|
paddingHorizontal: spacing.lg,
|
||||||
|
paddingVertical: spacing.sm,
|
||||||
|
},
|
||||||
|
settingsList: {
|
||||||
|
marginTop: 0,
|
||||||
},
|
},
|
||||||
settingItem: {
|
settingItem: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
paddingVertical: spacing.md,
|
paddingVertical: spacing.md,
|
||||||
paddingHorizontal: 0,
|
|
||||||
},
|
|
||||||
settingIconContainer: {
|
|
||||||
width: 36,
|
|
||||||
height: 36,
|
|
||||||
borderRadius: borderRadius.md,
|
|
||||||
backgroundColor: colors.background.default,
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
marginRight: spacing.md,
|
|
||||||
},
|
|
||||||
settingIconDanger: {
|
|
||||||
backgroundColor: colors.error.light + '15',
|
|
||||||
},
|
},
|
||||||
settingTitle: {
|
settingTitle: {
|
||||||
flex: 1,
|
fontSize: fontSizes.md,
|
||||||
|
fontWeight: '500',
|
||||||
|
color: colors.text.primary,
|
||||||
|
},
|
||||||
|
settingValueRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginLeft: spacing.md,
|
||||||
|
},
|
||||||
|
settingValueText: {
|
||||||
|
maxWidth: 180,
|
||||||
|
},
|
||||||
|
settingChevron: {
|
||||||
|
fontSize: fontSizes.xl,
|
||||||
|
marginLeft: spacing.xs,
|
||||||
|
fontWeight: '300',
|
||||||
},
|
},
|
||||||
dangerText: {
|
dangerText: {
|
||||||
color: colors.error.main,
|
color: colors.error.main,
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
},
|
},
|
||||||
divider: {
|
divider: {
|
||||||
marginLeft: 52,
|
|
||||||
width: 'auto',
|
|
||||||
marginVertical: 0,
|
marginVertical: 0,
|
||||||
|
opacity: 0.5,
|
||||||
},
|
},
|
||||||
deleteButton: {
|
dangerCard: {
|
||||||
marginTop: spacing.sm,
|
padding: 0,
|
||||||
borderRadius: borderRadius.lg,
|
overflow: 'hidden',
|
||||||
height: 52,
|
marginTop: 0,
|
||||||
|
},
|
||||||
|
dangerButton: {
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
paddingVertical: spacing.lg,
|
||||||
|
},
|
||||||
|
dangerButtonText: {
|
||||||
|
fontWeight: '600',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user