Skip to content

Instantly share code, notes, and snippets.

@d1y
Created March 14, 2023 07:34
Show Gist options
  • Save d1y/343dd54c4f2bb94d17f418d2ec581436 to your computer and use it in GitHub Desktop.
Save d1y/343dd54c4f2bb94d17f418d2ec581436 to your computer and use it in GitHub Desktop.
import { onMounted, ref, watch, computed } from "vue"
import {
getSyncPendings,
getSyncFinisheds,
getUploadPendings,
getUploadFinisheds,
getDownloadPendings,
getDownloadeds,
} from "@/shared/api/photoalbum"
import {
clearDownloadHistory,
clearUserSyncHistory,
clearUserSelfUploadHistory,
} from "@/shared/api/photoalbum"
import type { SyncStatusType, AlbumFile } from "@/shared/types"
import { backendApi } from "@/shared/api/axios"
import { useStatusChecker } from "@/mobile/use/useStatusChecker"
export interface useUploadItem {
type: string
content?: number
item?: SyncStatusType | AlbumFile
}
export type ISyncStatusType = SyncStatusType | null
const enum ICallbackCustomKey {
pending,
finished,
}
const enum PauseAndResumeDict {
pause,
resume,
}
/*
* 传输类型
*/
export const enum ITransType {
/**
* 上传
*/
upload = "upload",
/**
* 同步
*/
async = "async",
/**
* 下载
*/
download = "download",
}
export const enum ITransTriggerAction {
// 用户手动同步
userSelfSync = "/api/albums/record",
// 自动同步
sync = "/api/sync",
// 下载
download = "/api/download",
}
const getPauseUrl = (type: ITransTriggerAction) => `${type}/pause`
const getResumeUrl = (type: ITransTriggerAction) => `${type}/resume`
/**
* 获取WS接口地址
*/
function getLinkByTransType(type: ITransType): string {
switch (type) {
case ITransType.upload:
return "/api/albums/record/status"
case ITransType.async:
return "/api/sync/status"
case ITransType.download:
return "/api/download/status"
}
}
type FetchListResponse = Promise<{
data: any[]
next: boolean
total: number
}>
type FetchListFunction = (page: number) => FetchListResponse
const KJobMap = new Map<
ITransType,
Record<ICallbackCustomKey, FetchListFunction>
>([
[
ITransType.upload,
{
[ICallbackCustomKey.pending]: getUploadPendings,
[ICallbackCustomKey.finished]: getUploadFinisheds,
},
],
[
ITransType.async,
{
[ICallbackCustomKey.pending]: getSyncPendings,
[ICallbackCustomKey.finished]: getSyncFinisheds,
},
],
[
ITransType.download,
{
[ICallbackCustomKey.pending]: getDownloadPendings,
[ICallbackCustomKey.finished]: getDownloadeds,
},
],
])
/**
* 清除下载历史
*/
async function clearFinishedHistory(type: ITransType) {
switch (type) {
case "download":
return await clearDownloadHistory()
case "async":
return await clearUserSyncHistory()
case "upload":
return await clearUserSelfUploadHistory()
}
}
/**
* 获取准备下载的列表
*/
async function getPending(type: ITransType = ITransType.upload, page = 1) {
const response = await KJobMap.get(type)![ICallbackCustomKey.pending](page)
return response
}
/**
* 获取已完成的列表
*/
async function getFinished(type: ITransType = ITransType.upload, page = 1) {
const response = await KJobMap.get(type)![ICallbackCustomKey.finished](page)
return response
}
/**
* 循环获取数据
*/
async function getDataWithLoop<T>(
type: ITransType,
key: ICallbackCustomKey,
page: number = 1
) {
const fn = KJobMap.get(type)![key]
const res = await fn(page)
let { next, data } = res
if (next) {
const nextPage = page + 1
const newData = await getDataWithLoop(type, key, nextPage)
data = data.concat(newData)
}
return data as T[]
}
async function pauseAndResume(
type: ITransTriggerAction,
dict: PauseAndResumeDict
): Promise<boolean> {
const fn = dict == PauseAndResumeDict.pause ? getPauseUrl : getResumeUrl
const url = fn(type)
try {
await backendApi.get(url)
return true
} catch (error) {
// FIXME(d1y): api fail
console.error(error)
return false
}
}
function calcSyncInfoText(
item: ISyncStatusType,
type: ITransType = ITransType.upload
): string {
if (item && item.current_file !== "") {
if (item.status === "running") {
let currentPhotoSize = (item.current_size / (1024 * 1024)).toFixed(2)
let downloadedSize = (item.current_downloaded / (1024 * 1024)).toFixed(2)
return `${downloadedSize}M/${currentPhotoSize}M`
} else {
return type === "upload" ? "暂停同步" : "暂停下载"
}
}
return "暂无同步数据"
}
export default (type: ITransType, actionType: ITransTriggerAction) => {
const statusInfo = ref<ISyncStatusType>(null)
const currentWSURL = getLinkByTransType(type)
const pendingItems = ref<AlbumFile[]>([])
const finishItems = ref<AlbumFile[]>([])
let _pauseLock = false
let _resumeLock = false
async function getPendingItems() {
return await getDataWithLoop<AlbumFile>(type, ICallbackCustomKey.pending)
}
async function getFinishItems() {
return await getDataWithLoop<AlbumFile>(type, ICallbackCustomKey.finished)
}
function updatePendingAndFinishedInfo() {
getPendingItems().then((data) => {
pendingItems.value = data
})
getFinishItems().then((data) => {
finishItems.value = data
})
}
async function cleanHistory() {
await clearFinishedHistory(type)
finishItems.value = []
}
const { signal } = useStatusChecker(currentWSURL, (response: any)=> {
const result = JSON.parse(response)
const { type, data } = result
if (type === "status") {
statusInfo.value = data
} else if (type === "done") {
const pendingIndex = pendingItems.value.findIndex((t) => t.id === result.data.id)
finishItems.value.unshift(...pendingItems.value.splice(pendingIndex, 1))
if (pendingItems.value.length == 0) {
statusInfo.value = null
}
} else if (type == "idle") {
statusInfo.value = null
}
})
const list = computed<useUploadItem[]>(() => {
let tasklist: useUploadItem[] = []
if (statusInfo.value) {
const offset = statusInfo.value.total - statusInfo.value.current
tasklist = tasklist.concat([
{
type: "pendingTitle",
content: offset,
},
{
type: "current",
item: statusInfo.value,
},
])
}
if (pendingItems.value.length > 0) {
tasklist = tasklist.concat(
pendingItems.value.slice(1).map((item) => {
return {
type: "pending",
item: item,
}
})
)
}
if (finishItems.value.length > 0) {
tasklist.push({
type: "finishTitle",
content: finishItems.value.length,
})
tasklist = tasklist.concat(
finishItems.value.map((item) => {
return {
type: "finish",
item: item,
}
})
)
}
return tasklist
})
function init() {
updatePendingAndFinishedInfo()
}
async function pause() {
if (_pauseLock) return
_pauseLock = true
await pauseAndResume(actionType, PauseAndResumeDict.pause)
_pauseLock = false
}
async function resume() {
if (_resumeLock) return
_resumeLock = true
await pauseAndResume(actionType, PauseAndResumeDict.resume)
_pauseLock = false
}
function dispose() {
_resumeLock = false
_pauseLock = false
}
return {
updatePendingAndFinishedInfo,
cleanHistory,
pause,
resume,
dispose,
init,
list,
statusInfo,
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment