fix: call media-connect state, blocklist banner staleness, group rename, avatar URL
- stores/calls.ts, CallRoot.tsx: distinguish signaling-connected from actual media-joined (mediaJoined flag), so a call whose mic never attaches no longer shows a live timer/"网络良好"; propagate a disconnected reason on post-accept media failure so both sides agree on the outcome instead of one side seeing a normal "已结束". - DetailColumn.tsx: invalidate conversation-context alongside blocks on block/unblock, so the composer's blocked-notice banner updates immediately instead of only after a window-focus refetch. - GroupDetailColumn.tsx: add an owner-only group rename control (updateGroup was already implemented but never wired to any UI). - Avatar.tsx: stop re-wrapping an already-absolute avatar_file_id (seen on default-avatar placeholders) into a malformed /v0/file/s/https://... request.
This commit is contained in:
@@ -19,12 +19,15 @@ import {
|
||||
getGroupSettings,
|
||||
getGroupSummary,
|
||||
leaveGroup,
|
||||
updateGroup,
|
||||
updateGroupMemberSettings,
|
||||
updateMyGroupNickname,
|
||||
type GroupMember,
|
||||
type GroupProfile,
|
||||
type GroupSettings,
|
||||
} from '@/lib/api/groups';
|
||||
import type { MediaType } from '@/lib/api/messages';
|
||||
import { MAX_TITLE_LENGTH } from '@/lib/config';
|
||||
import { useTinode } from '@/lib/tinode/hooks';
|
||||
import { forgetTopic } from '@/lib/tinode/topics';
|
||||
import { requestConfirm } from '@/stores/confirmDialog';
|
||||
@@ -81,6 +84,13 @@ export function GroupDetailColumn({ topicId, peerName, onClose, onOpenHistory }:
|
||||
const myUserId = useSessionStore((s) => s.myUserId);
|
||||
const [nicknameEditing, setNicknameEditing] = useState(false);
|
||||
const [nicknameDraft, setNicknameDraft] = useState('');
|
||||
// `updateGroup` (lib/api/groups.ts) has always supported renaming a group -- confirmed live no UI
|
||||
// anywhere ever called it (grep across the whole repo: zero call sites), so a group's name was
|
||||
// fixed forever at whatever was typed into CreateGroupModal's input at creation time, with no way
|
||||
// to fix a typo or rename it later. Owner-only, matching this file's other high-consequence group-
|
||||
// level (as opposed to per-member) actions.
|
||||
const [nameEditing, setNameEditing] = useState(false);
|
||||
const [nameDraft, setNameDraft] = useState('');
|
||||
const [errorToast, setErrorToast] = useState('');
|
||||
const [openModal, setOpenModal] = useState<'members' | 'invite' | 'announcement' | 'files' | 'admin' | 'qrcode' | null>(null);
|
||||
const showError = (err: unknown, fallback: string) => setErrorToast(err instanceof Error ? err.message : fallback);
|
||||
@@ -90,6 +100,8 @@ export function GroupDetailColumn({ topicId, peerName, onClose, onOpenHistory }:
|
||||
useLayoutEffect(() => {
|
||||
setNicknameEditing(false);
|
||||
setNicknameDraft('');
|
||||
setNameEditing(false);
|
||||
setNameDraft('');
|
||||
setErrorToast('');
|
||||
setOpenModal(null);
|
||||
}, [topicId]);
|
||||
@@ -152,6 +164,17 @@ export function GroupDetailColumn({ topicId, peerName, onClose, onOpenHistory }:
|
||||
onError: (err) => showError(err, '昵称保存失败'),
|
||||
});
|
||||
|
||||
const nameMutation = useMutation({
|
||||
mutationFn: (name: string) => updateGroup(topicId, { name, version: groupQuery.data?.version ?? 1 }),
|
||||
// Merge, not replace -- same defensive reasoning as updateSettings above, in case this endpoint
|
||||
// ever turns out to return a partial object like member-settings did.
|
||||
onSuccess: (data) => {
|
||||
queryClient.setQueryData(['group', topicId], (prev: GroupProfile | undefined) => (prev ? { ...prev, ...data } : data));
|
||||
setNameEditing(false);
|
||||
},
|
||||
onError: (err) => showError(err, '群名保存失败'),
|
||||
});
|
||||
|
||||
// Both mutations call forgetTopic() the instant their own REST call succeeds, rather than waiting
|
||||
// on the async native/`group.dissolved` gone-signal that normally follows a group actually going
|
||||
// away -- real, user-reported bugs fixed by this: (1) the chat list kept showing a broken stale
|
||||
@@ -259,8 +282,49 @@ export function GroupDetailColumn({ topicId, peerName, onClose, onOpenHistory }:
|
||||
avatarFileId={groupQuery.data?.avatar_file_id}
|
||||
className="h-13 w-13 flex-none rounded-xl text-xl"
|
||||
/>
|
||||
<div className="flex min-w-0 flex-col">
|
||||
<strong className="truncate text-base font-semibold text-text-heading">{groupQuery.data?.name || peerName}</strong>
|
||||
<div className="flex min-w-0 flex-1 flex-col">
|
||||
{nameEditing ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
autoFocus
|
||||
value={nameDraft}
|
||||
onChange={(e) => setNameDraft(e.target.value.slice(0, MAX_TITLE_LENGTH))}
|
||||
onKeyDown={(e) => e.key === 'Enter' && nameDraft.trim() && nameMutation.mutate(nameDraft.trim())}
|
||||
className="min-w-0 flex-1 rounded-md bg-surface-input px-2 py-1 text-sm outline-none"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => nameDraft.trim() && nameMutation.mutate(nameDraft.trim())}
|
||||
disabled={!nameDraft.trim() || nameMutation.isPending}
|
||||
className="flex-none text-sm text-accent disabled:opacity-40"
|
||||
>
|
||||
保存
|
||||
</button>
|
||||
<button type="button" onClick={() => setNameEditing(false)} className="flex-none text-sm text-text-tertiary-2">
|
||||
取消
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<span className="flex min-w-0 items-center gap-1">
|
||||
<strong className="truncate text-base font-semibold text-text-heading">{groupQuery.data?.name || peerName}</strong>
|
||||
{/* Owner-only -- renaming a group is a group-level action, not a per-member one
|
||||
like the 我在本群的昵称 row further down, so this follows this file's other
|
||||
owner-gated actions (转让群主/解散群聊) rather than opening it to admins too. */}
|
||||
{isOwner ? (
|
||||
<button
|
||||
type="button"
|
||||
aria-label="修改群名"
|
||||
onClick={() => {
|
||||
setNameDraft(groupQuery.data?.name || '');
|
||||
setNameEditing(true);
|
||||
}}
|
||||
className="flex-none text-text-tertiary-3"
|
||||
>
|
||||
<Icon name="edit" className="text-[16px]" />
|
||||
</button>
|
||||
) : null}
|
||||
</span>
|
||||
)}
|
||||
<span className="text-xs text-text-tertiary-2">
|
||||
群号 {topicId} · {memberCount} 人
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user