128 lines
4.1 KiB
TypeScript
128 lines
4.1 KiB
TypeScript
import { needsImageDecrypt, parseImageExtra, toBusinessImagePath } from '@/utils/im/message';
|
||
|
||
/** 消息中心私信列表的一行数据(纯 SDK 会话映射) */
|
||
export type PrivateSessionItem = {
|
||
id: string;
|
||
/** 业务 uid;列表由 SDK 驱动时为 0,进会话前用 /im/user-id 反查 */
|
||
userId: number;
|
||
/** 对端 IM userId(= conv.receiverId) */
|
||
friendImUserId: number;
|
||
userName: string;
|
||
userAvatar: string;
|
||
preContent: string;
|
||
createdAt: string | number;
|
||
noReadNum: number;
|
||
};
|
||
|
||
/** SDK conversation.subscribeList 推送的会话结构 */
|
||
export type SdkConversation = {
|
||
type?: string;
|
||
receiverId?: number | string;
|
||
unreadCount?: number;
|
||
extra?: string;
|
||
lastMessageTime?: number;
|
||
updatedAt?: number;
|
||
createdAt?: number;
|
||
content?: string;
|
||
chatMessageType?: string | number;
|
||
user?: {
|
||
id?: number;
|
||
nickName?: string;
|
||
nickname?: string;
|
||
name?: string;
|
||
avatar?: string;
|
||
portrait?: string;
|
||
imgUrl?: string;
|
||
};
|
||
remark?: string;
|
||
};
|
||
|
||
// —— 展示文案 ——
|
||
|
||
/** blob/data 本地地址、http(s) 公网图无需再走 imgDecypt */
|
||
export function isPublicImageUrl(url?: string): boolean {
|
||
return !needsImageDecrypt(url);
|
||
}
|
||
|
||
export function formatSessionPreview(content?: string): string {
|
||
const text = (content || '').trim();
|
||
if (!text) return '暂无消息';
|
||
if (/^\[图片\]|^图片$|image/i.test(text)) return '[图片]';
|
||
return text;
|
||
}
|
||
|
||
export function formatSessionListTime(date: string | number): string {
|
||
if (date == null || date === '') return '';
|
||
const d = typeof date === 'number' ? new Date(date) : new Date(date);
|
||
if (Number.isNaN(d.getTime()) || d.getFullYear() === 1) return '';
|
||
return `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`;
|
||
}
|
||
|
||
export function buildMessagePreview(msg: {
|
||
content?: string;
|
||
chatMessageType?: string | number;
|
||
extra?: unknown;
|
||
}): string {
|
||
const type = msg.chatMessageType;
|
||
if (type === 'IMAGE' || type === 2 || parseImageExtra(msg.extra)) return '[图片]';
|
||
return (msg.content || '').trim() || '暂无消息';
|
||
}
|
||
|
||
// —— SDK 会话 → 列表行 ——
|
||
|
||
function toTimestamp(value: string | number | undefined): number {
|
||
if (value == null || value === '') return 0;
|
||
const ts = typeof value === 'number' ? value : new Date(value).getTime();
|
||
return Number.isFinite(ts) ? ts : 0;
|
||
}
|
||
|
||
function isDirectConversation(conv: SdkConversation): boolean {
|
||
if (conv.type && conv.type !== 'direct') return false;
|
||
const imId = Number(conv.receiverId);
|
||
return Number.isFinite(imId) && imId > 0;
|
||
}
|
||
|
||
/** IM 快照偶发幽灵会话(user 资料整体缺失),无法渲染也无法跳转,不展示 */
|
||
function hasUserProfile(conv: SdkConversation): boolean {
|
||
const user = conv.user;
|
||
return !!(user?.nickName || user?.nickname || user?.name || user?.avatar || user?.portrait || user?.imgUrl);
|
||
}
|
||
|
||
function toSessionItem(conv: SdkConversation): PrivateSessionItem {
|
||
const imId = Number(conv.receiverId);
|
||
const user = conv.user;
|
||
|
||
return {
|
||
id: String(imId),
|
||
userId: 0,
|
||
friendImUserId: imId,
|
||
userName: user?.nickName || user?.nickname || user?.name || conv.remark || '',
|
||
userAvatar: toBusinessImagePath(user?.avatar || user?.portrait || user?.imgUrl || ''),
|
||
preContent: parseImageExtra(conv.extra)
|
||
? '[图片]'
|
||
: buildMessagePreview({
|
||
content: conv.content,
|
||
chatMessageType: conv.chatMessageType,
|
||
extra: conv.extra,
|
||
}),
|
||
createdAt: conv.lastMessageTime ?? conv.updatedAt ?? conv.createdAt ?? '',
|
||
noReadNum: Number(conv.unreadCount ?? 0),
|
||
};
|
||
}
|
||
|
||
/** SDK 会话快照 → 私信列表(仅单聊;按最后消息时间倒序) */
|
||
export function mapSdkConversations(conversations: SdkConversation[]): PrivateSessionItem[] {
|
||
const seen = new Set<number>();
|
||
const list: PrivateSessionItem[] = [];
|
||
|
||
for (const conv of conversations) {
|
||
if (!isDirectConversation(conv) || !hasUserProfile(conv)) continue;
|
||
const imId = Number(conv.receiverId);
|
||
if (seen.has(imId)) continue;
|
||
seen.add(imId);
|
||
list.push(toSessionItem(conv));
|
||
}
|
||
|
||
return list.sort((a, b) => toTimestamp(b.createdAt) - toTimestamp(a.createdAt));
|
||
}
|