- 在 AI 教练聊天界面中添加会话缓存功能,支持冷启动时恢复聊天记录 - 实现轻量防抖机制,确保会话变动时及时保存缓存 - 在打卡功能中集成按月加载打卡记录,提升用户体验 - 更新 Redux 状态管理,支持打卡记录的按月加载和缓存 - 新增打卡日历页面,允许用户查看每日打卡记录 - 优化样式以适应新功能的展示和交互
60 lines
2.1 KiB
TypeScript
60 lines
2.1 KiB
TypeScript
import { buildCosKey, buildPublicUrl } from '@/constants/Cos';
|
|
import { uploadWithRetry } from '@/services/cos';
|
|
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
|
|
export type UseCosUploadOptions = {
|
|
prefix?: string;
|
|
userId?: string;
|
|
contentType?: string;
|
|
};
|
|
|
|
export function useCosUpload(defaultOptions?: UseCosUploadOptions) {
|
|
const abortRef = useRef<AbortController | null>(null);
|
|
const [progress, setProgress] = useState(0);
|
|
const [uploading, setUploading] = useState(false);
|
|
|
|
const cancel = useCallback(() => {
|
|
abortRef.current?.abort();
|
|
}, []);
|
|
|
|
const upload = useCallback(
|
|
async (file: { uri?: string; name?: string; type?: string; buffer?: any; blob?: Blob } | Blob | any, options?: UseCosUploadOptions) => {
|
|
const finalOptions = { ...(defaultOptions || {}), ...(options || {}) };
|
|
const extGuess = (() => {
|
|
const name = (file && (file.name || (file as any).filename)) || '';
|
|
const match = name.match(/\.([a-zA-Z0-9]+)$/);
|
|
return match ? match[1] : undefined;
|
|
})();
|
|
const key = buildCosKey({ prefix: finalOptions.prefix, userId: finalOptions.userId, ext: extGuess });
|
|
const controller = new AbortController();
|
|
abortRef.current = controller;
|
|
setProgress(0);
|
|
setUploading(true);
|
|
try {
|
|
let body = (file as any)?.blob || (file as any)?.buffer || file;
|
|
// Expo ImagePicker 返回 { uri } 时,转换为 Blob
|
|
if (!body && (file as any)?.uri) {
|
|
const resp = await fetch((file as any).uri);
|
|
body = await resp.blob();
|
|
}
|
|
const res = await uploadWithRetry({
|
|
key,
|
|
body,
|
|
contentType: finalOptions.contentType || (file as any)?.type,
|
|
signal: controller.signal,
|
|
onProgress: ({ percent }) => setProgress(percent),
|
|
});
|
|
const url = (res as any).publicUrl || buildPublicUrl(res.key);
|
|
return { key: res.key, url };
|
|
} finally {
|
|
setUploading(false);
|
|
}
|
|
},
|
|
[defaultOptions]
|
|
);
|
|
|
|
return useMemo(() => ({ upload, cancel, progress, uploading }), [upload, cancel, progress, uploading]);
|
|
}
|
|
|
|
|