205 lines
6.8 KiB
TypeScript
205 lines
6.8 KiB
TypeScript
import { Button } from '@tarojs/components';
|
||
import Taro, { UploadTask, useDidHide, useLoad, useUnload } from '@tarojs/taro';
|
||
|
||
import { isEqual } from 'lodash-es';
|
||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||
|
||
import MaterialVideoCard from '@/components/material-video-card';
|
||
import SafeBottomPadding from '@/components/safe-bottom-padding';
|
||
import { EventName, OpenSource, PageUrl } from '@/constants/app';
|
||
import { CollectEventName, ReportEventId } from '@/constants/event';
|
||
import { MaterialVideoInfo } from '@/types/material';
|
||
import { logWithPrefix } from '@/utils/common';
|
||
import { collectEvent, reportEvent } from '@/utils/event';
|
||
import { chooseMedia, postVideos, requestVideoList } from '@/utils/material';
|
||
import { getPageQuery, navigateBack, navigateTo } from '@/utils/route';
|
||
import Toast from '@/utils/toast';
|
||
import { uploadVideo } from '@/utils/video';
|
||
|
||
import './index.less';
|
||
|
||
// 限制 500M
|
||
const MAX_FILE_SIZE_LIMIT = 1024 * 1024 * 1000;
|
||
const PREFIX = 'page-material-upload-video';
|
||
const log = logWithPrefix(PREFIX);
|
||
const TEMP_DATA: MaterialVideoInfo = { url: '', coverUrl: '', title: '', type: 'image', isDefault: false };
|
||
|
||
export default function MaterialUploadVideo() {
|
||
const [source, setSource] = useState(OpenSource.None);
|
||
const [videoList, setVideoList] = useState<MaterialVideoInfo[]>([]);
|
||
const saveRef = useRef<(videos: MaterialVideoInfo[]) => Promise<void>>();
|
||
const lastSaveVideosRef = useRef<MaterialVideoInfo[]>([]);
|
||
|
||
const initVideoList = useCallback(async () => {
|
||
try {
|
||
const res = await requestVideoList();
|
||
lastSaveVideosRef.current = res;
|
||
setVideoList(res);
|
||
} catch (e) {
|
||
console.error(e);
|
||
Toast.error('加载失败请重试');
|
||
}
|
||
}, []);
|
||
|
||
const handleClickDelete = useCallback(
|
||
(video: MaterialVideoInfo) => {
|
||
log('handleClickDelete', video);
|
||
const newVideoList = videoList.filter(v => v.coverUrl !== video.coverUrl);
|
||
setVideoList(newVideoList);
|
||
},
|
||
[videoList]
|
||
);
|
||
|
||
const handleDefaultChange = useCallback(
|
||
(video: MaterialVideoInfo) => {
|
||
log('handleDefaultChange', video);
|
||
const newVideoList = videoList.map(v => ({ ...v, isDefault: v.coverUrl === video.coverUrl }));
|
||
setVideoList(newVideoList);
|
||
},
|
||
[videoList]
|
||
);
|
||
|
||
const handleTitleChange = useCallback(
|
||
(video: MaterialVideoInfo, newTitle: string) => {
|
||
// log('handleTitleChange', video, newTitle);
|
||
const newVideoList = [...videoList];
|
||
const index = newVideoList.findIndex(v => v.coverUrl === video.coverUrl);
|
||
if (index < 0) {
|
||
return;
|
||
}
|
||
newVideoList.splice(index, 1, { ...video, title: newTitle });
|
||
setVideoList(newVideoList);
|
||
},
|
||
[videoList]
|
||
);
|
||
|
||
const handleClickUpload = useCallback(async () => {
|
||
log('click upload');
|
||
let showLoading = false;
|
||
try {
|
||
reportEvent(ReportEventId.CLICK_UPLOAD_VIDEO);
|
||
const media = await chooseMedia();
|
||
if (!media) {
|
||
return;
|
||
}
|
||
const { type, tempFiles } = media;
|
||
log('upload result', type, tempFiles);
|
||
const tempFile = tempFiles[0];
|
||
if (!tempFile) {
|
||
throw new Error('tempFile is not exist');
|
||
}
|
||
if (tempFile.size > MAX_FILE_SIZE_LIMIT) {
|
||
Toast.info('视频超过1000m,请更换', 3000);
|
||
collectEvent(CollectEventName.VIDEO_EXCEEDING_LIMITS);
|
||
return;
|
||
}
|
||
showLoading = true;
|
||
Taro.showLoading({ title: '准备上传' });
|
||
const onProgress: UploadTask.OnProgressUpdateCallback = res => {
|
||
log('上传视频进度', res.progress, '总长度', res.totalBytesExpectedToSend, '已上传的长度', res.totalBytesSent);
|
||
Taro.showLoading({ title: `上传${res.progress}%` });
|
||
};
|
||
const { url, coverUrl } = await uploadVideo(tempFile.tempFilePath, tempFile.fileType, onProgress);
|
||
const newVideo: MaterialVideoInfo = {
|
||
title: '',
|
||
isDefault: false,
|
||
url: url,
|
||
coverUrl: coverUrl,
|
||
type: tempFile.fileType === 'video' ? 'video' : 'image',
|
||
};
|
||
setVideoList([...videoList, newVideo]);
|
||
} catch (e) {
|
||
console.error('upload fail', e);
|
||
Toast.error('上传失败');
|
||
collectEvent(CollectEventName.UPLOAD_VIDEO_FAILED, e);
|
||
} finally {
|
||
showLoading && Taro.hideLoading();
|
||
}
|
||
}, [videoList]);
|
||
|
||
const handleClickSubmit = useCallback(async () => {
|
||
log('handleClickSubmit', videoList);
|
||
reportEvent(ReportEventId.CLICK_SAVE_VIDEOS);
|
||
if (videoList.length < 2) {
|
||
Toast.info('请至少上传 2 个录屏');
|
||
return;
|
||
}
|
||
try {
|
||
Taro.showLoading();
|
||
await saveRef.current?.(videoList);
|
||
Taro.eventCenter.trigger(EventName.CREATE_PROFILE);
|
||
if (source === OpenSource.None) {
|
||
navigateTo(PageUrl.MaterialCreateProfile);
|
||
} else {
|
||
navigateBack();
|
||
}
|
||
} catch (e) {
|
||
Toast.error('保存失败请重试');
|
||
collectEvent(CollectEventName.SAVE_VIDEO_LIST_FAILED, e);
|
||
} finally {
|
||
Taro.hideLoading();
|
||
}
|
||
}, [videoList, source]);
|
||
|
||
useEffect(() => {
|
||
saveRef.current = async (videos: MaterialVideoInfo[]) => {
|
||
if (!videos.length) {
|
||
log('长度为空不保存');
|
||
return;
|
||
}
|
||
if (isEqual(lastSaveVideosRef.current, videos)) {
|
||
log('没变化不保存');
|
||
return;
|
||
}
|
||
lastSaveVideosRef.current = videos;
|
||
await postVideos(videos);
|
||
};
|
||
}, []);
|
||
|
||
useLoad(() => {
|
||
const query = getPageQuery<{ source: OpenSource }>();
|
||
log('query', query);
|
||
const { source: openSource } = query;
|
||
openSource && setSource(openSource);
|
||
initVideoList();
|
||
});
|
||
|
||
useDidHide(() => {
|
||
log('didHide', videoList);
|
||
saveRef.current?.(videoList);
|
||
});
|
||
|
||
useUnload(() => {
|
||
log('unload', videoList);
|
||
saveRef.current?.(videoList);
|
||
});
|
||
|
||
return (
|
||
<div className={PREFIX}>
|
||
<div className={`${PREFIX}__header`}>
|
||
<div className={`${PREFIX}__header-title`}>上传录屏更易获得企业青睐</div>
|
||
<div className={`${PREFIX}__header-tips`}>录屏是企业最关注的资料,建议提供多个风格和品类</div>
|
||
</div>
|
||
<div className={`${PREFIX}__video-list`}>
|
||
{videoList.map(video => (
|
||
<MaterialVideoCard
|
||
key={video.coverUrl}
|
||
videoInfo={video}
|
||
onClickDelete={() => handleClickDelete(video)}
|
||
onClickSetDefault={() => handleDefaultChange(video)}
|
||
onTitleChange={(newTitle: string) => handleTitleChange(video, newTitle)}
|
||
/>
|
||
))}
|
||
{videoList.length < 6 && <MaterialVideoCard videoInfo={TEMP_DATA} onClickUpload={handleClickUpload} isTemp />}
|
||
</div>
|
||
<SafeBottomPadding />
|
||
<div className={`${PREFIX}__footer`}>
|
||
<Button className={`${PREFIX}__submit-btn`} onClick={handleClickSubmit}>
|
||
{source === OpenSource.None ? '下一步' : '保存'}
|
||
</Button>
|
||
<SafeBottomPadding />
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|