feat(message): support group avatar in navigation and chat screen
All checks were successful
Frontend CI / ota-android (push) Successful in 1m50s
Frontend CI / ota-ios (push) Successful in 1m49s
Frontend CI / build-and-push-web (push) Successful in 2m55s
Frontend CI / build-android-apk (push) Successful in 1h10m58s

Pass group avatar through notification extras, navigation hrefs, and
route parameters. Update ChatScreen to resolve group information
(ID, name, and avatar) by prioritizing route parameters (e.g., from
push notifications) and falling back to conversation data from the
MessageManager.

- Update `NotificationBootstrap` to include `groupAvatar` in query params
- Update `hrefChat` to support `groupAvatar`
- Refactor `useChatScreen` to use `effectiveGroupId`, `effectiveGroupName`,
  and `effectiveGroupAvatar`
- Ensure `MessageManager` fetches conversation details if missing when
  activating a conversation
- Update `MessageListScreen` to pass `groupAvatar` during navigation
This commit is contained in:
2026-05-13 00:31:44 +08:00
parent 7e0436a799
commit cbe708b53b
6 changed files with 57 additions and 28 deletions

View File

@@ -96,6 +96,7 @@ export const useChatScreen = (props?: ChatScreenProps) => {
isGroupChat?: string | string[];
groupId?: string | string[];
groupName?: string | string[];
groupAvatar?: string | string[];
}>();
// 路由参数(动态段 + query群 ID 勿用 Number(),避免超过 2^53-1 时精度丢失
// 嵌入式模式下优先使用 props否则从路由参数获取
@@ -107,6 +108,7 @@ export const useChatScreen = (props?: ChatScreenProps) => {
isGroupChat: props.embeddedIsGroupChat ?? false,
groupId: props.embeddedGroupId ?? null,
groupName: props.embeddedGroupName ?? null,
groupAvatar: null as string | null,
};
}
const isGroupFlag = firstRouteParam(rawParams.isGroupChat);
@@ -116,6 +118,7 @@ export const useChatScreen = (props?: ChatScreenProps) => {
isGroupChat: isGroupFlag === '1' || isGroupFlag === 'true',
groupId: firstRouteParam(rawParams.groupId),
groupName: firstRouteParam(rawParams.groupName),
groupAvatar: firstRouteParam(rawParams.groupAvatar),
};
}, [
props?.embeddedConversationId,
@@ -127,9 +130,10 @@ export const useChatScreen = (props?: ChatScreenProps) => {
rawParams.isGroupChat,
rawParams.groupId,
rawParams.groupName,
rawParams.groupAvatar,
]);
const { conversationId: routeConversationId, userId: routeUserId, isGroupChat, groupId: routeGroupId, groupName: routeGroupName } = routeParams;
const { conversationId: routeConversationId, userId: routeUserId, isGroupChat, groupId: routeGroupId, groupName: routeGroupName, groupAvatar: routeGroupAvatar } = routeParams;
// 当前会话ID可能在新建私聊会话后动态更新
const [conversationId, setConversationId] = useState<string | null>(routeConversationId);
@@ -147,13 +151,18 @@ export const useChatScreen = (props?: ChatScreenProps) => {
conversation,
} = useChat(conversationId);
// 统一入口:优先路由参数,其次从 conversation 对象提取JPUSH 进入时 conversation 异步加载)
const effectiveGroupId = routeGroupId || (isGroupChat && conversation?.group?.id) || null;
const effectiveGroupName = routeGroupName || (isGroupChat && conversation?.group?.name) || null;
const effectiveGroupAvatar = routeGroupAvatar || (isGroupChat && conversation?.group?.avatar) || null;
// 本地状态
const [currentUserId, setCurrentUserId] = useState<string>('');
// 【新架构】使用 MessageManager 获取群聊输入状态和禁言状态
const { typingUsers: groupTypingUsers } = useGroupTyping(routeGroupId ? String(routeGroupId) : null);
const { typingUsers: groupTypingUsers } = useGroupTyping(effectiveGroupId ? String(effectiveGroupId) : null);
const { isMuted: isMutedFromManager, setMuted: setMutedStatus } = useGroupMuted(
routeGroupId ? String(routeGroupId) : null,
effectiveGroupId ? String(effectiveGroupId) : null,
currentUserId
);
@@ -202,8 +211,18 @@ export const useChatScreen = (props?: ChatScreenProps) => {
const [selectedMessageId, setSelectedMessageId] = useState<string | null>(null);
// 群聊相关状态
const [groupInfo, setGroupInfo] = useState<{ name?: string; member_count?: number } | null>(null);
const [groupInfo, setGroupInfo] = useState<{ name?: string; member_count?: number; avatar?: string | null } | null>(null);
const [groupMembers, setGroupMembers] = useState<GroupMemberResponse[]>([]);
// 群名/头像优先使用路由参数JPUSH extras其次从 conversation 对象获取
useEffect(() => {
if (!isGroupChat) return;
const name = effectiveGroupName;
const avatar = effectiveGroupAvatar;
if (name || avatar) {
setGroupInfo(prev => prev ? prev : { name: name || undefined, avatar: avatar || null });
}
}, [isGroupChat, effectiveGroupName, effectiveGroupAvatar]);
const [currentUserRole, setCurrentUserRole] = useState<UserRole>('member');
const [mentionQuery, setMentionQuery] = useState<string>('');
const [selectedMentions, setSelectedMentions] = useState<string[]>([]);
@@ -453,14 +472,14 @@ export const useChatScreen = (props?: ChatScreenProps) => {
// 群聊模式:获取群组信息和成员列表
useEffect(() => {
if (!isGroupChat || !routeGroupId) return;
if (!isGroupChat || !effectiveGroupId) return;
const fetchGroupInfo = async () => {
try {
const group = await groupManager.getGroup(routeGroupId);
const group = await groupManager.getGroup(effectiveGroupId);
setGroupInfo(group);
const membersResponse = await groupManager.getMembers(routeGroupId, 1, MEMBERS_PAGE_SIZE);
const membersResponse = await groupManager.getMembers(effectiveGroupId, 1, MEMBERS_PAGE_SIZE);
const members = membersResponse.list || [];
setGroupMembers(members);
@@ -470,7 +489,7 @@ export const useChatScreen = (props?: ChatScreenProps) => {
}
try {
const myInfo = await groupService.getMyMemberInfo(routeGroupId);
const myInfo = await groupService.getMyMemberInfo(effectiveGroupId);
setIsMuted(myInfo.is_muted);
setMutedStatus(myInfo.is_muted);
setMuteAll(myInfo.mute_all);
@@ -508,7 +527,7 @@ export const useChatScreen = (props?: ChatScreenProps) => {
if (currentUserId) {
fetchGroupInfo();
}
}, [isGroupChat, routeGroupId, currentUserId, setMutedStatus]);
}, [isGroupChat, effectiveGroupId, currentUserId, setMutedStatus]);
// 私聊模式:创建或获取会话
useEffect(() => {
@@ -1066,7 +1085,7 @@ export const useChatScreen = (props?: ChatScreenProps) => {
return;
}
if (isGroupChat && routeGroupId) {
if (isGroupChat && effectiveGroupId) {
await messageService.sendMessageByAction('group', conversationId, segments);
setInputText('');
@@ -1097,7 +1116,7 @@ export const useChatScreen = (props?: ChatScreenProps) => {
pendingAttachments,
conversationId,
isGroupChat,
routeGroupId,
effectiveGroupId,
sendMessageViaManager,
isMuted,
muteAll,
@@ -1264,7 +1283,7 @@ export const useChatScreen = (props?: ChatScreenProps) => {
try {
const segments = buildImageSegments(stickerUrl, stickerUrl, undefined, undefined, replyingTo);
if (isGroupChat && routeGroupId) {
if (isGroupChat && effectiveGroupId) {
await messageService.sendMessageByAction('group', conversationId, segments);
setReplyingTo(null);
} else {
@@ -1282,7 +1301,7 @@ export const useChatScreen = (props?: ChatScreenProps) => {
} finally {
setSendingImage(false);
}
}, [conversationId, isGroupChat, routeGroupId, sendMessageViaManager, isMuted, muteAll, buildImageSegments, replyingTo, canSendPrivateImage, scrollToLatest]);
}, [conversationId, isGroupChat, effectiveGroupId, sendMessageViaManager, isMuted, muteAll, buildImageSegments, replyingTo, canSendPrivateImage, scrollToLatest]);
// 切换表情面板
const toggleEmojiPanel = useCallback(() => {
@@ -1310,7 +1329,7 @@ export const useChatScreen = (props?: ChatScreenProps) => {
// 撤回消息 - 现在通过 MessageManager 处理
const handleRecall = useCallback(async (messageId: string) => {
try {
if (isGroupChat && routeGroupId) {
if (isGroupChat && effectiveGroupId) {
await messageService.recallMessage(messageId);
} else {
await messageService.recallMessage(messageId);
@@ -1320,7 +1339,7 @@ export const useChatScreen = (props?: ChatScreenProps) => {
console.error('撤回消息失败:', error);
Alert.alert('撤回失败', '无法撤回消息');
}
}, [isGroupChat, routeGroupId, conversationId]);
}, [isGroupChat, effectiveGroupId, conversationId]);
// 长按消息显示操作菜单
const handleLongPressMessage = useCallback((message: GroupMessage, position?: MenuPosition) => {
@@ -1455,17 +1474,17 @@ export const useChatScreen = (props?: ChatScreenProps) => {
// 导航到群组信息或用户资料
const navigateToInfo = useCallback(() => {
if (isGroupChat && routeGroupId) {
router.push(hrefs.hrefGroupInfo(routeGroupId, conversationId || undefined));
if (isGroupChat && effectiveGroupId) {
router.push(hrefs.hrefGroupInfo(effectiveGroupId, conversationId || undefined));
} else if (otherUser?.id) {
router.push(hrefs.hrefUserProfile(String(otherUser.id)));
}
}, [isGroupChat, routeGroupId, otherUser, conversationId]);
}, [isGroupChat, effectiveGroupId, otherUser, conversationId]);
// 导航到聊天管理页面
const navigateToChatSettings = useCallback(() => {
if (isGroupChat && routeGroupId) {
router.push(hrefs.hrefGroupInfo(routeGroupId, conversationId || undefined));
if (isGroupChat && effectiveGroupId) {
router.push(hrefs.hrefGroupInfo(effectiveGroupId, conversationId || undefined));
} else if (otherUser?.id && conversationId) {
router.push(
hrefs.hrefPrivateChatInfo({
@@ -1476,7 +1495,7 @@ export const useChatScreen = (props?: ChatScreenProps) => {
})
);
}
}, [isGroupChat, routeGroupId, otherUser, conversationId]);
}, [isGroupChat, effectiveGroupId, otherUser, conversationId]);
return {
// 状态
@@ -1512,8 +1531,8 @@ export const useChatScreen = (props?: ChatScreenProps) => {
muteAll,
followRestrictionHint,
canSendPrivateImage,
routeGroupId,
routeGroupName,
effectiveGroupId,
effectiveGroupName,
otherUserLastReadSeq,
messageMap,
loadingMore,