mirror of
https://gitee.com/spark-store-project/spark-store
synced 2026-05-30 01:31:06 +08:00
fix(sources): hide unavailable update and management entries
This commit is contained in:
@@ -10,6 +10,24 @@ import axios from "axios";
|
||||
|
||||
const logger = pino({ name: "install-manager" });
|
||||
|
||||
const getStoreFilterFromArgv = (): "spark" | "apm" | "both" => {
|
||||
const argv = process.argv;
|
||||
const noApm = argv.includes("--no-apm");
|
||||
const noSpark = argv.includes("--no-spark");
|
||||
|
||||
if (noApm && noSpark) return "both";
|
||||
if (noApm) return "spark";
|
||||
if (noSpark) return "apm";
|
||||
return "both";
|
||||
};
|
||||
|
||||
const isOriginEnabled = (
|
||||
storeFilter: "spark" | "apm" | "both",
|
||||
origin: "spark" | "apm",
|
||||
): boolean => {
|
||||
return storeFilter === "both" || storeFilter === origin;
|
||||
};
|
||||
|
||||
type InstallTask = {
|
||||
id: number;
|
||||
pkgname: string;
|
||||
@@ -88,6 +106,14 @@ const checkApmAvailable = async (): Promise<boolean> => {
|
||||
return found;
|
||||
};
|
||||
|
||||
/** 检测本机是否具备 Spark/aptss 管理能力 */
|
||||
const checkSparkAvailable = async (): Promise<boolean> => {
|
||||
const { code, stdout } = await runCommandCapture("which", ["aptss"]);
|
||||
const found = code === 0 && stdout.trim().length > 0;
|
||||
if (!found) logger.info("未检测到 aptss 命令");
|
||||
return found;
|
||||
};
|
||||
|
||||
/** 提权执行 shell-caller aptss install apm 安装 APM,安装后需用户重启电脑 */
|
||||
const runInstallApm = async (superUserCmd: string): Promise<boolean> => {
|
||||
const execCommand = superUserCmd || SHELL_CALLER_PATH;
|
||||
@@ -793,8 +819,33 @@ ipcMain.handle(
|
||||
payload: { origin: "apm" | "spark"; pkgnameList?: string[] },
|
||||
) => {
|
||||
const { origin, pkgnameList } = payload;
|
||||
const storeFilter = getStoreFilterFromArgv();
|
||||
const apmBasePath = "/var/lib/apm/apm/files/ace-env/var/lib/apm";
|
||||
|
||||
if (!isOriginEnabled(storeFilter, origin)) {
|
||||
return {
|
||||
success: false,
|
||||
message: `${origin} origin disabled by startup filter`,
|
||||
apps: [],
|
||||
};
|
||||
}
|
||||
|
||||
if (origin === "spark" && !(await checkSparkAvailable())) {
|
||||
return {
|
||||
success: false,
|
||||
message: "spark origin unavailable on this system",
|
||||
apps: [],
|
||||
};
|
||||
}
|
||||
|
||||
if (origin === "apm" && !(await checkApmAvailable())) {
|
||||
return {
|
||||
success: false,
|
||||
message: "apm origin unavailable on this system",
|
||||
apps: [],
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const installedApps: Array<{
|
||||
pkgname: string;
|
||||
@@ -1033,6 +1084,10 @@ ipcMain.handle("check-apm-available", async () => {
|
||||
return await checkApmAvailable();
|
||||
});
|
||||
|
||||
ipcMain.handle("check-spark-available", async () => {
|
||||
return await checkSparkAvailable();
|
||||
});
|
||||
|
||||
// 显示 APM 安装对话框(在点击安装按钮时提前检查)
|
||||
ipcMain.handle("show-apm-install-dialog", async (event) => {
|
||||
const webContents = event.sender;
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
import { resolveUpdateItemIcons } from "./icons";
|
||||
import {
|
||||
createUpdateCenterService,
|
||||
type StoreFilter,
|
||||
type UpdateCenterIgnorePayload,
|
||||
type UpdateCenterService,
|
||||
type UpdateCenterStartTask,
|
||||
@@ -349,35 +350,70 @@ const enrichItemIcons = (items: UpdateCenterItem[]): UpdateCenterItem[] => {
|
||||
});
|
||||
};
|
||||
|
||||
const isSourceEnabled = (
|
||||
storeFilter: StoreFilter,
|
||||
source: "spark" | "apm",
|
||||
): boolean => {
|
||||
return storeFilter === "both" || storeFilter === source;
|
||||
};
|
||||
|
||||
const isCommandAvailable = async (
|
||||
runCommand: UpdateCenterCommandRunner,
|
||||
command: "aptss" | "apm",
|
||||
): Promise<boolean> => {
|
||||
const result = await runCommand("which", [command]);
|
||||
return result.code === 0 && result.stdout.trim().length > 0;
|
||||
};
|
||||
|
||||
export const loadUpdateCenterItems = async (
|
||||
runCommand: UpdateCenterCommandRunner = runCommandCapture,
|
||||
storeFilter: StoreFilter = "both",
|
||||
): Promise<UpdateCenterLoadItemsResult> => {
|
||||
const [sparkEnabled, apmEnabled] = await Promise.all([
|
||||
isSourceEnabled(storeFilter, "spark")
|
||||
? isCommandAvailable(runCommand, "aptss")
|
||||
: Promise.resolve(false),
|
||||
isSourceEnabled(storeFilter, "apm")
|
||||
? isCommandAvailable(runCommand, "apm")
|
||||
: Promise.resolve(false),
|
||||
]);
|
||||
|
||||
const [aptssResult, apmResult, aptssInstalledResult, apmInstalledResult] =
|
||||
await Promise.all([
|
||||
runCommand(
|
||||
sparkEnabled
|
||||
? runCommand(
|
||||
APTSS_LIST_UPGRADABLE_COMMAND.command,
|
||||
APTSS_LIST_UPGRADABLE_COMMAND.args,
|
||||
),
|
||||
runCommand("apm", ["list", "--upgradable"]),
|
||||
runCommand(
|
||||
)
|
||||
: Promise.resolve({ code: 0, stdout: "", stderr: "" }),
|
||||
apmEnabled
|
||||
? runCommand("apm", ["list", "--upgradable"])
|
||||
: Promise.resolve({ code: 0, stdout: "", stderr: "" }),
|
||||
sparkEnabled
|
||||
? runCommand(
|
||||
DPKG_QUERY_INSTALLED_COMMAND.command,
|
||||
DPKG_QUERY_INSTALLED_COMMAND.args,
|
||||
),
|
||||
runCommand("apm", ["list", "--installed"]),
|
||||
)
|
||||
: Promise.resolve({ code: 0, stdout: "", stderr: "" }),
|
||||
apmEnabled
|
||||
? runCommand("apm", ["list", "--installed"])
|
||||
: Promise.resolve({ code: 0, stdout: "", stderr: "" }),
|
||||
]);
|
||||
|
||||
const aptssAvailable =
|
||||
aptssResult.code === 0 || aptssInstalledResult.code === 0;
|
||||
sparkEnabled && (aptssResult.code === 0 || aptssInstalledResult.code === 0);
|
||||
|
||||
const warnings = [
|
||||
aptssAvailable
|
||||
? getCommandError("aptss upgradable query", aptssResult)
|
||||
: null,
|
||||
getCommandError("apm upgradable query", apmResult),
|
||||
apmEnabled ? getCommandError("apm upgradable query", apmResult) : null,
|
||||
aptssAvailable
|
||||
? getCommandError("dpkg installed query", aptssInstalledResult)
|
||||
: null,
|
||||
getCommandError("apm installed query", apmInstalledResult),
|
||||
apmEnabled
|
||||
? getCommandError("apm installed query", apmInstalledResult)
|
||||
: null,
|
||||
].filter((message): message is string => message !== null);
|
||||
|
||||
const aptssItems =
|
||||
@@ -385,7 +421,9 @@ export const loadUpdateCenterItems = async (
|
||||
? parseAptssUpgradableOutput(aptssResult.stdout)
|
||||
: [];
|
||||
const apmItems =
|
||||
apmResult.code === 0 ? parseApmUpgradableOutput(apmResult.stdout) : [];
|
||||
apmEnabled && apmResult.code === 0
|
||||
? parseApmUpgradableOutput(apmResult.stdout)
|
||||
: [];
|
||||
|
||||
const installedSources = buildInstalledSourceMap(
|
||||
aptssAvailable && aptssInstalledResult.code === 0
|
||||
@@ -396,13 +434,15 @@ export const loadUpdateCenterItems = async (
|
||||
|
||||
const [categorizedAptssItems, categorizedApmItems] = await Promise.all([
|
||||
aptssAvailable ? enrichItemCategories(aptssItems) : Promise.resolve([]),
|
||||
enrichItemCategories(apmItems),
|
||||
apmEnabled ? enrichItemCategories(apmItems) : Promise.resolve([]),
|
||||
]);
|
||||
const [enrichedAptssItems, enrichedApmItems] = await Promise.all([
|
||||
aptssAvailable
|
||||
? enrichAptssItems(categorizedAptssItems, runCommand)
|
||||
: Promise.resolve({ items: [], warnings: [] }),
|
||||
enrichApmItems(categorizedApmItems, runCommand),
|
||||
apmEnabled
|
||||
? enrichApmItems(categorizedApmItems, runCommand)
|
||||
: Promise.resolve({ items: [], warnings: [] }),
|
||||
]);
|
||||
|
||||
return {
|
||||
@@ -433,8 +473,14 @@ export const registerUpdateCenterIpc = (
|
||||
| "subscribe"
|
||||
>,
|
||||
): void => {
|
||||
ipc.handle("update-center-open", () => service.open());
|
||||
ipc.handle("update-center-refresh", () => service.refresh());
|
||||
ipc.handle(
|
||||
"update-center-open",
|
||||
(_event, storeFilter: StoreFilter = "both") => service.open(storeFilter),
|
||||
);
|
||||
ipc.handle(
|
||||
"update-center-refresh",
|
||||
(_event, storeFilter: StoreFilter = "both") => service.refresh(storeFilter),
|
||||
);
|
||||
ipc.handle(
|
||||
"update-center-ignore",
|
||||
(_event, payload: UpdateCenterIgnorePayload) => service.ignore(payload),
|
||||
|
||||
@@ -13,6 +13,8 @@ import {
|
||||
} from "./queue";
|
||||
import type { UpdateCenterItem, UpdateSource } from "./types";
|
||||
|
||||
export type StoreFilter = "spark" | "apm" | "both";
|
||||
|
||||
export interface UpdateCenterLoadedItems {
|
||||
items: UpdateCenterItem[];
|
||||
warnings: string[];
|
||||
@@ -68,8 +70,8 @@ export interface UpdateCenterStartTask {
|
||||
}
|
||||
|
||||
export interface UpdateCenterService {
|
||||
open: () => Promise<UpdateCenterServiceState>;
|
||||
refresh: () => Promise<UpdateCenterServiceState>;
|
||||
open: (storeFilter?: StoreFilter) => Promise<UpdateCenterServiceState>;
|
||||
refresh: (storeFilter?: StoreFilter) => Promise<UpdateCenterServiceState>;
|
||||
ignore: (payload: UpdateCenterIgnorePayload) => Promise<void>;
|
||||
unignore: (payload: UpdateCenterIgnorePayload) => Promise<void>;
|
||||
start: (tasks: UpdateCenterStartTask[]) => Promise<void>;
|
||||
@@ -81,7 +83,9 @@ export interface UpdateCenterService {
|
||||
}
|
||||
|
||||
export interface CreateUpdateCenterServiceOptions {
|
||||
loadItems: () => Promise<UpdateCenterItem[] | UpdateCenterLoadedItems>;
|
||||
loadItems: (
|
||||
storeFilter: StoreFilter,
|
||||
) => Promise<UpdateCenterItem[] | UpdateCenterLoadedItems>;
|
||||
loadIgnoredEntries?: () => Promise<Set<string>>;
|
||||
saveIgnoredEntries?: (entries: ReadonlySet<string>) => Promise<void>;
|
||||
}
|
||||
@@ -135,6 +139,7 @@ export const createUpdateCenterService = (
|
||||
): UpdateCenterService => {
|
||||
const queue = createUpdateCenterQueue();
|
||||
const listeners = new Set<(snapshot: UpdateCenterServiceState) => void>();
|
||||
let currentStoreFilter: StoreFilter = "both";
|
||||
const loadIgnored =
|
||||
options.loadIgnoredEntries ??
|
||||
(() => loadIgnoredEntries(IGNORE_CONFIG_PATH));
|
||||
@@ -157,13 +162,18 @@ export const createUpdateCenterService = (
|
||||
return snapshot;
|
||||
};
|
||||
|
||||
const refresh = async (): Promise<UpdateCenterServiceState> => {
|
||||
const refresh = async (
|
||||
storeFilter: StoreFilter = currentStoreFilter,
|
||||
): Promise<UpdateCenterServiceState> => {
|
||||
currentStoreFilter = storeFilter;
|
||||
queue.startRefresh();
|
||||
emit();
|
||||
|
||||
try {
|
||||
const ignoredEntries = await loadIgnored();
|
||||
const loadedItems = normalizeLoadedItems(await options.loadItems());
|
||||
const loadedItems = normalizeLoadedItems(
|
||||
await options.loadItems(currentStoreFilter),
|
||||
);
|
||||
const items = sortIgnoredItems(
|
||||
applyIgnoredEntries(loadedItems.items, ignoredEntries),
|
||||
);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { ipcRenderer, contextBridge, type IpcRendererEvent } from "electron";
|
||||
|
||||
type StoreFilter = "spark" | "apm" | "both";
|
||||
|
||||
type UpdateCenterSnapshot = {
|
||||
items: Array<{
|
||||
taskKey: string;
|
||||
@@ -90,10 +92,10 @@ contextBridge.exposeInMainWorld("apm_store", {
|
||||
});
|
||||
|
||||
contextBridge.exposeInMainWorld("updateCenter", {
|
||||
open: (): Promise<UpdateCenterSnapshot> =>
|
||||
ipcRenderer.invoke("update-center-open"),
|
||||
refresh: (): Promise<UpdateCenterSnapshot> =>
|
||||
ipcRenderer.invoke("update-center-refresh"),
|
||||
open: (storeFilter: StoreFilter = "both"): Promise<UpdateCenterSnapshot> =>
|
||||
ipcRenderer.invoke("update-center-open", storeFilter),
|
||||
refresh: (storeFilter: StoreFilter = "both"): Promise<UpdateCenterSnapshot> =>
|
||||
ipcRenderer.invoke("update-center-refresh", storeFilter),
|
||||
ignore: (payload: {
|
||||
packageName: string;
|
||||
newVersion: string;
|
||||
|
||||
+69
-7
@@ -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();
|
||||
|
||||
// 分类目录加载后,并行加载主页数据和所有应用列表
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user