file-open.ts 2.5 KB

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