diff --git a/xmcl-keystone-ui/src/composables/instanceMods.ts b/xmcl-keystone-ui/src/composables/instanceMods.ts index 2733eccc0..e30987a13 100644 --- a/xmcl-keystone-ui/src/composables/instanceMods.ts +++ b/xmcl-keystone-ui/src/composables/instanceMods.ts @@ -30,11 +30,17 @@ export function useInstanceMods(instancePath: Ref, instanceRuntime: Ref< const enabledModCounts = computed(() => mods.value.filter(v => v.enabled).length) watch([computed(() => state.value?.mods), java], () => { - if (!state.value?.mods) return + if (!state.value?.mods) { + mods.value = [] + return + } updateItems(state.value?.mods, instanceRuntime.value) }) watch(instanceRuntime, () => { - if (!state.value?.mods) return + if (!state.value?.mods) { + mods.value = [] + return + } updateItems(state.value?.mods, instanceRuntime.value) }, { deep: true }) diff --git a/xmcl-keystone-ui/src/composables/syncableState.ts b/xmcl-keystone-ui/src/composables/syncableState.ts index 73fb0cf09..4b44ccc4f 100644 --- a/xmcl-keystone-ui/src/composables/syncableState.ts +++ b/xmcl-keystone-ui/src/composables/syncableState.ts @@ -19,6 +19,7 @@ export function useState(fetcher: (abortSignal: AbortSignal) = // Avoid calling dispose multiple times try { isValidating.value = true + error.value = undefined data = await fetcher(signal) if (!data || signal.aborted) { return } data.subscribeAll((mutation, payload) => { @@ -28,6 +29,7 @@ export function useState(fetcher: (abortSignal: AbortSignal) = } catch (e) { if (signal.aborted) { return } error.value = e + state.value = undefined if (import.meta.env.DEV) console.error(e) } finally { isValidating.value = false diff --git a/xmcl-keystone-ui/src/views/HomeCardBase.vue b/xmcl-keystone-ui/src/views/HomeCardBase.vue index 6502ae269..0a67f867b 100644 --- a/xmcl-keystone-ui/src/views/HomeCardBase.vue +++ b/xmcl-keystone-ui/src/views/HomeCardBase.vue @@ -1,7 +1,7 @@ @@ -16,7 +17,7 @@ import HomeCardBase from './HomeCardBase.vue' const props = defineProps<{ row: number; rowCount: number }>() -const { mods, enabledModCounts, isValidating } = injection(kInstanceModsContext) +const { mods, enabledModCounts, isValidating, error } = injection(kInstanceModsContext) const icons = computed(() => mods.value.filter(i => i.enabled).map((m) => ({ name: m.name + ' (' + m.version + ')', icon: m.icon })) .slice(0, props.row * props.rowCount)) const { push } = useRouter() diff --git a/xmcl-runtime/lib/resources/tryPersistResource.ts b/xmcl-runtime/lib/resources/tryPersistResource.ts index e82d1a263..336f39f29 100644 --- a/xmcl-runtime/lib/resources/tryPersistResource.ts +++ b/xmcl-runtime/lib/resources/tryPersistResource.ts @@ -1,7 +1,7 @@ import { ResourceDomain } from '@xmcl/runtime-api' import { randomBytes } from 'crypto' import filenamify from 'filenamify' -import { rename } from 'fs/promises' +import { rename, stat, unlink } from 'fs/promises' import { dirname, extname, join } from 'path' import { linkOrCopy } from '../util/fs' import { getResourceEntry } from './getResourceEntry' @@ -55,6 +55,31 @@ export async function tryPersistResource(resource: { fileName: string; domain: R existedEntry = await context.db.selectFrom('snapshots').where('domainedPath', '=', `${resource.domain}/${fileName}`).selectAll().executeTakeFirst() } + const fstat = await stat(filePath).catch(e => undefined) + + if (fstat) { + // existed but not in database + // this is a broken resource + const localEntry = await getResourceEntry(filePath, context, true) + if (localEntry.sha1 === resource.hash) { + // The file is already imported... + // Recover db + await context.db.insertInto('snapshots').values({ + domainedPath: entryName, + fileType: localEntry.fileType, + sha1: localEntry.sha1, + size: localEntry.size, + mtime: localEntry.mtime, + ctime: localEntry.ctime, + ino: localEntry.ino, + }).execute().catch(() => undefined) + return filePath + } else { + // Remove the file + await unlink(filePath) + } + } + if (dirname(resource.path) === dirname(filePath)) { // Just rename if they are in same dir await rename(resource.path, filePath) diff --git a/xmcl-runtime/lib/services/ResourceService.ts b/xmcl-runtime/lib/services/ResourceService.ts index 2d729bf4f..d1aa2ee68 100644 --- a/xmcl-runtime/lib/services/ResourceService.ts +++ b/xmcl-runtime/lib/services/ResourceService.ts @@ -307,9 +307,12 @@ export class ResourceService extends AbstractService implements IResourceService this.log(`Persist new resource ${resource.path} -> ${storedPath}`) return resource - })) + }).map(r => r.catch(e => { + this.error(e) + return undefined + }))) - return result + return result.filter(r => r) as Resource[] } async exportResources({ resources, targetDirectory }: ExportResourceOptions) {