import { previewKindOf } from '../components/preview/file-kind'; /** * 文件点击后的打开方式(2026-11-06) * * ## 用户裁决 * * 「文件的预览形态保持当前逻辑。需要实现的是:**点击文件卡片之后, * 右侧侧边栏会自动打开进行预览**。」并补充:**图片走灯箱,其余走侧边栏**。 * * ## 为什么要抽成纯函数 * * 原先这段分流逻辑散在 `desktop/pages/chat/chat.ts#openFile`、 * `mobile/pages/chat/chat.ts#openFile`、`mobile/components/m-msg/m-msg.ts#openFile` * 三处,且**右栏列表点击完全不经过它**(`session-rail.ts` 自己 set 了预览, * 结果图片也被塞进右栏内联预览)—— 这就是「同一张图,从消息里点和从右栏点 * 表现不同」的根源。收成一份,所有入口共用。 * * 纯函数,便于单测;不依赖 Angular 与任何组件。 */ /** 打开目标 */ export type FileOpenTarget = /** 图片:全屏灯箱 */ | { kind: 'lightbox'; file: OpenableFile } /** 音视频:模态播放器 */ | { kind: 'media'; file: OpenableFile } /** 其余(PDF / 文档 / 表格 / 文本…):右侧栏内联预览 */ | { kind: 'rail'; file: OpenableFile }; /** 分流所需的最小字段集(`FileCard`、`RailFile` 都能满足) */ export interface OpenableFile { name: string; url: string; mimeType?: string; size?: number; } /** * 按文件类型决定打开到哪里。 * * 判定复用 `previewKindOf`(扩展名优先,mimeType 兜底), * 与 `file-preview` 组件内部使用的口径**完全一致**, * 避免「分流认为是图片、预览组件却当成文本」这类错配。 */ export function routeOpen(f: OpenableFile): FileOpenTarget { const file: OpenableFile = { name: f.name, url: f.url, mimeType: f.mimeType ?? '', size: f.size, }; // 没有地址:按「右栏」处理,由预览组件给出「文件还没有同步到云盘」的明确提示。 // 不在这里吞掉 —— 用户点了要有反馈,不能点了没反应。 if (!file.url) return { kind: 'rail', file }; const kind = previewKindOf(file.name || file.url, file.mimeType); if (kind === 'image') return { kind: 'lightbox', file }; if (kind === 'video' || kind === 'audio') return { kind: 'media', file }; return { kind: 'rail', file }; } /** 该文件是否能在右栏预览(供禁用态判断) */ export function canPreviewInRail(f: OpenableFile): boolean { return routeOpen(f).kind === 'rail'; }