fix(import): 修复本地 TXT 文件选择与导入

- 书架和文件管理统一使用 TXT 文件选择器,支持大小写扩展名及多选,排除 JSON 书源
- 保留所选文件访问权限至异步导入完成,等待选择器关闭后展示导入确认
- 补充双入口文件选择回归、导入校验和 TXT 测试样本
This commit is contained in:
Aminark
2026-10-22 10:51:09 +08:00
parent 7daa6b5635
commit bf72ebc894
9 changed files with 252 additions and 37 deletions
+22 -3
View File
@@ -4,6 +4,20 @@ import SwiftUI
import LegadoCore
#endif
final class LocalBookImportFile: @unchecked Sendable {
let url: URL
private let hasSecurityScope: Bool
init(url: URL) {
self.url = url
hasSecurityScope = url.startAccessingSecurityScopedResource()
}
deinit {
if hasSecurityScope { url.stopAccessingSecurityScopedResource() }
}
}
enum AppBookChapterRepository {
static func live() -> any BookChapterRepository {
let base = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
@@ -318,10 +332,15 @@ final class BookLibraryModel: ObservableObject {
}
func importLocalFiles(_ urls: [URL]) async {
await importPickedLocalFiles(urls.map { LocalBookImportFile(url: $0) })
}
func importPickedLocalFiles(_ files: [LocalBookImportFile]) async {
// Keep the picker grants alive through the review and asynchronous copy.
defer { withExtendedLifetime(files) {} }
var failures: [String] = []
for url in urls {
let granted = url.startAccessingSecurityScopedResource()
defer { if granted { url.stopAccessingSecurityScopedResource() } }
for file in files {
let url = file.url
do {
let stored = try await localFiles.importFile(from: url)
do {