2026-03-09 21:29:03 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* GroupInfoScreen 群组信息/设置界面
|
|
|
|
|
|
* 显示群组详细信息,提供群组管理功能
|
|
|
|
|
|
* 支持响应式布局
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
|
|
|
|
|
import {
|
|
|
|
|
|
View,
|
|
|
|
|
|
StyleSheet,
|
|
|
|
|
|
ScrollView,
|
|
|
|
|
|
TouchableOpacity,
|
|
|
|
|
|
Alert,
|
|
|
|
|
|
Clipboard,
|
|
|
|
|
|
TextInput,
|
|
|
|
|
|
Modal,
|
|
|
|
|
|
Switch,
|
|
|
|
|
|
KeyboardAvoidingView,
|
|
|
|
|
|
Platform,
|
|
|
|
|
|
Image,
|
|
|
|
|
|
Dimensions,
|
|
|
|
|
|
} from 'react-native';
|
|
|
|
|
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
2026-03-24 14:21:31 +08:00
|
|
|
|
import { useFocusEffect } from '@react-navigation/native';
|
|
|
|
|
|
import { useLocalSearchParams, useRouter } from 'expo-router';
|
2026-03-09 21:29:03 +08:00
|
|
|
|
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
|
|
|
|
|
import * as ImagePicker from 'expo-image-picker';
|
2026-03-25 05:16:54 +08:00
|
|
|
|
import {
|
|
|
|
|
|
spacing,
|
|
|
|
|
|
fontSizes,
|
|
|
|
|
|
borderRadius,
|
|
|
|
|
|
shadows,
|
|
|
|
|
|
useAppColors,
|
|
|
|
|
|
type AppColors,
|
|
|
|
|
|
} from '../../theme';
|
2026-03-09 21:29:03 +08:00
|
|
|
|
import { useAuthStore } from '../../stores';
|
2026-04-13 01:30:37 +08:00
|
|
|
|
import { groupService } from '@/services/message';
|
|
|
|
|
|
import { uploadService } from '@/services/upload';
|
|
|
|
|
|
import { messageService } from '@/services/message';
|
2026-03-09 21:29:03 +08:00
|
|
|
|
import { Avatar, Text, Button, Loading, Divider } from '../../components/common';
|
|
|
|
|
|
import { useResponsive, useBreakpointGTE } from '../../hooks/useResponsive';
|
|
|
|
|
|
import {
|
|
|
|
|
|
GroupResponse,
|
|
|
|
|
|
GroupMemberResponse,
|
|
|
|
|
|
GroupAnnouncementResponse,
|
|
|
|
|
|
JoinType,
|
|
|
|
|
|
} from '../../types/dto';
|
|
|
|
|
|
import { User } from '../../types';
|
2026-04-11 22:35:11 +08:00
|
|
|
|
import { blurActiveElement } from '../../infrastructure/platform';
|
2026-03-24 14:21:31 +08:00
|
|
|
|
import { firstRouteParam } from '../../navigation/paramUtils';
|
|
|
|
|
|
import * as hrefs from '../../navigation/hrefs';
|
2026-04-13 04:56:58 +08:00
|
|
|
|
import { messageManager } from '../../stores/message';
|
2026-04-25 21:23:22 +08:00
|
|
|
|
import { useMessageStore } from '../../stores/message/store';
|
2026-04-13 04:56:58 +08:00
|
|
|
|
import { groupManager } from '../../stores/group';
|
2026-03-09 21:29:03 +08:00
|
|
|
|
import MutualFollowSelectorModal from './components/MutualFollowSelectorModal';
|
2026-03-24 15:39:28 +08:00
|
|
|
|
import { AppBackButton } from '../../components/common';
|
2026-03-09 21:29:03 +08:00
|
|
|
|
|
|
|
|
|
|
const { width: SCREEN_WIDTH } = Dimensions.get('window');
|
|
|
|
|
|
|
|
|
|
|
|
// 加群方式选项
|
|
|
|
|
|
const JOIN_TYPE_OPTIONS: { value: JoinType; label: string; icon: string; desc: string }[] = [
|
|
|
|
|
|
{ value: 0, label: '允许任何人加入', icon: 'earth', desc: '任何人都可以直接加入群聊' },
|
|
|
|
|
|
{ value: 1, label: '需要管理员审批', icon: 'shield-check', desc: '新成员需要管理员审核' },
|
|
|
|
|
|
{ value: 2, label: '不允许任何人加入', icon: 'lock', desc: '只能通过邀请加入群聊' },
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
const GroupInfoScreen: React.FC = () => {
|
2026-03-25 05:16:54 +08:00
|
|
|
|
const colors = useAppColors();
|
|
|
|
|
|
const styles = useMemo(() => createGroupInfoStyles(colors), [colors]);
|
2026-03-24 14:21:31 +08:00
|
|
|
|
const router = useRouter();
|
|
|
|
|
|
const { groupId: groupIdParam, conversationId: conversationIdParam } = useLocalSearchParams<{
|
|
|
|
|
|
groupId?: string | string[];
|
|
|
|
|
|
conversationId?: string | string[];
|
|
|
|
|
|
}>();
|
|
|
|
|
|
const groupId = firstRouteParam(groupIdParam) ?? '';
|
|
|
|
|
|
const conversationId = firstRouteParam(conversationIdParam);
|
2026-03-09 21:29:03 +08:00
|
|
|
|
const { currentUser } = useAuthStore();
|
|
|
|
|
|
|
|
|
|
|
|
// 响应式布局
|
|
|
|
|
|
const { isDesktop, isTablet, 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 [members, setMembers] = useState<GroupMemberResponse[]>([]);
|
|
|
|
|
|
const [announcements, setAnnouncements] = useState<GroupAnnouncementResponse[]>([]);
|
|
|
|
|
|
const [currentMember, setCurrentMember] = useState<GroupMemberResponse | null>(null);
|
|
|
|
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
|
|
const [isPinned, setIsPinned] = useState(false);
|
|
|
|
|
|
const [pinLoading, setPinLoading] = useState(false);
|
2026-04-25 21:23:22 +08:00
|
|
|
|
const isNotificationMuted = useMessageStore(state =>
|
|
|
|
|
|
conversationId ? (state.notificationMutedMap.get(conversationId) || false) : false
|
|
|
|
|
|
);
|
2026-03-09 21:29:03 +08:00
|
|
|
|
|
|
|
|
|
|
// 编辑模态框状态
|
|
|
|
|
|
const [editModalVisible, setEditModalVisible] = useState(false);
|
|
|
|
|
|
const [editName, setEditName] = useState('');
|
|
|
|
|
|
const [editDescription, setEditDescription] = useState('');
|
|
|
|
|
|
const [editAvatar, setEditAvatar] = useState<string>('');
|
|
|
|
|
|
const [saving, setSaving] = useState(false);
|
|
|
|
|
|
const [uploadingAvatar, setUploadingAvatar] = useState(false);
|
|
|
|
|
|
|
|
|
|
|
|
// 群公告模态框状态
|
|
|
|
|
|
const [announcementModalVisible, setAnnouncementModalVisible] = useState(false);
|
|
|
|
|
|
const [newAnnouncement, setNewAnnouncement] = useState('');
|
|
|
|
|
|
|
|
|
|
|
|
// 转让群主模态框状态
|
|
|
|
|
|
const [transferModalVisible, setTransferModalVisible] = useState(false);
|
|
|
|
|
|
const [transferUserId, setTransferUserId] = useState<string>('');
|
|
|
|
|
|
|
|
|
|
|
|
// 加群方式模态框状态
|
|
|
|
|
|
const [joinTypeModalVisible, setJoinTypeModalVisible] = useState(false);
|
|
|
|
|
|
|
|
|
|
|
|
// 邀请成员模态框状态
|
|
|
|
|
|
const [inviteModalVisible, setInviteModalVisible] = useState(false);
|
|
|
|
|
|
const [inviting, setInviting] = useState(false);
|
|
|
|
|
|
|
2026-04-04 08:01:45 +08:00
|
|
|
|
useEffect(() => {
|
2026-04-11 22:35:11 +08:00
|
|
|
|
if (editModalVisible || announcementModalVisible || transferModalVisible || joinTypeModalVisible || inviteModalVisible) {
|
|
|
|
|
|
blurActiveElement();
|
2026-04-04 08:01:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
}, [editModalVisible, announcementModalVisible, transferModalVisible, joinTypeModalVisible, inviteModalVisible]);
|
|
|
|
|
|
|
2026-03-09 21:29:03 +08:00
|
|
|
|
// 计算当前用户的角色
|
|
|
|
|
|
const isOwner = currentMember?.role === 'owner';
|
|
|
|
|
|
const isAdmin = currentMember?.role === 'admin' || isOwner;
|
|
|
|
|
|
|
|
|
|
|
|
// 加载群组信息
|
|
|
|
|
|
const loadGroupInfo = useCallback(async () => {
|
2026-03-24 14:21:31 +08:00
|
|
|
|
if (!groupId) return;
|
2026-03-09 21:29:03 +08:00
|
|
|
|
try {
|
|
|
|
|
|
setLoading(true);
|
|
|
|
|
|
|
|
|
|
|
|
// 并行加载群组信息和成员列表
|
|
|
|
|
|
const [groupData, membersData] = await Promise.all([
|
2026-03-10 12:58:23 +08:00
|
|
|
|
groupManager.getGroup(groupId, true),
|
|
|
|
|
|
groupManager.getMembers(groupId, 1, 100, true),
|
2026-03-09 21:29:03 +08:00
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
setGroup(groupData);
|
|
|
|
|
|
setMembers(membersData.list);
|
|
|
|
|
|
|
|
|
|
|
|
// 查找当前用户的成员信息
|
|
|
|
|
|
const myMember = membersData.list.find(m => m.user_id === currentUser?.id);
|
|
|
|
|
|
setCurrentMember(myMember || null);
|
|
|
|
|
|
|
|
|
|
|
|
// 加载群公告
|
|
|
|
|
|
try {
|
|
|
|
|
|
const announcementsData = await groupService.getAnnouncements(groupId, 1, 10);
|
|
|
|
|
|
setAnnouncements(announcementsData.list);
|
|
|
|
|
|
} catch (e) {
|
2026-03-09 22:18:47 +08:00
|
|
|
|
console.error('加载群公告失败:', e);
|
2026-03-09 21:29:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('加载群组信息失败:', error);
|
|
|
|
|
|
Alert.alert('错误', '加载群组信息失败', [
|
2026-03-24 14:21:31 +08:00
|
|
|
|
{ text: '确定', onPress: () => router.back() },
|
2026-03-09 21:29:03 +08:00
|
|
|
|
]);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setLoading(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [groupId, currentUser]);
|
|
|
|
|
|
|
|
|
|
|
|
// 加载会话置顶状态
|
|
|
|
|
|
const loadPinStatus = useCallback(async () => {
|
|
|
|
|
|
if (!conversationId) {
|
|
|
|
|
|
setIsPinned(false);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const conv = messageManager.getConversation(conversationId);
|
|
|
|
|
|
if (conv) {
|
|
|
|
|
|
setIsPinned(Boolean(conv.is_pinned));
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const detail = await messageService.getConversationById(conversationId);
|
|
|
|
|
|
setIsPinned(Boolean(detail.is_pinned));
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('加载置顶状态失败:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [conversationId]);
|
|
|
|
|
|
|
|
|
|
|
|
// 页面聚焦时刷新数据
|
|
|
|
|
|
useFocusEffect(
|
|
|
|
|
|
useCallback(() => {
|
|
|
|
|
|
loadGroupInfo();
|
|
|
|
|
|
loadPinStatus();
|
|
|
|
|
|
}, [loadGroupInfo, loadPinStatus])
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// 设置会话置顶
|
|
|
|
|
|
const handleTogglePinned = async () => {
|
|
|
|
|
|
if (!conversationId || pinLoading) return;
|
|
|
|
|
|
|
|
|
|
|
|
const nextValue = !isPinned;
|
|
|
|
|
|
setIsPinned(nextValue);
|
|
|
|
|
|
setPinLoading(true);
|
|
|
|
|
|
try {
|
|
|
|
|
|
await messageService.setConversationPinned(conversationId, nextValue);
|
|
|
|
|
|
messageManager.updateConversation(conversationId, { is_pinned: nextValue });
|
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
|
setIsPinned(!nextValue);
|
|
|
|
|
|
Alert.alert('错误', error?.message || '设置置顶失败');
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setPinLoading(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-04-25 21:23:22 +08:00
|
|
|
|
// 设置会话免打扰
|
|
|
|
|
|
const handleToggleNotificationMuted = async () => {
|
|
|
|
|
|
if (!conversationId) return;
|
|
|
|
|
|
try {
|
|
|
|
|
|
await messageManager.toggleNotificationMuted(conversationId, !isNotificationMuted);
|
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
|
Alert.alert('错误', error?.message || '设置免打扰失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-09 21:29:03 +08:00
|
|
|
|
// 打开编辑模态框
|
|
|
|
|
|
const openEditModal = () => {
|
|
|
|
|
|
if (group) {
|
|
|
|
|
|
setEditName(group.name);
|
|
|
|
|
|
setEditDescription(group.description || '');
|
|
|
|
|
|
setEditAvatar(group.avatar || '');
|
|
|
|
|
|
setEditModalVisible(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 选择群头像
|
|
|
|
|
|
const handleSelectGroupAvatar = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
|
|
|
|
|
|
if (status !== 'granted') {
|
|
|
|
|
|
Alert.alert('提示', '需要访问相册权限才能选择头像');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const result = await ImagePicker.launchImageLibraryAsync({
|
|
|
|
|
|
mediaTypes: 'images',
|
|
|
|
|
|
allowsEditing: true,
|
|
|
|
|
|
aspect: [1, 1],
|
|
|
|
|
|
quality: 0.8,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!result.canceled && result.assets && result.assets.length > 0) {
|
|
|
|
|
|
const selectedAsset = result.assets[0];
|
|
|
|
|
|
setUploadingAvatar(true);
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const uploadResult = await uploadService.uploadGroupAvatar({
|
|
|
|
|
|
uri: selectedAsset.uri,
|
|
|
|
|
|
type: selectedAsset.mimeType || 'image/jpeg',
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (uploadResult && uploadResult.url) {
|
|
|
|
|
|
setEditAvatar(uploadResult.url);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Alert.alert('上传失败', '头像上传失败,请重试');
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('上传头像失败:', error);
|
|
|
|
|
|
Alert.alert('上传失败', '头像上传失败,请重试');
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setUploadingAvatar(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('选择头像失败:', error);
|
|
|
|
|
|
Alert.alert('错误', '选择头像时发生错误');
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 保存群组信息
|
|
|
|
|
|
const handleSaveGroupInfo = async () => {
|
|
|
|
|
|
if (!editName.trim()) {
|
|
|
|
|
|
Alert.alert('提示', '群名称不能为空');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setSaving(true);
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 更新群名称
|
|
|
|
|
|
await groupService.setGroupName(groupId, editName.trim());
|
|
|
|
|
|
|
|
|
|
|
|
// 如果头像有变化,更新群头像
|
|
|
|
|
|
if (editAvatar !== (group?.avatar || '')) {
|
|
|
|
|
|
await groupService.setGroupAvatar(groupId, editAvatar);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 重新获取群信息
|
|
|
|
|
|
const updatedGroup = await groupManager.getGroup(groupId, true);
|
|
|
|
|
|
setGroup(updatedGroup);
|
|
|
|
|
|
setEditModalVisible(false);
|
|
|
|
|
|
Alert.alert('成功', '群信息已更新');
|
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
|
console.error('更新群信息失败:', error);
|
|
|
|
|
|
Alert.alert('错误', error.message || '更新群信息失败');
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setSaving(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 设置全员禁言
|
|
|
|
|
|
const handleToggleMuteAll = async () => {
|
|
|
|
|
|
if (!group) return;
|
|
|
|
|
|
|
|
|
|
|
|
const newMuteAll = !group.mute_all;
|
|
|
|
|
|
|
|
|
|
|
|
Alert.alert(
|
|
|
|
|
|
newMuteAll ? '开启全员禁言' : '关闭全员禁言',
|
|
|
|
|
|
newMuteAll
|
|
|
|
|
|
? '确定要开启全员禁言吗?开启后只有管理员可以发言。'
|
|
|
|
|
|
: '确定要关闭全员禁言吗?',
|
|
|
|
|
|
[
|
|
|
|
|
|
{ text: '取消', style: 'cancel' },
|
|
|
|
|
|
{
|
|
|
|
|
|
text: '确定',
|
|
|
|
|
|
onPress: async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await groupService.setMuteAll(groupId, { mute_all: newMuteAll });
|
|
|
|
|
|
setGroup({ ...group, mute_all: newMuteAll });
|
|
|
|
|
|
Alert.alert('成功', newMuteAll ? '已开启全员禁言' : '已关闭全员禁言');
|
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
|
console.error('设置全员禁言失败:', error);
|
|
|
|
|
|
Alert.alert('错误', error.message || '设置失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 设置加群方式
|
|
|
|
|
|
const handleSetJoinType = async (joinType: JoinType) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await groupService.setJoinType(groupId, { join_type: joinType });
|
|
|
|
|
|
if (group) {
|
|
|
|
|
|
setGroup({ ...group, join_type: joinType });
|
|
|
|
|
|
}
|
|
|
|
|
|
setJoinTypeModalVisible(false);
|
|
|
|
|
|
Alert.alert('成功', '加群方式已更新');
|
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
|
console.error('设置加群方式失败:', error);
|
|
|
|
|
|
Alert.alert('错误', error.message || '设置失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 发布群公告
|
|
|
|
|
|
const handlePublishAnnouncement = async () => {
|
|
|
|
|
|
if (!newAnnouncement.trim()) {
|
|
|
|
|
|
Alert.alert('提示', '公告内容不能为空');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setSaving(true);
|
|
|
|
|
|
try {
|
|
|
|
|
|
await groupService.createAnnouncement(groupId, {
|
|
|
|
|
|
content: newAnnouncement.trim(),
|
|
|
|
|
|
});
|
|
|
|
|
|
setNewAnnouncement('');
|
|
|
|
|
|
setAnnouncementModalVisible(false);
|
|
|
|
|
|
// 刷新公告列表
|
|
|
|
|
|
const announcementsData = await groupService.getAnnouncements(groupId, 1, 10);
|
|
|
|
|
|
setAnnouncements(announcementsData.list);
|
|
|
|
|
|
Alert.alert('成功', '群公告已发布');
|
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
|
console.error('发布群公告失败:', error);
|
|
|
|
|
|
Alert.alert('错误', error.message || '发布失败');
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setSaving(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 转让群主
|
|
|
|
|
|
const handleTransferOwner = async () => {
|
|
|
|
|
|
if (!transferUserId) {
|
|
|
|
|
|
Alert.alert('提示', '请选择新群主');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Alert.alert(
|
|
|
|
|
|
'转让群主',
|
|
|
|
|
|
'确定要将群主转让给该成员吗?转让后您将成为普通成员。',
|
|
|
|
|
|
[
|
|
|
|
|
|
{ text: '取消', style: 'cancel' },
|
|
|
|
|
|
{
|
|
|
|
|
|
text: '确定',
|
|
|
|
|
|
onPress: async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await groupService.transferOwner(groupId, {
|
|
|
|
|
|
new_owner_id: transferUserId,
|
|
|
|
|
|
});
|
|
|
|
|
|
setTransferModalVisible(false);
|
|
|
|
|
|
Alert.alert('成功', '群主已转让', [
|
|
|
|
|
|
{ text: '确定', onPress: () => loadGroupInfo() },
|
|
|
|
|
|
]);
|
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
|
console.error('转让群主失败:', error);
|
|
|
|
|
|
Alert.alert('错误', error.message || '转让失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 解散群组
|
|
|
|
|
|
const handleDissolveGroup = () => {
|
|
|
|
|
|
Alert.alert(
|
|
|
|
|
|
'解散群组',
|
|
|
|
|
|
'确定要解散群组吗?此操作不可撤销,所有成员将被移出。',
|
|
|
|
|
|
[
|
|
|
|
|
|
{ text: '取消', style: 'cancel' },
|
|
|
|
|
|
{
|
|
|
|
|
|
text: '解散',
|
|
|
|
|
|
style: 'destructive',
|
|
|
|
|
|
onPress: async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await groupService.dissolveGroup(groupId);
|
|
|
|
|
|
Alert.alert('成功', '群组已解散', [
|
2026-03-24 14:21:31 +08:00
|
|
|
|
{ text: '确定', onPress: () => router.back() },
|
2026-03-09 21:29:03 +08:00
|
|
|
|
]);
|
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
|
console.error('解散群组失败:', error);
|
|
|
|
|
|
Alert.alert('错误', error.message || '解散失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 退出群聊
|
|
|
|
|
|
const handleLeaveGroup = () => {
|
|
|
|
|
|
Alert.alert(
|
|
|
|
|
|
'退出群聊',
|
|
|
|
|
|
'确定要退出群聊吗?',
|
|
|
|
|
|
[
|
|
|
|
|
|
{ text: '取消', style: 'cancel' },
|
|
|
|
|
|
{
|
|
|
|
|
|
text: '退出',
|
|
|
|
|
|
style: 'destructive',
|
|
|
|
|
|
onPress: async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await groupService.leaveGroup(groupId);
|
|
|
|
|
|
Alert.alert('成功', '已退出群聊', [
|
2026-03-24 14:21:31 +08:00
|
|
|
|
{ text: '确定', onPress: () => router.back() },
|
2026-03-09 21:29:03 +08:00
|
|
|
|
]);
|
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
|
console.error('退出群聊失败:', error);
|
|
|
|
|
|
Alert.alert('错误', error.message || '退出失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 打开邀请成员模态框
|
|
|
|
|
|
const openInviteModal = () => {
|
|
|
|
|
|
setInviteModalVisible(true);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 邀请成员
|
|
|
|
|
|
const handleInviteMembers = async (selectedUsers: User[]) => {
|
|
|
|
|
|
if (selectedUsers.length === 0) {
|
|
|
|
|
|
Alert.alert('提示', '请选择要邀请的成员');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setInviting(true);
|
|
|
|
|
|
try {
|
|
|
|
|
|
const memberIds = selectedUsers.map(user => user.id);
|
|
|
|
|
|
await groupService.inviteMembers(groupId, { member_ids: memberIds });
|
|
|
|
|
|
|
|
|
|
|
|
setInviteModalVisible(false);
|
|
|
|
|
|
Alert.alert('成功', '邀请已发送');
|
|
|
|
|
|
|
|
|
|
|
|
// 刷新成员列表
|
|
|
|
|
|
loadGroupInfo();
|
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
|
console.error('邀请成员失败:', error);
|
|
|
|
|
|
Alert.alert('错误', error.message || '邀请失败');
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setInviting(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 跳转到成员管理
|
|
|
|
|
|
const goToMembers = () => {
|
2026-03-24 14:21:31 +08:00
|
|
|
|
router.push(hrefs.hrefGroupMembers(groupId));
|
2026-03-09 21:29:03 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 获取加群方式文本
|
|
|
|
|
|
const getJoinTypeText = (joinType: JoinType): string => {
|
|
|
|
|
|
const option = JOIN_TYPE_OPTIONS.find(o => o.value === joinType);
|
|
|
|
|
|
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 = () => {
|
|
|
|
|
|
if (!group) return;
|
|
|
|
|
|
Clipboard.setString(String(group.id));
|
|
|
|
|
|
Alert.alert('已复制', '群号已复制到剪贴板');
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-24 14:21:31 +08:00
|
|
|
|
const formatGroupNo = (id: string) => {
|
|
|
|
|
|
const raw = id;
|
2026-03-09 21:29:03 +08:00
|
|
|
|
if (raw.length <= 12) return raw;
|
|
|
|
|
|
return `${raw.slice(0, 6)}...${raw.slice(-4)}`;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 渲染设置项
|
|
|
|
|
|
const renderSettingItem = (
|
|
|
|
|
|
icon: string,
|
|
|
|
|
|
title: string,
|
|
|
|
|
|
value?: string,
|
|
|
|
|
|
onPress?: () => void,
|
|
|
|
|
|
danger: boolean = false
|
|
|
|
|
|
) => (
|
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
|
style={styles.settingItem}
|
|
|
|
|
|
onPress={onPress}
|
|
|
|
|
|
disabled={!onPress}
|
|
|
|
|
|
activeOpacity={0.7}
|
|
|
|
|
|
>
|
|
|
|
|
|
<View style={[styles.settingIconContainer, danger && styles.settingIconDanger]}>
|
|
|
|
|
|
<MaterialCommunityIcons
|
|
|
|
|
|
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}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
{value && (
|
|
|
|
|
|
<Text variant="caption" color={colors.text.secondary} numberOfLines={1}>
|
|
|
|
|
|
{value}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</View>
|
|
|
|
|
|
{onPress && (
|
|
|
|
|
|
<MaterialCommunityIcons
|
|
|
|
|
|
name="chevron-right"
|
|
|
|
|
|
size={22}
|
|
|
|
|
|
color={colors.text.hint}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (loading) {
|
|
|
|
|
|
return (
|
2026-03-24 15:39:28 +08:00
|
|
|
|
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
|
|
|
|
|
|
<View style={styles.pageHeader}>
|
|
|
|
|
|
<AppBackButton onPress={() => router.back()} />
|
|
|
|
|
|
<Text variant="h3" style={styles.pageTitle}>群聊信息</Text>
|
|
|
|
|
|
<View style={styles.pageHeaderPlaceholder} />
|
|
|
|
|
|
</View>
|
2026-03-09 21:29:03 +08:00
|
|
|
|
<Loading />
|
|
|
|
|
|
</SafeAreaView>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!group) {
|
|
|
|
|
|
return (
|
2026-03-24 15:39:28 +08:00
|
|
|
|
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
|
|
|
|
|
|
<View style={styles.pageHeader}>
|
|
|
|
|
|
<AppBackButton onPress={() => router.back()} />
|
|
|
|
|
|
<Text variant="h3" style={styles.pageTitle}>群聊信息</Text>
|
|
|
|
|
|
<View style={styles.pageHeaderPlaceholder} />
|
|
|
|
|
|
</View>
|
2026-03-09 21:29:03 +08:00
|
|
|
|
<View style={styles.errorContainer}>
|
|
|
|
|
|
<Text variant="body" color={colors.text.secondary}>
|
|
|
|
|
|
群组信息不存在
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</SafeAreaView>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
2026-03-24 15:39:28 +08:00
|
|
|
|
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
|
|
|
|
|
|
<View style={styles.pageHeader}>
|
|
|
|
|
|
<AppBackButton onPress={() => router.back()} />
|
|
|
|
|
|
<Text variant="h3" style={styles.pageTitle}>群聊信息</Text>
|
|
|
|
|
|
<View style={styles.pageHeaderPlaceholder} />
|
|
|
|
|
|
</View>
|
2026-03-09 21:29:03 +08:00
|
|
|
|
<ScrollView
|
|
|
|
|
|
style={styles.scrollView}
|
|
|
|
|
|
contentContainerStyle={styles.scrollContent}
|
|
|
|
|
|
showsVerticalScrollIndicator={false}
|
|
|
|
|
|
>
|
2026-04-26 12:04:27 +08:00
|
|
|
|
{/* 群组基本信息 */}
|
2026-03-09 21:29:03 +08:00
|
|
|
|
<View style={styles.headerCard}>
|
|
|
|
|
|
<View style={styles.groupHeader}>
|
|
|
|
|
|
<Avatar
|
|
|
|
|
|
source={group.avatar}
|
2026-04-26 12:04:27 +08:00
|
|
|
|
size={80}
|
2026-03-09 21:29:03 +08:00
|
|
|
|
name={group.name}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<View style={styles.groupInfo}>
|
|
|
|
|
|
<Text variant="h3" style={styles.groupName} numberOfLines={1}>
|
|
|
|
|
|
{group.name}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
<View style={styles.groupMeta}>
|
2026-04-26 12:04:27 +08:00
|
|
|
|
<MaterialCommunityIcons name="account-group" size={14} color={colors.text.secondary} />
|
2026-03-09 21:29:03 +08:00
|
|
|
|
<Text variant="caption" color={colors.text.secondary}>
|
|
|
|
|
|
{group.member_count} 人
|
|
|
|
|
|
</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 }}>
|
|
|
|
|
|
{getJoinTypeText(group.join_type)}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
<View style={styles.groupNoRow}>
|
|
|
|
|
|
<Text variant="caption" color={colors.text.secondary}>
|
|
|
|
|
|
群号:{formatGroupNo(group.id)}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
<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>
|
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
{group.description ? (
|
|
|
|
|
|
<View style={styles.descriptionContainer}>
|
|
|
|
|
|
<MaterialCommunityIcons name="information-outline" size={16} color={colors.text.secondary} />
|
|
|
|
|
|
<Text variant="body" color={colors.text.secondary} style={styles.groupDesc}>
|
|
|
|
|
|
{group.description}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<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>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</View>
|
|
|
|
|
|
|
2026-04-26 12:04:27 +08:00
|
|
|
|
{/* 群公告 */}
|
2026-03-09 21:29:03 +08:00
|
|
|
|
{announcements.length > 0 && (
|
|
|
|
|
|
<View style={styles.card}>
|
|
|
|
|
|
<View style={styles.cardHeader}>
|
|
|
|
|
|
<View style={styles.cardIconContainer}>
|
|
|
|
|
|
<MaterialCommunityIcons name="bullhorn" size={18} color={colors.warning.main} />
|
|
|
|
|
|
</View>
|
|
|
|
|
|
<Text variant="label" style={styles.cardTitle}>群公告</Text>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
<View style={styles.announcementContent}>
|
|
|
|
|
|
<Text variant="body" style={styles.announcementText}>
|
|
|
|
|
|
{announcements[0].content}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
<Text variant="caption" color={colors.text.hint} style={styles.announcementTime}>
|
2026-04-25 15:25:42 +08:00
|
|
|
|
{(() => {
|
|
|
|
|
|
const d = new Date(announcements[0].created_at);
|
|
|
|
|
|
return Number.isNaN(d.getTime()) ? '' : d.toLocaleDateString();
|
|
|
|
|
|
})()}
|
2026-03-09 21:29:03 +08:00
|
|
|
|
</Text>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
2026-04-26 12:04:27 +08:00
|
|
|
|
{/* 成员预览 */}
|
2026-03-09 21:29:03 +08:00
|
|
|
|
<View style={styles.card}>
|
|
|
|
|
|
<TouchableOpacity style={styles.cardHeader} onPress={goToMembers} activeOpacity={0.7}>
|
|
|
|
|
|
<View style={styles.cardIconContainer}>
|
|
|
|
|
|
<MaterialCommunityIcons name="account-multiple" size={18} color={colors.info.main} />
|
|
|
|
|
|
</View>
|
|
|
|
|
|
<Text variant="label" style={styles.cardTitle}>群成员</Text>
|
|
|
|
|
|
<Text variant="caption" color={colors.text.secondary} style={styles.memberCount}>
|
|
|
|
|
|
{group.member_count}人
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
<MaterialCommunityIcons name="chevron-right" size={20} color={colors.text.hint} />
|
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
|
|
|
|
|
|
<View style={styles.memberPreview}>
|
|
|
|
|
|
{members.slice(0, 8).map((member, index) => (
|
|
|
|
|
|
<View
|
|
|
|
|
|
key={member.id}
|
|
|
|
|
|
style={[
|
|
|
|
|
|
styles.memberAvatar,
|
|
|
|
|
|
index === 0 && styles.memberAvatarFirst,
|
|
|
|
|
|
{ zIndex: index + 1 },
|
|
|
|
|
|
]}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Avatar
|
|
|
|
|
|
source={member.user?.avatar}
|
|
|
|
|
|
size={44}
|
|
|
|
|
|
name={member.user?.nickname || member.nickname}
|
|
|
|
|
|
/>
|
|
|
|
|
|
{member.role === 'owner' && (
|
|
|
|
|
|
<View style={styles.ownerBadge}>
|
|
|
|
|
|
<Text variant="caption" color={colors.background.paper} style={styles.ownerBadgeText}>主</Text>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</View>
|
|
|
|
|
|
))}
|
|
|
|
|
|
{group.member_count > 8 && (
|
|
|
|
|
|
<View style={styles.moreMembers}>
|
|
|
|
|
|
<Text
|
|
|
|
|
|
variant="caption"
|
|
|
|
|
|
color={colors.text.secondary}
|
|
|
|
|
|
style={styles.moreMembersText}
|
|
|
|
|
|
numberOfLines={1}
|
|
|
|
|
|
>
|
|
|
|
|
|
+{group.member_count - 8}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 邀请成员按钮 */}
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<MaterialCommunityIcons name="chevron-right" size={20} color={colors.primary.main} />
|
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 管理员设置 */}
|
|
|
|
|
|
{isAdmin && (
|
|
|
|
|
|
<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}>
|
|
|
|
|
|
{renderSettingItem('pencil-outline', '修改群信息', undefined, openEditModal)}
|
|
|
|
|
|
<Divider style={styles.settingDivider} />
|
|
|
|
|
|
{isOwner && renderSettingItem('earth', '加群方式', getJoinTypeText(group.join_type), () => setJoinTypeModalVisible(true))}
|
|
|
|
|
|
{isOwner && <Divider style={styles.settingDivider} />}
|
|
|
|
|
|
{isOwner && (
|
|
|
|
|
|
<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 && renderSettingItem('account-switch', '转让群主', undefined, () => setTransferModalVisible(true))}
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{/* 聊天设置 */}
|
|
|
|
|
|
<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.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>
|
2026-04-26 12:04:27 +08:00
|
|
|
|
<Divider style={styles.settingDivider} />
|
2026-04-25 21:23:22 +08:00
|
|
|
|
<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>
|
2026-03-09 21:29:03 +08:00
|
|
|
|
</View>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 危险操作区域 */}
|
|
|
|
|
|
<View style={[styles.card, styles.dangerCard]}>
|
|
|
|
|
|
{isOwner ? (
|
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
|
style={styles.dangerButton}
|
|
|
|
|
|
onPress={handleDissolveGroup}
|
|
|
|
|
|
activeOpacity={0.7}
|
|
|
|
|
|
>
|
|
|
|
|
|
<MaterialCommunityIcons name="delete-forever" size={22} color={colors.error.main} />
|
|
|
|
|
|
<Text variant="body" color={colors.error.main} style={styles.dangerButtonText}>
|
|
|
|
|
|
解散群组
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
|
style={styles.dangerButton}
|
|
|
|
|
|
onPress={handleLeaveGroup}
|
|
|
|
|
|
activeOpacity={0.7}
|
|
|
|
|
|
>
|
|
|
|
|
|
<MaterialCommunityIcons name="logout" size={22} color={colors.error.main} />
|
|
|
|
|
|
<Text variant="body" color={colors.error.main} style={styles.dangerButtonText}>
|
|
|
|
|
|
退出群聊
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</ScrollView>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 编辑群信息模态框 */}
|
|
|
|
|
|
<Modal
|
|
|
|
|
|
visible={editModalVisible}
|
|
|
|
|
|
animationType="slide"
|
|
|
|
|
|
transparent
|
|
|
|
|
|
onRequestClose={() => setEditModalVisible(false)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<KeyboardAvoidingView
|
|
|
|
|
|
style={styles.modalOverlay}
|
|
|
|
|
|
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
|
|
|
|
|
>
|
|
|
|
|
|
<View style={styles.modalContent}>
|
|
|
|
|
|
<View style={styles.modalHeader}>
|
|
|
|
|
|
<TouchableOpacity onPress={() => setEditModalVisible(false)}>
|
|
|
|
|
|
<Text variant="body" color={colors.text.secondary}>取消</Text>
|
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
<Text variant="h3" style={styles.modalTitle}>编辑群信息</Text>
|
|
|
|
|
|
<TouchableOpacity onPress={handleSaveGroupInfo} disabled={saving}>
|
|
|
|
|
|
<Text variant="body" color={saving ? colors.text.disabled : colors.primary.main} style={styles.saveButton}>
|
|
|
|
|
|
{saving ? '保存中...' : '保存'}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
|
|
<View style={styles.editForm}>
|
|
|
|
|
|
<View style={styles.editAvatarContainer}>
|
|
|
|
|
|
<TouchableOpacity onPress={handleSelectGroupAvatar} disabled={uploadingAvatar}>
|
|
|
|
|
|
{uploadingAvatar ? (
|
|
|
|
|
|
<View style={[styles.editAvatarPlaceholder, { width: 90, height: 90 }]}>
|
|
|
|
|
|
<Loading />
|
|
|
|
|
|
</View>
|
|
|
|
|
|
) : editAvatar ? (
|
|
|
|
|
|
<Image source={{ uri: editAvatar }} style={styles.editAvatarImage} />
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<Avatar source={undefined} size={90} name={editName || group.name} />
|
|
|
|
|
|
)}
|
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
<TouchableOpacity style={styles.editAvatarButton} onPress={handleSelectGroupAvatar} disabled={uploadingAvatar}>
|
|
|
|
|
|
<MaterialCommunityIcons name="camera" size={16} color={colors.background.paper} />
|
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
|
|
<View style={styles.editInputGroup}>
|
|
|
|
|
|
<Text variant="label" style={styles.editLabel}>群名称</Text>
|
|
|
|
|
|
<TextInput
|
|
|
|
|
|
style={styles.editInput}
|
|
|
|
|
|
value={editName}
|
|
|
|
|
|
onChangeText={setEditName}
|
|
|
|
|
|
placeholder="请输入群名称"
|
|
|
|
|
|
placeholderTextColor={colors.text.hint}
|
|
|
|
|
|
maxLength={50}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Text variant="caption" color={colors.text.hint} style={styles.editCharCount}>
|
|
|
|
|
|
{editName.length}/50
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
|
|
<View style={styles.editInputGroup}>
|
|
|
|
|
|
<Text variant="label" style={styles.editLabel}>群描述</Text>
|
|
|
|
|
|
<TextInput
|
|
|
|
|
|
style={[styles.editInput, styles.editTextArea]}
|
|
|
|
|
|
value={editDescription}
|
|
|
|
|
|
onChangeText={setEditDescription}
|
|
|
|
|
|
placeholder="介绍一下这个群聊..."
|
|
|
|
|
|
placeholderTextColor={colors.text.hint}
|
|
|
|
|
|
maxLength={500}
|
|
|
|
|
|
multiline
|
|
|
|
|
|
numberOfLines={4}
|
|
|
|
|
|
textAlignVertical="top"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Text variant="caption" color={colors.text.hint} style={styles.editCharCount}>
|
|
|
|
|
|
{editDescription.length}/500
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</KeyboardAvoidingView>
|
|
|
|
|
|
</Modal>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 发布群公告模态框 */}
|
|
|
|
|
|
<Modal
|
|
|
|
|
|
visible={announcementModalVisible}
|
|
|
|
|
|
animationType="slide"
|
|
|
|
|
|
transparent
|
|
|
|
|
|
onRequestClose={() => setAnnouncementModalVisible(false)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<KeyboardAvoidingView
|
|
|
|
|
|
style={styles.modalOverlay}
|
|
|
|
|
|
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
|
|
|
|
|
>
|
|
|
|
|
|
<View style={styles.modalContent}>
|
|
|
|
|
|
<View style={styles.modalHeader}>
|
|
|
|
|
|
<TouchableOpacity onPress={() => setAnnouncementModalVisible(false)}>
|
|
|
|
|
|
<Text variant="body" color={colors.text.secondary}>取消</Text>
|
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
<Text variant="h3" style={styles.modalTitle}>发布群公告</Text>
|
|
|
|
|
|
<TouchableOpacity onPress={handlePublishAnnouncement} disabled={saving}>
|
|
|
|
|
|
<Text variant="body" color={saving ? colors.text.disabled : colors.primary.main} style={styles.saveButton}>
|
|
|
|
|
|
{saving ? '发布中...' : '发布'}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
|
|
<View style={styles.announcementForm}>
|
|
|
|
|
|
<TextInput
|
|
|
|
|
|
style={styles.announcementInput}
|
|
|
|
|
|
value={newAnnouncement}
|
|
|
|
|
|
onChangeText={setNewAnnouncement}
|
|
|
|
|
|
placeholder="请输入群公告内容..."
|
|
|
|
|
|
placeholderTextColor={colors.text.hint}
|
|
|
|
|
|
maxLength={500}
|
|
|
|
|
|
multiline
|
|
|
|
|
|
numberOfLines={8}
|
|
|
|
|
|
textAlignVertical="top"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Text variant="caption" color={colors.text.hint} style={styles.announcementCharCount}>
|
|
|
|
|
|
{newAnnouncement.length}/500
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</KeyboardAvoidingView>
|
|
|
|
|
|
</Modal>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 加群方式模态框 */}
|
|
|
|
|
|
<Modal
|
|
|
|
|
|
visible={joinTypeModalVisible}
|
|
|
|
|
|
animationType="slide"
|
|
|
|
|
|
transparent
|
|
|
|
|
|
onRequestClose={() => setJoinTypeModalVisible(false)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<View style={styles.modalOverlay}>
|
|
|
|
|
|
<View style={styles.modalContent}>
|
|
|
|
|
|
<View style={styles.modalHeader}>
|
|
|
|
|
|
<TouchableOpacity onPress={() => setJoinTypeModalVisible(false)}>
|
|
|
|
|
|
<Text variant="body" color={colors.text.secondary}>取消</Text>
|
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
<Text variant="h3" style={styles.modalTitle}>加群方式</Text>
|
|
|
|
|
|
<View style={{ width: 40 }} />
|
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
|
|
<View style={styles.joinTypeList}>
|
|
|
|
|
|
{JOIN_TYPE_OPTIONS.map((option) => (
|
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
|
key={option.value}
|
|
|
|
|
|
style={[
|
|
|
|
|
|
styles.joinTypeItem,
|
|
|
|
|
|
group.join_type === option.value && styles.joinTypeItemSelected,
|
|
|
|
|
|
]}
|
|
|
|
|
|
onPress={() => handleSetJoinType(option.value)}
|
|
|
|
|
|
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}>
|
|
|
|
|
|
<Text
|
|
|
|
|
|
variant="body"
|
|
|
|
|
|
style={group.join_type === option.value ? styles.joinTypeTextSelected : undefined}
|
|
|
|
|
|
>
|
|
|
|
|
|
{option.label}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
<Text variant="caption" color={colors.text.secondary}>
|
|
|
|
|
|
{option.desc}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
{group.join_type === option.value && (
|
|
|
|
|
|
<MaterialCommunityIcons name="check-circle" size={24} color={colors.primary.main} />
|
|
|
|
|
|
)}
|
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</Modal>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 转让群主模态框 */}
|
|
|
|
|
|
<Modal
|
|
|
|
|
|
visible={transferModalVisible}
|
|
|
|
|
|
animationType="slide"
|
|
|
|
|
|
transparent
|
|
|
|
|
|
onRequestClose={() => setTransferModalVisible(false)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<View style={styles.modalOverlay}>
|
|
|
|
|
|
<View style={styles.modalContent}>
|
|
|
|
|
|
<View style={styles.modalHeader}>
|
|
|
|
|
|
<TouchableOpacity onPress={() => setTransferModalVisible(false)}>
|
|
|
|
|
|
<Text variant="body" color={colors.text.secondary}>取消</Text>
|
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
<Text variant="h3" style={styles.modalTitle}>转让群主</Text>
|
|
|
|
|
|
<View style={{ width: 40 }} />
|
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
|
|
<Text variant="caption" color={colors.text.secondary} style={styles.transferDesc}>
|
|
|
|
|
|
选择新的群主(仅显示管理员和普通成员)
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
|
|
|
|
|
|
<ScrollView style={styles.transferList} showsVerticalScrollIndicator={false}>
|
|
|
|
|
|
{members
|
|
|
|
|
|
.filter(m => m.role === 'admin' || m.role === 'member')
|
|
|
|
|
|
.map((member) => (
|
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
|
key={member.id}
|
|
|
|
|
|
style={[
|
|
|
|
|
|
styles.transferItem,
|
|
|
|
|
|
transferUserId === member.user_id && styles.transferItemSelected,
|
|
|
|
|
|
]}
|
|
|
|
|
|
onPress={() => setTransferUserId(member.user_id)}
|
|
|
|
|
|
activeOpacity={0.7}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Avatar
|
|
|
|
|
|
source={member.user?.avatar}
|
|
|
|
|
|
size={48}
|
|
|
|
|
|
name={member.user?.nickname || member.nickname}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<View style={styles.transferItemInfo}>
|
|
|
|
|
|
<Text variant="body">{member.user?.nickname || member.nickname}</Text>
|
|
|
|
|
|
<View style={styles.transferRoleBadge}>
|
|
|
|
|
|
<Text variant="caption" color={colors.primary.main}>
|
|
|
|
|
|
{member.role === 'admin' ? '管理员' : '成员'}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
<View style={[styles.transferRadio, transferUserId === member.user_id && styles.transferRadioSelected]}>
|
|
|
|
|
|
{transferUserId === member.user_id && (
|
|
|
|
|
|
<MaterialCommunityIcons name="check" size={14} color={colors.background.paper} />
|
|
|
|
|
|
)}
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</ScrollView>
|
|
|
|
|
|
|
|
|
|
|
|
<Button
|
|
|
|
|
|
title="确认转让"
|
|
|
|
|
|
onPress={handleTransferOwner}
|
|
|
|
|
|
disabled={!transferUserId}
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
style={styles.transferButton}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</View>
|
|
|
|
|
|
</Modal>
|
|
|
|
|
|
|
|
|
|
|
|
<MutualFollowSelectorModal
|
|
|
|
|
|
visible={inviteModalVisible}
|
|
|
|
|
|
title="邀请成员"
|
|
|
|
|
|
confirmText="确认"
|
|
|
|
|
|
confirmingText="发送中..."
|
|
|
|
|
|
confirmLoading={inviting}
|
|
|
|
|
|
excludeUserIds={members.map(member => member.user_id)}
|
|
|
|
|
|
onClose={() => setInviteModalVisible(false)}
|
|
|
|
|
|
onConfirm={handleInviteMembers}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</SafeAreaView>
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-25 05:16:54 +08:00
|
|
|
|
function createGroupInfoStyles(colors: AppColors) {
|
|
|
|
|
|
return StyleSheet.create({
|
2026-03-09 21:29:03 +08:00
|
|
|
|
container: {
|
|
|
|
|
|
flex: 1,
|
2026-04-26 12:04:27 +08:00
|
|
|
|
backgroundColor: colors.background.paper,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
2026-03-24 15:39:28 +08:00
|
|
|
|
pageHeader: {
|
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
justifyContent: 'space-between',
|
2026-04-26 12:04:27 +08:00
|
|
|
|
paddingHorizontal: spacing.lg,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
paddingVertical: spacing.md,
|
2026-03-24 15:39:28 +08:00
|
|
|
|
backgroundColor: colors.background.paper,
|
2026-04-26 12:04:27 +08:00
|
|
|
|
borderBottomWidth: 0,
|
2026-03-24 15:39:28 +08:00
|
|
|
|
},
|
|
|
|
|
|
pageTitle: {
|
2026-04-26 12:04:27 +08:00
|
|
|
|
fontWeight: '600',
|
2026-03-30 17:53:30 +08:00
|
|
|
|
fontSize: fontSizes.xl,
|
2026-03-24 15:39:28 +08:00
|
|
|
|
},
|
2026-04-25 00:39:40 +08:00
|
|
|
|
pageHeaderPlaceholder: {
|
|
|
|
|
|
width: 40,
|
|
|
|
|
|
height: 40,
|
|
|
|
|
|
},
|
2026-03-09 21:29:03 +08:00
|
|
|
|
scrollView: {
|
|
|
|
|
|
flex: 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
scrollContent: {
|
2026-04-26 12:04:27 +08:00
|
|
|
|
paddingHorizontal: spacing.lg,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
paddingBottom: spacing.xl * 2,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
errorContainer: {
|
|
|
|
|
|
flex: 1,
|
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-04-26 12:04:27 +08:00
|
|
|
|
// 头部区域 - 扁平化无卡片
|
2026-03-09 21:29:03 +08:00
|
|
|
|
headerCard: {
|
|
|
|
|
|
backgroundColor: colors.background.paper,
|
2026-04-26 12:04:27 +08:00
|
|
|
|
paddingVertical: spacing.lg,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
marginBottom: spacing.md,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
groupHeader: {
|
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
|
alignItems: 'center',
|
2026-04-26 12:04:27 +08:00
|
|
|
|
marginBottom: spacing.lg,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
groupInfo: {
|
|
|
|
|
|
marginLeft: spacing.lg,
|
|
|
|
|
|
flex: 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
groupName: {
|
2026-04-26 12:04:27 +08:00
|
|
|
|
fontWeight: '700',
|
|
|
|
|
|
fontSize: fontSizes['2xl'],
|
2026-03-09 21:29:03 +08:00
|
|
|
|
marginBottom: spacing.xs,
|
|
|
|
|
|
},
|
|
|
|
|
|
groupMeta: {
|
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
gap: spacing.xs,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
marginBottom: spacing.xs,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
groupNoRow: {
|
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
|
alignItems: 'center',
|
2026-03-30 17:53:30 +08:00
|
|
|
|
gap: spacing.sm,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
copyGroupNoBtn: {
|
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
paddingHorizontal: spacing.sm,
|
|
|
|
|
|
paddingVertical: 4,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
borderRadius: borderRadius.md,
|
|
|
|
|
|
backgroundColor: colors.primary.light + '15',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
copyGroupNoBtnText: {
|
|
|
|
|
|
marginLeft: 4,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
fontWeight: '500',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
descriptionContainer: {
|
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
|
alignItems: 'flex-start',
|
|
|
|
|
|
backgroundColor: colors.background.default,
|
|
|
|
|
|
borderRadius: borderRadius.lg,
|
|
|
|
|
|
padding: spacing.md,
|
|
|
|
|
|
},
|
|
|
|
|
|
groupDesc: {
|
|
|
|
|
|
marginLeft: spacing.sm,
|
|
|
|
|
|
flex: 1,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
lineHeight: 22,
|
|
|
|
|
|
fontWeight: '400',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
2026-04-26 12:04:27 +08:00
|
|
|
|
// 通用卡片样式 - 扁平化:无阴影无圆角,使用分割线
|
2026-03-09 21:29:03 +08:00
|
|
|
|
card: {
|
|
|
|
|
|
backgroundColor: colors.background.paper,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
marginBottom: spacing.md,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
cardHeader: {
|
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
marginBottom: spacing.md,
|
2026-04-26 12:04:27 +08:00
|
|
|
|
paddingTop: spacing.sm,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
cardIconContainer: {
|
|
|
|
|
|
width: 36,
|
|
|
|
|
|
height: 36,
|
|
|
|
|
|
borderRadius: borderRadius.md,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
backgroundColor: colors.info.light + '15',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
marginRight: spacing.sm,
|
|
|
|
|
|
},
|
|
|
|
|
|
cardTitle: {
|
2026-04-26 12:04:27 +08:00
|
|
|
|
fontWeight: '600',
|
|
|
|
|
|
fontSize: fontSizes.md,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
flex: 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-04-26 12:04:27 +08:00
|
|
|
|
// 公告样式 - 扁平化
|
2026-03-09 21:29:03 +08:00
|
|
|
|
announcementContent: {
|
2026-03-30 17:53:30 +08:00
|
|
|
|
backgroundColor: colors.warning.light + '08',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
borderRadius: borderRadius.lg,
|
|
|
|
|
|
padding: spacing.md,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
borderLeftWidth: 3,
|
|
|
|
|
|
borderLeftColor: colors.warning.main,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
announcementText: {
|
|
|
|
|
|
lineHeight: 22,
|
|
|
|
|
|
marginBottom: spacing.sm,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
fontWeight: '400',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
announcementTime: {
|
|
|
|
|
|
textAlign: 'right',
|
2026-03-30 17:53:30 +08:00
|
|
|
|
fontWeight: '500',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 成员预览样式
|
|
|
|
|
|
memberCount: {
|
|
|
|
|
|
marginRight: spacing.xs,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
fontWeight: '600',
|
|
|
|
|
|
color: colors.text.secondary,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
memberPreview: {
|
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
flexWrap: 'nowrap',
|
|
|
|
|
|
marginBottom: spacing.md,
|
|
|
|
|
|
paddingLeft: spacing.xs,
|
|
|
|
|
|
},
|
|
|
|
|
|
memberAvatar: {
|
|
|
|
|
|
position: 'relative',
|
2026-03-30 17:53:30 +08:00
|
|
|
|
marginLeft: -8,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
memberAvatarFirst: {
|
|
|
|
|
|
marginLeft: 0,
|
|
|
|
|
|
},
|
|
|
|
|
|
ownerBadge: {
|
|
|
|
|
|
position: 'absolute',
|
|
|
|
|
|
bottom: -2,
|
|
|
|
|
|
right: -2,
|
|
|
|
|
|
backgroundColor: colors.warning.main,
|
|
|
|
|
|
borderRadius: borderRadius.sm,
|
|
|
|
|
|
paddingHorizontal: 4,
|
|
|
|
|
|
paddingVertical: 1,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
borderWidth: 2,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
borderColor: colors.background.paper,
|
|
|
|
|
|
},
|
|
|
|
|
|
ownerBadgeText: {
|
|
|
|
|
|
fontSize: 8,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
fontWeight: '800',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
moreMembers: {
|
|
|
|
|
|
minWidth: 44,
|
|
|
|
|
|
height: 44,
|
|
|
|
|
|
borderRadius: 22,
|
|
|
|
|
|
backgroundColor: colors.background.default,
|
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
borderWidth: 1,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
borderColor: colors.divider + '60',
|
|
|
|
|
|
marginLeft: -8,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
paddingHorizontal: spacing.xs,
|
|
|
|
|
|
},
|
|
|
|
|
|
moreMembersText: {
|
2026-03-30 17:53:30 +08:00
|
|
|
|
fontWeight: '700',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
includeFontPadding: false,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
fontSize: fontSizes.sm,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
2026-04-26 12:04:27 +08:00
|
|
|
|
// 邀请按钮样式 - 扁平化
|
2026-03-09 21:29:03 +08:00
|
|
|
|
inviteButton: {
|
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
|
paddingVertical: spacing.md,
|
|
|
|
|
|
borderRadius: borderRadius.lg,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
backgroundColor: colors.primary.light + '10',
|
|
|
|
|
|
borderWidth: 1.5,
|
|
|
|
|
|
borderColor: colors.primary.light + '60',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
borderStyle: 'dashed',
|
|
|
|
|
|
},
|
|
|
|
|
|
inviteIconContainer: {
|
|
|
|
|
|
width: 32,
|
|
|
|
|
|
height: 32,
|
|
|
|
|
|
borderRadius: 16,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
backgroundColor: colors.primary.light + '20',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
marginRight: spacing.sm,
|
|
|
|
|
|
},
|
|
|
|
|
|
inviteButtonText: {
|
2026-03-30 17:53:30 +08:00
|
|
|
|
fontWeight: '700',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
marginRight: spacing.xs,
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-04-26 12:04:27 +08:00
|
|
|
|
// 设置项样式 - 扁平化:无圆角背景,简洁分割线
|
2026-03-09 21:29:03 +08:00
|
|
|
|
settingsList: {
|
2026-04-26 12:04:27 +08:00
|
|
|
|
marginTop: 0,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
settingItem: {
|
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
|
alignItems: 'center',
|
2026-03-30 17:53:30 +08:00
|
|
|
|
paddingVertical: spacing.md,
|
2026-04-26 12:04:27 +08:00
|
|
|
|
paddingHorizontal: 0,
|
|
|
|
|
|
marginLeft: 0,
|
|
|
|
|
|
marginRight: 0,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
settingIconContainer: {
|
2026-04-26 12:04:27 +08:00
|
|
|
|
width: 36,
|
|
|
|
|
|
height: 36,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
borderRadius: borderRadius.md,
|
|
|
|
|
|
backgroundColor: colors.background.default,
|
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
marginRight: spacing.md,
|
|
|
|
|
|
},
|
|
|
|
|
|
settingIconDanger: {
|
2026-03-30 17:53:30 +08:00
|
|
|
|
backgroundColor: colors.error.light + '15',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
settingContent: {
|
|
|
|
|
|
flex: 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
dangerText: {
|
|
|
|
|
|
color: colors.error.main,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
fontWeight: '600',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
settingDivider: {
|
2026-04-26 12:04:27 +08:00
|
|
|
|
marginLeft: 52,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
width: 'auto',
|
2026-04-26 12:04:27 +08:00
|
|
|
|
marginVertical: 0,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
opacity: 0.5,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
2026-04-26 12:04:27 +08:00
|
|
|
|
// 危险操作区域 - 扁平化
|
2026-03-09 21:29:03 +08:00
|
|
|
|
dangerCard: {
|
|
|
|
|
|
padding: 0,
|
|
|
|
|
|
overflow: 'hidden',
|
2026-03-30 17:53:30 +08:00
|
|
|
|
marginTop: spacing.sm,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
dangerButton: {
|
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
|
paddingVertical: spacing.lg,
|
|
|
|
|
|
gap: spacing.sm,
|
|
|
|
|
|
},
|
|
|
|
|
|
dangerButtonText: {
|
2026-04-26 12:04:27 +08:00
|
|
|
|
fontWeight: '600',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
2026-04-26 12:04:27 +08:00
|
|
|
|
// 模态框样式 - 扁平化
|
2026-03-09 21:29:03 +08:00
|
|
|
|
modalOverlay: {
|
|
|
|
|
|
flex: 1,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
backgroundColor: 'rgba(0, 0, 0, 0.45)',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
justifyContent: 'flex-end',
|
|
|
|
|
|
},
|
|
|
|
|
|
modalContent: {
|
|
|
|
|
|
backgroundColor: colors.background.paper,
|
|
|
|
|
|
borderTopLeftRadius: borderRadius['2xl'],
|
|
|
|
|
|
borderTopRightRadius: borderRadius['2xl'],
|
|
|
|
|
|
height: '80%',
|
|
|
|
|
|
paddingHorizontal: spacing.lg,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
paddingTop: spacing.md,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
paddingBottom: spacing.xl,
|
|
|
|
|
|
},
|
|
|
|
|
|
modalHeader: {
|
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
|
justifyContent: 'space-between',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
marginBottom: spacing.lg,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
paddingBottom: spacing.md,
|
|
|
|
|
|
borderBottomWidth: 0.5,
|
|
|
|
|
|
borderBottomColor: colors.divider + '60',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
modalHeaderButton: {
|
|
|
|
|
|
paddingVertical: spacing.sm,
|
|
|
|
|
|
minWidth: 60,
|
|
|
|
|
|
},
|
|
|
|
|
|
modalTitle: {
|
2026-04-26 12:04:27 +08:00
|
|
|
|
fontWeight: '700',
|
|
|
|
|
|
fontSize: fontSizes.xl,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
saveButton: {
|
2026-04-26 12:04:27 +08:00
|
|
|
|
fontWeight: '600',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
confirmButton: {
|
2026-04-26 12:04:27 +08:00
|
|
|
|
fontWeight: '600',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
textAlign: 'right',
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-04-26 12:04:27 +08:00
|
|
|
|
// 编辑表单样式 - 扁平化
|
2026-03-09 21:29:03 +08:00
|
|
|
|
editForm: {
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
},
|
|
|
|
|
|
editAvatarContainer: {
|
|
|
|
|
|
position: 'relative',
|
|
|
|
|
|
marginBottom: spacing.xl,
|
|
|
|
|
|
},
|
|
|
|
|
|
editAvatarImage: {
|
|
|
|
|
|
width: 90,
|
|
|
|
|
|
height: 90,
|
|
|
|
|
|
borderRadius: 45,
|
2026-04-26 12:04:27 +08:00
|
|
|
|
borderWidth: 2,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
borderColor: colors.primary.light + '40',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
editAvatarPlaceholder: {
|
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
backgroundColor: colors.background.default,
|
|
|
|
|
|
borderRadius: 45,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
borderWidth: 2,
|
|
|
|
|
|
borderColor: colors.divider,
|
|
|
|
|
|
borderStyle: 'dashed',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
editAvatarButton: {
|
|
|
|
|
|
position: 'absolute',
|
|
|
|
|
|
bottom: 0,
|
|
|
|
|
|
right: 0,
|
|
|
|
|
|
backgroundColor: colors.primary.main,
|
|
|
|
|
|
width: 32,
|
|
|
|
|
|
height: 32,
|
|
|
|
|
|
borderRadius: 16,
|
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
borderWidth: 3,
|
|
|
|
|
|
borderColor: colors.background.paper,
|
|
|
|
|
|
},
|
|
|
|
|
|
editInputGroup: {
|
|
|
|
|
|
width: '100%',
|
|
|
|
|
|
marginBottom: spacing.lg,
|
|
|
|
|
|
},
|
|
|
|
|
|
editLabel: {
|
|
|
|
|
|
marginBottom: spacing.sm,
|
2026-04-26 12:04:27 +08:00
|
|
|
|
fontWeight: '600',
|
|
|
|
|
|
fontSize: fontSizes.sm,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
editInput: {
|
|
|
|
|
|
backgroundColor: colors.background.default,
|
|
|
|
|
|
borderRadius: borderRadius.lg,
|
|
|
|
|
|
paddingHorizontal: spacing.md,
|
|
|
|
|
|
paddingVertical: spacing.md,
|
|
|
|
|
|
fontSize: fontSizes.md,
|
|
|
|
|
|
color: colors.text.primary,
|
2026-04-26 12:04:27 +08:00
|
|
|
|
borderWidth: 1,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
borderColor: colors.divider + '80',
|
|
|
|
|
|
fontWeight: '400',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
editTextArea: {
|
|
|
|
|
|
minHeight: 100,
|
|
|
|
|
|
paddingTop: spacing.md,
|
|
|
|
|
|
lineHeight: 22,
|
|
|
|
|
|
},
|
|
|
|
|
|
editCharCount: {
|
|
|
|
|
|
textAlign: 'right',
|
|
|
|
|
|
marginTop: spacing.xs,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
fontWeight: '500',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 公告表单样式
|
|
|
|
|
|
announcementForm: {
|
|
|
|
|
|
flex: 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
announcementInput: {
|
|
|
|
|
|
flex: 1,
|
|
|
|
|
|
backgroundColor: colors.background.default,
|
|
|
|
|
|
borderRadius: borderRadius.lg,
|
|
|
|
|
|
padding: spacing.md,
|
|
|
|
|
|
fontSize: fontSizes.md,
|
|
|
|
|
|
color: colors.text.primary,
|
2026-04-26 12:04:27 +08:00
|
|
|
|
borderWidth: 1,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
borderColor: colors.divider + '80',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
lineHeight: 22,
|
|
|
|
|
|
textAlignVertical: 'top',
|
|
|
|
|
|
},
|
|
|
|
|
|
announcementCharCount: {
|
|
|
|
|
|
textAlign: 'right',
|
|
|
|
|
|
marginTop: spacing.sm,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
fontWeight: '500',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
2026-03-30 17:53:30 +08:00
|
|
|
|
// 加群方式样式 - 更现代的选中状态
|
2026-03-09 21:29:03 +08:00
|
|
|
|
joinTypeList: {
|
|
|
|
|
|
gap: spacing.md,
|
|
|
|
|
|
},
|
|
|
|
|
|
joinTypeItem: {
|
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
padding: spacing.md,
|
|
|
|
|
|
borderRadius: borderRadius.lg,
|
2026-04-26 12:04:27 +08:00
|
|
|
|
borderWidth: 1,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
borderColor: colors.divider + '80',
|
|
|
|
|
|
backgroundColor: colors.background.default,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
joinTypeItemSelected: {
|
|
|
|
|
|
borderColor: colors.primary.main,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
backgroundColor: colors.primary.light + '12',
|
2026-04-26 12:04:27 +08:00
|
|
|
|
borderWidth: 1.5,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
joinTypeIcon: {
|
|
|
|
|
|
width: 48,
|
|
|
|
|
|
height: 48,
|
|
|
|
|
|
borderRadius: borderRadius.lg,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
backgroundColor: colors.background.paper,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
marginRight: spacing.md,
|
|
|
|
|
|
},
|
|
|
|
|
|
joinTypeIconSelected: {
|
2026-03-30 17:53:30 +08:00
|
|
|
|
backgroundColor: colors.primary.light + '25',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
joinTypeInfo: {
|
|
|
|
|
|
flex: 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
joinTypeTextSelected: {
|
2026-03-30 17:53:30 +08:00
|
|
|
|
fontWeight: '700',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
color: colors.primary.main,
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 转让群主样式
|
|
|
|
|
|
transferDesc: {
|
|
|
|
|
|
marginBottom: spacing.md,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
fontWeight: '500',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
transferList: {
|
|
|
|
|
|
flex: 1,
|
|
|
|
|
|
marginBottom: spacing.md,
|
|
|
|
|
|
},
|
|
|
|
|
|
transferItem: {
|
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
paddingVertical: spacing.md,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
paddingHorizontal: spacing.md,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
borderRadius: borderRadius.lg,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
marginBottom: spacing.sm,
|
|
|
|
|
|
backgroundColor: colors.background.default,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
transferItemSelected: {
|
2026-03-30 17:53:30 +08:00
|
|
|
|
backgroundColor: colors.primary.light + '12',
|
2026-04-26 12:04:27 +08:00
|
|
|
|
borderWidth: 1,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
borderColor: colors.primary.light + '60',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
transferItemInfo: {
|
|
|
|
|
|
flex: 1,
|
|
|
|
|
|
marginLeft: spacing.md,
|
|
|
|
|
|
},
|
|
|
|
|
|
transferRoleBadge: {
|
|
|
|
|
|
backgroundColor: colors.primary.light + '20',
|
|
|
|
|
|
paddingHorizontal: spacing.sm,
|
|
|
|
|
|
paddingVertical: 2,
|
|
|
|
|
|
borderRadius: borderRadius.sm,
|
|
|
|
|
|
alignSelf: 'flex-start',
|
|
|
|
|
|
marginTop: spacing.xs,
|
|
|
|
|
|
},
|
|
|
|
|
|
transferRadio: {
|
|
|
|
|
|
width: 24,
|
|
|
|
|
|
height: 24,
|
|
|
|
|
|
borderRadius: 12,
|
|
|
|
|
|
borderWidth: 2,
|
|
|
|
|
|
borderColor: colors.divider,
|
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
|
alignItems: 'center',
|
2026-03-30 17:53:30 +08:00
|
|
|
|
backgroundColor: colors.background.paper,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
transferRadioSelected: {
|
|
|
|
|
|
backgroundColor: colors.primary.main,
|
|
|
|
|
|
borderColor: colors.primary.main,
|
|
|
|
|
|
},
|
|
|
|
|
|
transferButton: {
|
|
|
|
|
|
marginTop: spacing.sm,
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 邀请成员模态框样式
|
|
|
|
|
|
tabContainer: {
|
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
|
backgroundColor: colors.background.default,
|
|
|
|
|
|
borderRadius: borderRadius.lg,
|
|
|
|
|
|
padding: spacing.xs,
|
|
|
|
|
|
marginBottom: spacing.md,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
borderWidth: 1,
|
|
|
|
|
|
borderColor: colors.divider + '60',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
tab: {
|
|
|
|
|
|
flex: 1,
|
|
|
|
|
|
paddingVertical: spacing.sm,
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
borderRadius: borderRadius.md,
|
|
|
|
|
|
},
|
|
|
|
|
|
tabActive: {
|
|
|
|
|
|
backgroundColor: colors.background.paper,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
borderWidth: 1,
|
|
|
|
|
|
borderColor: colors.divider + '60',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
tabTextActive: {
|
2026-03-30 17:53:30 +08:00
|
|
|
|
fontWeight: '700',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
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,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
marginBottom: spacing.sm,
|
|
|
|
|
|
backgroundColor: colors.background.default,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
friendItemSelected: {
|
2026-03-30 17:53:30 +08:00
|
|
|
|
backgroundColor: colors.primary.light + '12',
|
2026-04-26 12:04:27 +08:00
|
|
|
|
borderWidth: 1,
|
2026-03-30 17:53:30 +08:00
|
|
|
|
borderColor: colors.primary.light + '60',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
friendInfo: {
|
|
|
|
|
|
flex: 1,
|
|
|
|
|
|
marginLeft: spacing.md,
|
|
|
|
|
|
marginRight: spacing.sm,
|
|
|
|
|
|
},
|
|
|
|
|
|
nickname: {
|
2026-03-30 17:53:30 +08:00
|
|
|
|
fontWeight: '600',
|
2026-03-09 21:29:03 +08:00
|
|
|
|
marginBottom: 2,
|
|
|
|
|
|
},
|
|
|
|
|
|
checkbox: {
|
|
|
|
|
|
width: 26,
|
|
|
|
|
|
height: 26,
|
|
|
|
|
|
borderRadius: 13,
|
|
|
|
|
|
borderWidth: 2,
|
|
|
|
|
|
borderColor: colors.divider,
|
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
|
alignItems: 'center',
|
2026-03-30 17:53:30 +08:00
|
|
|
|
backgroundColor: colors.background.paper,
|
2026-03-09 21:29:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
checkboxSelected: {
|
|
|
|
|
|
backgroundColor: colors.primary.main,
|
|
|
|
|
|
borderColor: colors.primary.main,
|
|
|
|
|
|
},
|
2026-03-25 05:16:54 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
2026-03-09 21:29:03 +08:00
|
|
|
|
|
|
|
|
|
|
export default GroupInfoScreen;
|