Skip to content

Commit 8dbf314

Browse files
committed
fix: resolve file type extension and editing issues
- Fix FileType.from() to only use user-specified extension instead of all UTI-associated extensions (prevents .srt from adding .mp4) - Add edit functionality for protection rules - Add 'save' and 'edit_protection_rule' localization strings
1 parent 4c29c88 commit 8dbf314

6 files changed

Lines changed: 155 additions & 17 deletions

File tree

FileTypeGuard/Features/ProtectedTypes/FileTypePickerView.swift

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ struct FileTypePickerView: View {
2020

2121
@Binding var isPresented: Bool
2222

23+
// MARK: - Properties
24+
25+
/// 可选:要编辑的现有规则。如果为 nil,则是添加新规则
26+
var editingRule: ProtectionRule?
27+
2328
// MARK: - State
2429

2530
@StateObject private var viewModel = AddTypeViewModel()
@@ -41,6 +46,13 @@ struct FileTypePickerView: View {
4146
private let typesByCategory = CommonFileTypes.typesByCategory()
4247
private let schemesByCategory = CommonURLSchemes.schemesByCategory()
4348

49+
// MARK: - Initialization
50+
51+
init(isPresented: Binding<Bool>, editingRule: ProtectionRule? = nil) {
52+
self._isPresented = isPresented
53+
self.editingRule = editingRule
54+
}
55+
4456
// MARK: - Body
4557

4658
var body: some View {
@@ -71,13 +83,56 @@ struct FileTypePickerView: View {
7183
} message: {
7284
Text(errorMessage)
7385
}
86+
.onAppear {
87+
loadEditingRule()
88+
}
89+
}
90+
91+
/// 加载要编辑的规则的现有值
92+
private func loadEditingRule() {
93+
guard let rule = editingRule else { return }
94+
95+
// 预选择应用
96+
selectedApplication = rule.expectedApplication
97+
98+
switch rule.target {
99+
case .fileType(let fileType):
100+
pickerMode = .fileTypes
101+
102+
// 查找是否匹配预设类型
103+
if let preset = CommonFileTypes.allTypes.first(where: { $0.uti == fileType.uti }) {
104+
selectedCategory = preset.category
105+
selectedPresetType = preset
106+
} else if let firstExt = fileType.extensions.first {
107+
// 自定义类型
108+
showCustomInput = true
109+
customExtension = firstExt
110+
}
111+
112+
case .urlScheme(let scheme):
113+
pickerMode = .urlSchemes
114+
115+
// 查找是否匹配预设 scheme
116+
if let preset = CommonURLSchemes.allSchemes.first(where: { $0.scheme == scheme.scheme }) {
117+
selectedSchemeCategory = preset.category
118+
selectedPresetScheme = preset
119+
} else {
120+
// 自定义 scheme
121+
showCustomSchemeInput = true
122+
customScheme = scheme.scheme
123+
}
124+
}
74125
}
75126

76127
// MARK: - Header
77128

129+
private var isEditing: Bool {
130+
editingRule != nil
131+
}
132+
78133
private var header: some View {
79134
HStack {
80-
Text("add_file_type_protection")
135+
Text(isEditing ? "edit_protection_rule" : "add_file_type_protection")
81136
.font(.title2)
82137
.fontWeight(.semibold)
83138

@@ -520,7 +575,7 @@ struct FileTypePickerView: View {
520575
}
521576
.keyboardShortcut(.cancelAction)
522577

523-
Button(String(localized: "add_protection")) {
578+
Button(String(localized: isEditing ? "save" : "add_protection")) {
524579
addProtectionRule()
525580
}
526581
.keyboardShortcut(.defaultAction)
@@ -564,12 +619,25 @@ struct FileTypePickerView: View {
564619
}
565620

566621
do {
567-
let rule = ProtectionRule(
568-
target: target,
569-
expectedApplication: app
570-
)
571-
572-
try ConfigurationManager.shared.addProtectionRule(rule)
622+
let rule: ProtectionRule
623+
if let existingRule = editingRule {
624+
// 编辑模式:保留原有 ID
625+
rule = ProtectionRule(
626+
id: existingRule.id,
627+
target: target,
628+
expectedApplication: app,
629+
isEnabled: existingRule.isEnabled,
630+
createdAt: existingRule.createdAt
631+
)
632+
try ConfigurationManager.shared.updateProtectionRule(rule)
633+
} else {
634+
// 添加新模式
635+
rule = ProtectionRule(
636+
target: target,
637+
expectedApplication: app
638+
)
639+
try ConfigurationManager.shared.addProtectionRule(rule)
640+
}
573641

574642
// 立即设置默认应用
575643
switch target {

FileTypeGuard/Features/ProtectedTypes/ProtectedTypesView.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ struct ProtectedTypesView: View {
77

88
@StateObject private var viewModel = ProtectedTypesViewModel()
99
@State private var showingAddSheet = false
10-
@State private var selectedRule: ProtectionRule?
10+
@State private var showingEditSheet = false
11+
@State private var ruleToEdit: ProtectionRule?
1112

1213
// MARK: - Body
1314

@@ -29,6 +30,11 @@ struct ProtectedTypesView: View {
2930
.sheet(isPresented: $showingAddSheet) {
3031
FileTypePickerView(isPresented: $showingAddSheet)
3132
}
33+
.sheet(isPresented: $showingEditSheet) {
34+
if let rule = ruleToEdit {
35+
FileTypePickerView(isPresented: $showingEditSheet, editingRule: rule)
36+
}
37+
}
3238
.onAppear {
3339
viewModel.loadRules()
3440
}
@@ -66,13 +72,13 @@ struct ProtectedTypesView: View {
6672
// MARK: - Rules List
6773

6874
private var rulesList: some View {
69-
List(selection: $selectedRule) {
75+
List {
7076
ForEach(viewModel.protectionRules) { rule in
7177
RuleRow(rule: rule)
72-
.tag(rule)
7378
.contextMenu {
7479
Button(String(localized: "edit")) {
75-
selectedRule = rule
80+
ruleToEdit = rule
81+
showingEditSheet = true
7682
}
7783

7884
Button(rule.isEnabled ? String(localized: "disable") : String(localized: "enable")) {

FileTypeGuard/Models/FileType.swift

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,26 @@ struct FileType: Identifiable, Codable, Hashable {
4242
// MARK: - Factory Methods
4343

4444
/// 从扩展名创建文件类型
45+
/// - Note: 只使用用户指定的扩展名,而不是该 UTI 关联的所有扩展名
4546
static func from(extension ext: String) -> FileType? {
46-
guard let uti = UTIManager.shared.getUTI(forExtension: ext) else {
47+
var cleanExt = ext
48+
// 确保扩展名以点开头
49+
if !cleanExt.hasPrefix(".") {
50+
cleanExt = ".\(cleanExt)"
51+
}
52+
53+
guard let uti = UTIManager.shared.getUTI(forExtension: cleanExt) else {
4754
return nil
4855
}
4956

50-
let extensions = UTIManager.shared.getExtensions(forUTI: uti)
5157
let description = UTIManager.shared.getDescription(forUTI: uti)
5258

59+
// 只使用用户指定的扩展名,而不是该 UTI 关联的所有扩展名
60+
// 这样可以避免添加 .srt 时同时添加 .mp4 的问题
5361
return FileType(
5462
uti: uti,
55-
extensions: extensions.isEmpty ? [ext] : extensions,
56-
displayName: description ?? ext
63+
extensions: [cleanExt],
64+
displayName: description ?? cleanExt
5765
)
5866
}
5967

FileTypeGuard/Resources/Localizable.xcstrings

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4074,6 +4074,62 @@
40744074
}
40754075
}
40764076
},
4077+
"save" : {
4078+
"localizations" : {
4079+
"en" : {
4080+
"stringUnit" : {
4081+
"state" : "translated",
4082+
"value" : "Save"
4083+
}
4084+
},
4085+
"ja" : {
4086+
"stringUnit" : {
4087+
"state" : "translated",
4088+
"value" : "保存"
4089+
}
4090+
},
4091+
"zh-Hans" : {
4092+
"stringUnit" : {
4093+
"state" : "translated",
4094+
"value" : "保存"
4095+
}
4096+
},
4097+
"zh-Hant" : {
4098+
"stringUnit" : {
4099+
"state" : "translated",
4100+
"value" : "儲存"
4101+
}
4102+
}
4103+
}
4104+
},
4105+
"edit_protection_rule" : {
4106+
"localizations" : {
4107+
"en" : {
4108+
"stringUnit" : {
4109+
"state" : "translated",
4110+
"value" : "Edit Protection Rule"
4111+
}
4112+
},
4113+
"ja" : {
4114+
"stringUnit" : {
4115+
"state" : "translated",
4116+
"value" : "保護ルールを編集"
4117+
}
4118+
},
4119+
"zh-Hans" : {
4120+
"stringUnit" : {
4121+
"state" : "translated",
4122+
"value" : "編輯保護規則"
4123+
}
4124+
},
4125+
"zh-Hant" : {
4126+
"stringUnit" : {
4127+
"state" : "translated",
4128+
"value" : "編輯保護規則"
4129+
}
4130+
}
4131+
}
4132+
},
40774133
"select_default_app" : {
40784134
"localizations" : {
40794135
"en" : {

build/FileTypeGuard.xcarchive/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
<key>ArchiveVersion</key>
2626
<integer>2</integer>
2727
<key>CreationDate</key>
28-
<date>2026-02-24T07:42:50Z</date>
28+
<date>2026-02-26T00:22:37Z</date>
2929
<key>Name</key>
3030
<string>FileTypeGuard</string>
3131
<key>SchemeName</key>

0 commit comments

Comments
 (0)