Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions electron/main/ipc/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,17 @@ export const registerLibraryIpc = (): void => {
if (dirs.includes(dir)) {
return { success: false, error: ErrorCode.SCAN_DIR_EXISTS };
}
// 嵌套目录在写入前拒绝:撤销流程若已落库,会连带删掉另一目录覆盖下的已入库曲目
const nested = dirs.some(
(d) =>
dir.startsWith(d + "\\") ||
dir.startsWith(d + "/") ||
d.startsWith(dir + "\\") ||
d.startsWith(dir + "/"),
);
if (nested) {
return { success: false, error: ErrorCode.SCAN_DIR_NESTED };
}
dirs.push(dir);
store.set("library.scanDirs", dirs);
libraryLog.info(`添加扫描目录: ${dir}`);
Expand Down
2 changes: 2 additions & 0 deletions shared/types/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ export enum ErrorCode {
SCAN_DIR_NOT_FOUND = "SCAN_DIR_NOT_FOUND",
/** 用户取消了目录选择 */
SCAN_DIR_NOT_SELECTED = "SCAN_DIR_NOT_SELECTED",
/** 目录与已有扫描目录存在嵌套关系 */
SCAN_DIR_NESTED = "SCAN_DIR_NESTED",

// 通用
/** 当前账号没有执行操作所需的权限 */
Expand Down
3 changes: 2 additions & 1 deletion src/components/library/FolderManager.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { useLibraryStore } from "@/stores/library";
import { ErrorCode } from "@shared/types/errors";
import { toast } from "@/composables/useToast";
import IconLucideFolder from "~icons/lucide/folder";
import IconLucideFolderPlus from "~icons/lucide/folder-plus";
Expand Down Expand Up @@ -30,7 +31,7 @@ const handleAdd = async (): Promise<void> => {
const res = await libraryStore.addScanDir();
if (res.success) {
emit("added");
} else if (res.error === "nested") {
} else if (res.error === ErrorCode.SCAN_DIR_NESTED) {
toast.warning(t("library.nestedHint"));
} else if (res.error) {
toast.error(res.error);
Expand Down
48 changes: 48 additions & 0 deletions src/stores/library.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createPinia, setActivePinia } from "pinia";
import { ErrorCode } from "@shared/types/errors";

const mocks = vi.hoisted(() => ({
addScanDir: vi.fn(),
removeScanDir: vi.fn(),
}));

beforeEach(() => {
setActivePinia(createPinia());
vi.clearAllMocks();
Object.assign(window, {
api: {
library: {
addScanDir: mocks.addScanDir,
removeScanDir: mocks.removeScanDir,
},
},
});
});

describe("曲库扫描目录", () => {
it("添加成功时同步到本地列表且不触发移除", async () => {
mocks.addScanDir.mockResolvedValue({ success: true, data: "C:/music" });
const { useLibraryStore } = await import("./library");
const store = useLibraryStore();
const res = await store.addScanDir();
expect(res.success).toBe(true);
expect(store.scanDirs).toContain("C:/music");
expect(mocks.removeScanDir).not.toHaveBeenCalled();
});

it("嵌套目录被主进程拒绝时列表不变且不调用移除", async () => {
mocks.addScanDir.mockResolvedValue({
success: false,
error: ErrorCode.SCAN_DIR_NESTED,
});
const { useLibraryStore } = await import("./library");
const store = useLibraryStore();
store.scanDirs = ["C:/music"];
const res = await store.addScanDir();
expect(res.success).toBe(false);
expect(res.error).toBe(ErrorCode.SCAN_DIR_NESTED);
expect(store.scanDirs).toEqual(["C:/music"]);
expect(mocks.removeScanDir).not.toHaveBeenCalled();
});
});
15 changes: 2 additions & 13 deletions src/stores/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,20 +157,9 @@ export const useLibraryStore = defineStore("library", () => {
/** 添加扫描目录 */
const addScanDir = async (): Promise<{ success: boolean; error?: string }> => {
const res = await window.api.library.addScanDir();
// 嵌套目录由主进程在写入前拒绝,成功才同步到本地列表
if (res.success) {
const newDir = res.data as string;
const nested = scanDirs.value.some(
(d) =>
newDir.startsWith(d + "\\") ||
newDir.startsWith(d + "/") ||
d.startsWith(newDir + "\\") ||
d.startsWith(newDir + "/"),
);
if (nested) {
await window.api.library.removeScanDir(newDir);
return { success: false, error: "nested" };
}
scanDirs.value = [...scanDirs.value, newDir];
scanDirs.value = [...scanDirs.value, res.data as string];
}
return res;
};
Expand Down
Loading