Files
WebDrop/web/src/features/item/source.ts
T

54 lines
2.1 KiB
TypeScript

import { createContext, useContext } from 'react';
import type { ArchiveEntry, Item } from '@/core-bridge/types';
import { useShareSession } from '@/store/shareSession';
import { useWorkspace } from '@/store/workspace';
export interface ItemActions {
markSeen: (id: string) => Promise<void>;
editText: (id: string, text: string) => Promise<void>;
resolveConflict: (id: string, choice: 'mine' | 'theirs') => Promise<void>;
}
/**
* 条目组件(行、详情、预览)的数据来源。频道和便捷分享各自提供自己的,
* 这样条目组件不依赖任何一个 store,两边也不会互相牵连。
*/
export interface ItemSource {
useItem: (id: string) => Item | undefined;
/** 返回「设备 id → 名字」的函数 */
useDeviceNamer: () => (deviceId: string | undefined) => string | undefined;
getBlob: (id: string, versionId?: string) => Promise<Blob>;
listArchive: (id: string) => Promise<ArchiveEntry[]>;
/** 只有频道有:分享是只读的 */
actions?: ItemActions;
}
export const channelSource: ItemSource = {
useItem: (id) => useWorkspace((s) => s.byId[id]),
useDeviceNamer: () => {
const devices = useWorkspace((s) => s.devices);
return (deviceId) => devices.find((d) => d.id === deviceId)?.name ?? deviceId;
},
getBlob: (id, versionId) => useWorkspace.getState().getBlob(id, versionId),
listArchive: (id) => useWorkspace.getState().listArchive(id),
actions: {
markSeen: (id) => useWorkspace.getState().markSeen(id),
editText: (id, text) => useWorkspace.getState().editText(id, text),
resolveConflict: (id, choice) => useWorkspace.getState().resolveConflict(id, choice),
},
};
export const shareSource: ItemSource = {
useItem: (id) => useShareSession((s) => s.byId[id]),
// 分享里没有「设备」的概念,内容都来自创建者
useDeviceNamer: () => () => '创建者',
getBlob: (id) => useShareSession.getState().getBlob(id),
listArchive: (id) => useShareSession.getState().listArchive(id),
};
export const ItemSourceContext = createContext<ItemSource>(channelSource);
export function useItemSource(): ItemSource {
return useContext(ItemSourceContext);
}