fix(sources): hide unavailable update and management entries

This commit is contained in:
2026-04-16 13:04:54 +08:00
parent e1ec526cb9
commit 0b784af3d7
16 changed files with 667 additions and 58 deletions
+69 -7
View File
@@ -20,6 +20,7 @@
:active-category="activeCategory"
:category-counts="categoryCounts"
:theme-mode="themeMode"
:spark-available="sparkAvailable"
:apm-available="apmAvailable"
:store-filter="storeFilter"
@toggle-theme="toggleTheme"
@@ -120,6 +121,7 @@
:error="installedError"
:active-origin="activeInstalledOrigin"
:store-filter="storeFilter"
:spark-available="sparkAvailable"
:apm-available="apmAvailable"
@close="closeInstalledModal"
@refresh="refreshInstalledApps"
@@ -192,6 +194,12 @@ import {
rankAppsBySearch,
} from "./modules/appSearch";
import { handleInstall, handleRetry } from "./modules/processInstall";
import {
getAllowedInstalledOrigin,
getEffectiveStoreFilter,
getDefaultInstalledOrigin,
isOriginEnabled,
} from "./modules/storeFilter";
import { createUpdateCenterStore } from "./modules/updateCenter";
import type {
App,
@@ -264,10 +272,18 @@ const showUninstallModal = ref(false);
const uninstallTargetApp: Ref<App | null> = ref(null);
const showAboutModal = ref(false);
const showSettingsModal = ref(false);
const sparkAvailable = ref(false);
const apmAvailable = ref(false);
/** 启动参数 --no-apm => 仅 Spark--no-spark => 仅 APM;由主进程 IPC 提供 */
const storeFilter = ref<"spark" | "apm" | "both">("both");
const availableSources = computed(() => ({
spark: sparkAvailable.value,
apm: apmAvailable.value,
}));
const effectiveStoreFilter = computed(() =>
getEffectiveStoreFilter(storeFilter.value, availableSources.value),
);
// 计算属性
const baseApps = computed(() => {
@@ -761,7 +777,11 @@ const handleList = () => {
const openUpdateModal = async () => {
try {
await updateCenterStore.open();
if (!effectiveStoreFilter.value) {
return;
}
await updateCenterStore.open(effectiveStoreFilter.value);
} catch (error) {
logger.error(`打开更新中心失败: ${error}`);
}
@@ -791,11 +811,21 @@ const confirmMigrationStart = async () => {
};
const openInstalledModal = () => {
showInstalledModal.value = true;
// 如果没有 APM 可用,默认切换到 Spark 应用管理
if (!apmAvailable.value && activeInstalledOrigin.value === "apm") {
activeInstalledOrigin.value = "spark";
const defaultOrigin = getDefaultInstalledOrigin(
storeFilter.value,
availableSources.value,
);
if (!defaultOrigin) {
return;
}
showInstalledModal.value = true;
activeInstalledOrigin.value =
getAllowedInstalledOrigin(
storeFilter.value,
activeInstalledOrigin.value,
availableSources.value,
) ?? defaultOrigin;
refreshInstalledApps();
};
@@ -804,7 +834,12 @@ const closeInstalledModal = () => {
};
const handleSwitchOrigin = (origin: "apm" | "spark") => {
activeInstalledOrigin.value = origin;
activeInstalledOrigin.value =
getAllowedInstalledOrigin(
storeFilter.value,
origin,
availableSources.value,
) ?? activeInstalledOrigin.value;
refreshInstalledApps();
};
@@ -812,7 +847,24 @@ const refreshInstalledApps = async () => {
installedLoading.value = true;
installedError.value = "";
try {
const origin = activeInstalledOrigin.value;
const origin = getAllowedInstalledOrigin(
storeFilter.value,
activeInstalledOrigin.value,
availableSources.value,
);
if (!origin) {
installedApps.value = [];
installedError.value = "当前系统不可用应用管理功能";
return;
}
activeInstalledOrigin.value = origin;
if (!isOriginEnabled(storeFilter.value, origin)) {
installedApps.value = [];
installedError.value = `当前启动模式已禁用 ${origin === "spark" ? "Spark" : "APM"} 软件管理`;
return;
}
// Spark 优化:只检查远端商店目录中的应用,避免全量扫描
let pkgnameList: string[] | undefined;
@@ -1151,11 +1203,21 @@ onMounted(async () => {
// 从主进程获取启动参数(--no-apm / --no-spark),再加载数据
storeFilter.value = await window.ipcRenderer.invoke("get-store-filter");
if (storeFilter.value !== "apm") {
sparkAvailable.value = await window.ipcRenderer.invoke(
"check-spark-available",
);
}
// 检查 apm 是否可用
if (storeFilter.value !== "spark") {
apmAvailable.value = await window.ipcRenderer.invoke("check-apm-available");
}
activeInstalledOrigin.value =
getDefaultInstalledOrigin(storeFilter.value, availableSources.value) ??
"spark";
await loadCategories();
// 分类目录加载后,并行加载主页数据和所有应用列表
+37
View File
@@ -0,0 +1,37 @@
import { render, screen } from "@testing-library/vue";
import { describe, expect, it } from "vitest";
import AppSidebar from "@/components/AppSidebar.vue";
const renderSidebar = (
overrides: Partial<InstanceType<typeof AppSidebar>["$props"]> = {},
) => {
return render(AppSidebar, {
props: {
categories: {},
activeCategory: "all",
categoryCounts: { all: 0 },
themeMode: "auto",
storeFilter: "both",
sparkAvailable: true,
apmAvailable: true,
...overrides,
},
});
};
describe("AppSidebar", () => {
it("shows management and update entries when at least one source is usable", () => {
renderSidebar({ sparkAvailable: true, apmAvailable: false });
expect(screen.getByText("应用管理")).toBeTruthy();
expect(screen.getByText("软件更新")).toBeTruthy();
});
it("hides management and update entries when both sources are unavailable", () => {
renderSidebar({ sparkAvailable: false, apmAvailable: false });
expect(screen.queryByText("应用管理")).toBeNull();
expect(screen.queryByText("软件更新")).toBeNull();
});
});
@@ -35,6 +35,7 @@ describe("InstalledAppsModal", () => {
error: "",
activeOrigin: "spark",
storeFilter: "both",
sparkAvailable: true,
apmAvailable: true,
},
});
@@ -54,6 +55,7 @@ describe("InstalledAppsModal", () => {
error: "",
activeOrigin: "spark",
storeFilter: "both",
sparkAvailable: true,
apmAvailable: true,
},
});
@@ -71,6 +73,7 @@ describe("InstalledAppsModal", () => {
error: "",
activeOrigin: "spark",
storeFilter: "both",
sparkAvailable: true,
apmAvailable: true,
},
});
@@ -92,6 +95,7 @@ describe("InstalledAppsModal", () => {
error: "",
activeOrigin: "spark",
storeFilter: "both",
sparkAvailable: true,
apmAvailable: true,
},
});
@@ -113,6 +117,7 @@ describe("InstalledAppsModal", () => {
error: "",
activeOrigin: "spark",
storeFilter: "both",
sparkAvailable: true,
apmAvailable: true,
},
});
@@ -129,6 +134,7 @@ describe("InstalledAppsModal", () => {
error: "",
activeOrigin: "spark",
storeFilter: "both",
sparkAvailable: true,
apmAvailable: true,
},
});
+80
View File
@@ -0,0 +1,80 @@
import { describe, expect, it } from "vitest";
import {
getEffectiveStoreFilter,
getAllowedInstalledOrigin,
getDefaultInstalledOrigin,
isOriginEnabled,
isOriginUsable,
} from "@/modules/storeFilter";
describe("storeFilter helpers", () => {
it("reports whether an origin is enabled by the current store filter", () => {
expect(isOriginEnabled("both", "spark")).toBe(true);
expect(isOriginEnabled("both", "apm")).toBe(true);
expect(isOriginEnabled("spark", "spark")).toBe(true);
expect(isOriginEnabled("spark", "apm")).toBe(false);
expect(isOriginEnabled("apm", "apm")).toBe(true);
expect(isOriginEnabled("apm", "spark")).toBe(false);
});
it("chooses the default installed origin from the active store filter", () => {
expect(getDefaultInstalledOrigin("spark", { spark: true, apm: true })).toBe(
"spark",
);
expect(getDefaultInstalledOrigin("apm", { spark: true, apm: true })).toBe(
"apm",
);
expect(getDefaultInstalledOrigin("both", { spark: true, apm: true })).toBe(
"apm",
);
expect(getDefaultInstalledOrigin("both", { spark: true, apm: false })).toBe(
"spark",
);
expect(
getDefaultInstalledOrigin("both", { spark: false, apm: false }),
).toBe(null);
});
it("redirects disallowed installed origins to an allowed one", () => {
expect(
getAllowedInstalledOrigin("spark", "apm", { spark: true, apm: true }),
).toBe("spark");
expect(
getAllowedInstalledOrigin("apm", "spark", { spark: true, apm: true }),
).toBe("apm");
expect(
getAllowedInstalledOrigin("both", "apm", { spark: true, apm: false }),
).toBe("spark");
expect(
getAllowedInstalledOrigin("both", "spark", { spark: false, apm: false }),
).toBeNull();
});
it("computes the effective runtime store filter from source availability", () => {
expect(getEffectiveStoreFilter("both", { spark: true, apm: true })).toBe(
"both",
);
expect(getEffectiveStoreFilter("both", { spark: true, apm: false })).toBe(
"spark",
);
expect(getEffectiveStoreFilter("both", { spark: false, apm: true })).toBe(
"apm",
);
expect(getEffectiveStoreFilter("both", { spark: false, apm: false })).toBe(
null,
);
});
it("only treats enabled and installed origins as usable", () => {
expect(isOriginUsable("both", "spark", { spark: true, apm: false })).toBe(
true,
);
expect(isOriginUsable("both", "apm", { spark: true, apm: false })).toBe(
false,
);
expect(isOriginUsable("spark", "apm", { spark: true, apm: true })).toBe(
false,
);
});
});
@@ -25,6 +25,9 @@ const APTSS_WEATHER_PRINT_URIS_KEY =
const APTSS_NOTES_PRINT_URIS_KEY =
"bash -lc /usr/bin/apt download spark-notes --print-uris -c /opt/durapps/spark-store/bin/apt-fast-conf/aptss-apt.conf -o Dir::Etc::sourcelist=/opt/durapps/spark-store/bin/apt-fast-conf/sources.list.d/aptss.list -o Dir::Etc::sourceparts=/dev/null";
const WHICH_APTSS_KEY = "which aptss";
const WHICH_APM_KEY = "which apm";
const loadUpdateCenterModule = async (
remoteStore: Record<string, RemoteStoreResponse>,
) => {
@@ -106,6 +109,22 @@ afterEach(() => {
describe("update-center load items", () => {
it("enriches apm migration items with download metadata and remote fallback icons", async () => {
const commandResults = new Map<string, CommandResult>([
[
WHICH_APTSS_KEY,
{
code: 0,
stdout: "/usr/bin/aptss\n",
stderr: "",
},
],
[
WHICH_APM_KEY,
{
code: 0,
stdout: "/usr/bin/apm\n",
stderr: "",
},
],
[
APTSS_LIST_UPGRADABLE_KEY,
{
@@ -217,6 +236,14 @@ describe("update-center load items", () => {
const result = await loadUpdateCenterItems(async (command, args) => {
const key = `${command} ${args.join(" ")}`;
if (key === WHICH_APTSS_KEY) {
return { code: 0, stdout: "/usr/bin/aptss\n", stderr: "" };
}
if (key === WHICH_APM_KEY) {
return { code: 127, stdout: "", stderr: "apm: command not found" };
}
if (key === APTSS_LIST_UPGRADABLE_KEY) {
return {
code: 0,
@@ -279,10 +306,7 @@ describe("update-center load items", () => {
sha512: "beadfeed",
},
]);
expect(result.warnings).toEqual([
"apm upgradable query failed: apm: command not found",
"apm installed query failed: apm: command not found",
]);
expect(result.warnings).toEqual([]);
});
it("retries category lookup after an earlier fetch failure in the same process", async () => {
@@ -292,6 +316,14 @@ describe("update-center load items", () => {
const runCommand = async (command: string, args: string[]) => {
const key = `${command} ${args.join(" ")}`;
if (key === WHICH_APTSS_KEY) {
return { code: 0, stdout: "/usr/bin/aptss\n", stderr: "" };
}
if (key === WHICH_APM_KEY) {
return { code: 127, stdout: "", stderr: "apm: command not found" };
}
if (key === APTSS_LIST_UPGRADABLE_KEY) {
return {
code: 0,
@@ -387,6 +419,14 @@ describe("update-center load items", () => {
const result = await loadUpdateCenterItems(async (command, args) => {
const key = `${command} ${args.join(" ")}`;
if (key === WHICH_APTSS_KEY) {
return { code: 0, stdout: "/usr/bin/aptss\n", stderr: "" };
}
if (key === WHICH_APM_KEY) {
return { code: 127, stdout: "", stderr: "apm: command not found" };
}
if (key === APTSS_LIST_UPGRADABLE_KEY) {
return {
code: 0,
@@ -440,9 +480,122 @@ describe("update-center load items", () => {
sha512: "beadfeed",
},
]);
expect(result.warnings).toEqual([
"apm upgradable query failed: apm: command not found",
"apm installed query failed: apm: command not found",
expect(result.warnings).toEqual([]);
});
it("skips aptss commands when the store filter disables Spark", async () => {
const { loadUpdateCenterItems } = await loadUpdateCenterModule({
"https://erotica.spark-app.store/amd64-apm/categories.json": {
tools: { zh: "Tools" },
},
"https://erotica.spark-app.store/amd64-apm/tools/applist.json": [
{ Name: "Spark Clock", Pkgname: "spark-clock" },
],
});
const runCommand = vi.fn(async (command: string, args: string[]) => {
const key = `${command} ${args.join(" ")}`;
if (key === WHICH_APM_KEY) {
return { code: 0, stdout: "/usr/bin/apm\n", stderr: "" };
}
if (key === "apm list --upgradable") {
return {
code: 0,
stdout: "spark-clock/main 2.0.0 amd64 [upgradable from: 1.0.0]",
stderr: "",
};
}
if (key === "apm list --installed") {
return {
code: 0,
stdout: "",
stderr: "",
};
}
if (
key ===
"bash -lc amber-pm-debug /usr/bin/apt -c /opt/durapps/spark-store/bin/apt-fast-conf/aptss-apt.conf download spark-clock --print-uris"
) {
return {
code: 0,
stdout:
"'https://example.invalid/spark-clock_2.0.0_amd64.deb' spark-clock_2.0.0_amd64.deb 1234 SHA512:feedface",
stderr: "",
};
}
throw new Error(`Unexpected command ${key}`);
});
await loadUpdateCenterItems(runCommand, "apm");
expect(runCommand).not.toHaveBeenCalledWith(
"bash",
expect.arrayContaining([
expect.stringContaining("apt list --upgradable"),
]),
);
expect(runCommand).not.toHaveBeenCalledWith(
"dpkg-query",
expect.any(Array),
);
});
it("skips apm commands when the store filter disables APM", async () => {
const { loadUpdateCenterItems } = await loadUpdateCenterModule({
"https://erotica.spark-app.store/amd64-store/categories.json": {
office: { zh: "Office" },
},
"https://erotica.spark-app.store/amd64-store/office/applist.json": [
{ Name: "Spark Notes", Pkgname: "spark-notes" },
],
});
const runCommand = vi.fn(async (command: string, args: string[]) => {
const key = `${command} ${args.join(" ")}`;
if (key === WHICH_APTSS_KEY) {
return { code: 0, stdout: "/usr/bin/aptss\n", stderr: "" };
}
if (key === APTSS_LIST_UPGRADABLE_KEY) {
return {
code: 0,
stdout: "spark-notes/stable 2.0.0 amd64 [upgradable from: 1.0.0]",
stderr: "",
};
}
if (key === DPKG_QUERY_INSTALLED_KEY) {
return {
code: 0,
stdout: "spark-notes\tinstall ok installed\n",
stderr: "",
};
}
if (key === APTSS_NOTES_PRINT_URIS_KEY) {
return {
code: 0,
stdout:
"'https://example.invalid/spark-notes_2.0.0_amd64.deb' spark-notes_2.0.0_amd64.deb 654321 SHA512:beadfeed",
stderr: "",
};
}
throw new Error(`Unexpected command ${key}`);
});
await loadUpdateCenterItems(runCommand, "spark");
expect(runCommand).not.toHaveBeenCalledWith("apm", [
"list",
"--upgradable",
]);
expect(runCommand).not.toHaveBeenCalledWith("apm", ["list", "--installed"]);
});
});
@@ -157,8 +157,8 @@ describe("update-center/ipc", () => {
await cancelHandler?.({}, "aptss:spark-weather");
expect(getStateHandler?.()).toEqual(snapshot);
expect(service.open).toHaveBeenCalledTimes(1);
expect(service.refresh).toHaveBeenCalledTimes(1);
expect(service.open).toHaveBeenCalledWith("both");
expect(service.refresh).toHaveBeenCalledWith("both");
expect(service.ignore).toHaveBeenCalledWith({
packageName: "spark-weather",
newVersion: "2.0.0",
@@ -176,6 +176,51 @@ describe("update-center/ipc", () => {
expect(send).toHaveBeenCalledWith("update-center-state", snapshot);
});
it("forwards store filter payloads to open and refresh", async () => {
const handle = vi.fn();
const snapshot: UpdateCenterServiceState = {
items: [],
tasks: [],
warnings: [],
hasRunningTasks: false,
};
const service = {
open: vi.fn().mockResolvedValue(snapshot),
refresh: vi.fn().mockResolvedValue(snapshot),
ignore: vi.fn().mockResolvedValue(undefined),
unignore: vi.fn().mockResolvedValue(undefined),
start: vi.fn().mockResolvedValue(undefined),
cancel: vi.fn().mockResolvedValue(undefined),
getState: vi.fn().mockReturnValue(snapshot),
subscribe: vi.fn(() => () => undefined),
};
registerUpdateCenterIpc({ handle }, service);
const openHandler = handle.mock.calls.find(
([channel]: [string]) => channel === "update-center-open",
)?.[1] as
| ((
event: unknown,
storeFilter?: "spark" | "apm" | "both",
) => Promise<UpdateCenterServiceState>)
| undefined;
const refreshHandler = handle.mock.calls.find(
([channel]: [string]) => channel === "update-center-refresh",
)?.[1] as
| ((
event: unknown,
storeFilter?: "spark" | "apm" | "both",
) => Promise<UpdateCenterServiceState>)
| undefined;
await openHandler?.({}, "apm");
await refreshHandler?.({}, "spark");
expect(service.open).toHaveBeenCalledWith("apm");
expect(service.refresh).toHaveBeenCalledWith("spark");
});
it("service subscribers receive state updates after refresh start and ignore", async () => {
let ignoredEntries = new Set<string>();
const send = vi.fn();
@@ -61,9 +61,9 @@ describe("updateCenter store", () => {
open.mockResolvedValue(snapshot);
const store = createUpdateCenterStore();
await store.open();
await store.open("apm");
expect(open).toHaveBeenCalledTimes(1);
expect(open).toHaveBeenCalledWith("apm");
expect(store.isOpen.value).toBe(true);
expect(store.snapshot.value).toEqual(snapshot);
expect(store.filteredItems.value).toEqual(snapshot.items);
+14 -2
View File
@@ -89,7 +89,7 @@
<div class="border-t border-slate-200 pt-4 dark:border-slate-800">
<button
v-if="storeFilter !== 'spark'"
v-if="canManageApps"
type="button"
class="flex w-full items-center gap-3 rounded-2xl border border-transparent px-4 py-3 text-left text-sm font-medium text-slate-600 transition hover:border-brand/30 hover:bg-brand/5 hover:text-brand focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand/40 dark:text-slate-300 dark:hover:bg-slate-800"
@click="$emit('list')"
@@ -98,6 +98,7 @@
<span>应用管理</span>
</button>
<button
v-if="canOpenUpdateCenter"
type="button"
class="flex w-full items-center gap-3 rounded-2xl border border-transparent px-4 py-3 text-left text-sm font-medium text-slate-600 transition hover:border-brand/30 hover:bg-brand/5 hover:text-brand focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand/40 dark:text-slate-300 dark:hover:bg-slate-800"
@click="$emit('update')"
@@ -110,15 +111,17 @@
</template>
<script setup lang="ts">
import { computed } from "vue";
import ThemeToggle from "./ThemeToggle.vue";
import amberLogo from "../assets/imgs/spark-store.svg";
defineProps<{
const props = defineProps<{
// eslint-disable-next-line @typescript-eslint/no-explicit-any
categories: Record<string, any>;
activeCategory: string;
categoryCounts: Record<string, number>;
themeMode: "light" | "dark" | "auto";
sparkAvailable: boolean;
apmAvailable: boolean;
storeFilter: "spark" | "apm" | "both";
}>();
@@ -135,6 +138,15 @@ const toggleTheme = () => {
emit("toggle-theme");
};
const canManageApps = computed(() => {
return (
(props.storeFilter !== "apm" && props.sparkAvailable) ||
(props.storeFilter !== "spark" && props.apmAvailable)
);
});
const canOpenUpdateCenter = canManageApps;
const selectCategory = (category: string) => {
emit("select-category", category);
};
+18 -3
View File
@@ -29,10 +29,11 @@
</div>
<div class="flex items-center gap-3">
<div
v-if="storeFilter === 'both'"
v-if="showOriginSwitcher"
class="flex items-center rounded-2xl border border-slate-200/70 p-1 dark:border-slate-800/70"
>
<button
v-if="apmEnabled"
type="button"
class="rounded-xl px-4 py-1.5 text-sm font-semibold transition"
:class="
@@ -46,6 +47,7 @@
APM 软件
</button>
<button
v-if="sparkEnabled"
type="button"
class="rounded-xl px-4 py-1.5 text-sm font-semibold transition"
:class="
@@ -185,7 +187,7 @@
</template>
<script setup lang="ts">
import { reactive } from "vue";
import { computed, reactive } from "vue";
import { App } from "../global/typedefinition";
import { APM_STORE_BASE_URL } from "../global/storeConfig";
@@ -209,13 +211,14 @@ const canOpenDetail = (app: App) => {
);
};
defineProps<{
const props = defineProps<{
show: boolean;
apps: App[];
loading: boolean;
error: string;
activeOrigin: "apm" | "spark";
storeFilter: "spark" | "apm" | "both";
sparkAvailable: boolean;
apmAvailable: boolean;
}>();
@@ -233,4 +236,16 @@ const onOverlayWheel = (e: WheelEvent) => {
if (target.closest(".overflow-y-auto, .overflow-auto")) return;
e.preventDefault();
};
const sparkEnabled = computed(() => {
return props.storeFilter !== "apm" && props.sparkAvailable;
});
const apmEnabled = computed(() => {
return props.storeFilter !== "spark" && props.apmAvailable;
});
const showOriginSwitcher = computed(() => {
return sparkEnabled.value && apmEnabled.value;
});
</script>
+4 -2
View File
@@ -26,6 +26,8 @@ export type DownloadItemStatus =
export type StoreMode = "spark" | "apm" | "hybrid";
export type StoreFilter = "spark" | "apm" | "both";
export interface DownloadItem {
id: number;
name: string;
@@ -178,8 +180,8 @@ export interface UpdateCenterSnapshot {
}
export interface UpdateCenterBridge {
open: () => Promise<UpdateCenterSnapshot>;
refresh: () => Promise<UpdateCenterSnapshot>;
open: (storeFilter?: StoreFilter) => Promise<UpdateCenterSnapshot>;
refresh: (storeFilter?: StoreFilter) => Promise<UpdateCenterSnapshot>;
ignore: (payload: {
packageName: string;
newVersion: string;
+83
View File
@@ -0,0 +1,83 @@
import type { StoreFilter } from "@/global/typedefinition";
export interface SourceAvailability {
spark: boolean;
apm: boolean;
}
export const isOriginEnabled = (
storeFilter: StoreFilter,
origin: "spark" | "apm",
): boolean => {
return storeFilter === "both" || storeFilter === origin;
};
export const getDefaultInstalledOrigin = (
storeFilter: StoreFilter,
availability: SourceAvailability,
): "spark" | "apm" | null => {
if (storeFilter === "spark") {
return availability.spark ? "spark" : null;
}
if (storeFilter === "apm") {
return availability.apm ? "apm" : null;
}
if (availability.apm) {
return "apm";
}
if (availability.spark) {
return "spark";
}
return null;
};
export const getEffectiveStoreFilter = (
storeFilter: StoreFilter,
availability: SourceAvailability,
): StoreFilter | null => {
if (storeFilter === "spark") {
return availability.spark ? "spark" : null;
}
if (storeFilter === "apm") {
return availability.apm ? "apm" : null;
}
if (availability.spark && availability.apm) {
return "both";
}
if (availability.spark) {
return "spark";
}
if (availability.apm) {
return "apm";
}
return null;
};
export const isOriginUsable = (
storeFilter: StoreFilter,
origin: "spark" | "apm",
availability: SourceAvailability,
): boolean => {
return isOriginEnabled(storeFilter, origin) && availability[origin];
};
export const getAllowedInstalledOrigin = (
storeFilter: StoreFilter,
requestedOrigin: "spark" | "apm",
availability: SourceAvailability,
): "spark" | "apm" | null => {
if (isOriginUsable(storeFilter, requestedOrigin, availability)) {
return requestedOrigin;
}
return getDefaultInstalledOrigin(storeFilter, availability);
};
+7 -6
View File
@@ -5,6 +5,7 @@ import type {
UpdateCenterSnapshot,
DownloadItem,
UpdateCenterStartTask,
StoreFilter,
} from "@/global/typedefinition";
import { downloads, getNextUpdateDownloadId } from "@/global/downloadStatus";
import { APM_STORE_BASE_URL } from "@/global/storeConfig";
@@ -28,8 +29,8 @@ export interface UpdateCenterStore {
someSelected: ComputedRef<boolean>;
bind: () => void;
unbind: () => void;
open: () => Promise<void>;
refresh: () => Promise<void>;
open: (storeFilter?: StoreFilter) => Promise<void>;
refresh: (storeFilter?: StoreFilter) => Promise<void>;
ignoreItem: (packageName: string, newVersion: string) => Promise<void>;
unignoreItem: (packageName: string, newVersion: string) => Promise<void>;
toggleSelection: (taskKey: string) => void;
@@ -129,15 +130,15 @@ export const createUpdateCenterStore = (): UpdateCenterStore => {
isBound = false;
};
const open = async (): Promise<void> => {
const open = async (storeFilter: StoreFilter = "both"): Promise<void> => {
resetSessionState();
const nextSnapshot = await window.updateCenter.open();
const nextSnapshot = await window.updateCenter.open(storeFilter);
applySnapshot(nextSnapshot);
isOpen.value = true;
};
const refresh = async (): Promise<void> => {
const nextSnapshot = await window.updateCenter.refresh();
const refresh = async (storeFilter: StoreFilter = "both"): Promise<void> => {
const nextSnapshot = await window.updateCenter.refresh(storeFilter);
applySnapshot(nextSnapshot);
};