mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-06 16:16:42 +08:00
fix(plugins): reconcile runtime changes after tab switch (#695)
This commit is contained in:
@@ -218,6 +218,7 @@ const isMarketRefreshing = ref(false)
|
|||||||
const pluginRuntimeSummary = computed(() => pluginRuntimeStore.summary)
|
const pluginRuntimeSummary = computed(() => pluginRuntimeStore.summary)
|
||||||
const installingPluginIds = ref(new Set<string>())
|
const installingPluginIds = ref(new Set<string>())
|
||||||
const isPluginPageActive = ref(false)
|
const isPluginPageActive = ref(false)
|
||||||
|
const appliedRuntimeReconciliation = ref(0)
|
||||||
|
|
||||||
// 每类远程快照独立管理 writer 代际,旧请求只能完成自身 Promise,不能覆盖新状态。
|
// 每类远程快照独立管理 writer 代际,旧请求只能完成自身 Promise,不能覆盖新状态。
|
||||||
let installedWriterGeneration = 0
|
let installedWriterGeneration = 0
|
||||||
@@ -1038,6 +1039,17 @@ async function fetchInstalledPlugins(context: KeepAliveRefreshContext = {}): Pro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 运行态变化在市场页发生时延后应用,回到已安装列表后再补一次快照。 */
|
||||||
|
async function reconcileInstalledRuntime(): Promise<void> {
|
||||||
|
const reconciliation = pluginRuntimeStore.reconciliation
|
||||||
|
if (reconciliation <= appliedRuntimeReconciliation.value) return
|
||||||
|
|
||||||
|
const refreshed = await fetchInstalledPlugins({ silent: true })
|
||||||
|
if (refreshed && reconciliation === pluginRuntimeStore.reconciliation) {
|
||||||
|
appliedRuntimeReconciliation.value = reconciliation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function isPluginRuntimeSettling(pluginId: string) {
|
function isPluginRuntimeSettling(pluginId: string) {
|
||||||
return pluginRuntimeSummary.value?.ready === false || installingPluginIds.value.has(pluginId)
|
return pluginRuntimeSummary.value?.ready === false || installingPluginIds.value.has(pluginId)
|
||||||
}
|
}
|
||||||
@@ -1448,19 +1460,26 @@ watch(activeTab, (newTab, oldTab) => {
|
|||||||
if (generation !== tabScrollRestoreGeneration) return
|
if (generation !== tabScrollRestoreGeneration) return
|
||||||
window.scrollTo({ behavior: 'auto', top: tabScrollPositions[newTab] })
|
window.scrollTo({ behavior: 'auto', top: tabScrollPositions[newTab] })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (newTab === 'installed' && isPluginPageActive.value && !document.hidden) {
|
||||||
|
void reconcileInstalledRuntime()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => pluginRuntimeStore.reconciliation,
|
() => pluginRuntimeStore.reconciliation,
|
||||||
() => {
|
() => {
|
||||||
if (isPluginPageActive.value && activeTab.value === 'installed' && !document.hidden) {
|
if (isPluginPageActive.value && activeTab.value === 'installed' && !document.hidden) {
|
||||||
void fetchInstalledPlugins({ silent: true })
|
void reconcileInstalledRuntime()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
onActivated(() => {
|
onActivated(() => {
|
||||||
isPluginPageActive.value = true
|
isPluginPageActive.value = true
|
||||||
|
if (activeTab.value === 'installed' && !document.hidden) {
|
||||||
|
void reconcileInstalledRuntime()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
onDeactivated(() => {
|
onDeactivated(() => {
|
||||||
|
|||||||
@@ -794,6 +794,30 @@ describe('PluginCardListView loading and request ownership', () => {
|
|||||||
await waitForRequestsToFinish()
|
await waitForRequestsToFinish()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('applies a runtime generation received while the market tab is open', async () => {
|
||||||
|
let installedRequests = 0
|
||||||
|
const { pinia } = await renderList({
|
||||||
|
installed: () => {
|
||||||
|
installedRequests += 1
|
||||||
|
return [createPlugin({ id: 'Installed', installed: true, plugin_name: '已安装插件' })]
|
||||||
|
},
|
||||||
|
market: () => [createPlugin({ id: 'Market', plugin_name: '市场插件' })],
|
||||||
|
})
|
||||||
|
await waitForRequestsToFinish()
|
||||||
|
const initialInstalledRequests = installedRequests
|
||||||
|
const runtimeStore = usePluginRuntimeStore(pinia)
|
||||||
|
|
||||||
|
getHeaderConfig().modelValue.value = 'market'
|
||||||
|
await nextTick()
|
||||||
|
runtimeStore.reconciliation = 1
|
||||||
|
await nextTick()
|
||||||
|
expect(installedRequests).toBe(initialInstalledRequests)
|
||||||
|
|
||||||
|
getHeaderConfig().modelValue.value = 'installed'
|
||||||
|
await waitFor(() => expect(installedRequests).toBe(initialInstalledRequests + 1))
|
||||||
|
await waitForRequestsToFinish()
|
||||||
|
})
|
||||||
|
|
||||||
it('does not request the superuser runtime summary for an ordinary administrator', async () => {
|
it('does not request the superuser runtime summary for an ordinary administrator', async () => {
|
||||||
server.use(
|
server.use(
|
||||||
http.get(apiUrls.runtime, () => {
|
http.get(apiUrls.runtime, () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user