forked from intitni/CopilotForXcode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCustomCommand.swift
More file actions
130 lines (117 loc) · 4.89 KB
/
CustomCommand.swift
File metadata and controls
130 lines (117 loc) · 4.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import ComposableArchitecture
import Foundation
import PlusFeatureFlag
import Preferences
import SwiftUI
import Toast
struct CustomCommandFeature: ReducerProtocol {
struct State: Equatable {
var editCustomCommand: EditCustomCommand.State?
}
let settings: CustomCommandView.Settings
enum Action: Equatable {
case createNewCommand
case editCommand(CustomCommand)
case editCustomCommand(EditCustomCommand.Action)
case deleteCommand(CustomCommand)
case exportCommand(CustomCommand)
case importCommand(at: URL)
case importCommandClicked
}
@Dependency(\.toast) var toast
var body: some ReducerProtocol<State, Action> {
Reduce { state, action in
switch action {
case .createNewCommand:
if !isFeatureAvailable(\.unlimitedCustomCommands),
settings.customCommands.count >= 10
{
toast("Upgrade to Plus to add more commands", .info)
return .none
}
state.editCustomCommand = EditCustomCommand.State(nil)
return .none
case let .editCommand(command):
state.editCustomCommand = EditCustomCommand.State(command)
return .none
case .editCustomCommand(.close):
state.editCustomCommand = nil
return .none
case let .deleteCommand(command):
settings.customCommands.removeAll(
where: { $0.id == command.id }
)
if state.editCustomCommand?.commandId == command.id {
state.editCustomCommand = nil
}
return .none
case .editCustomCommand:
return .none
case let .exportCommand(command):
return .run { _ in
do {
let data = try JSONEncoder().encode(command)
let filename = "CustomCommand-\(command.name).json"
let url = await withCheckedContinuation { continuation in
Task { @MainActor in
let panel = NSSavePanel()
panel.canCreateDirectories = true
panel.nameFieldStringValue = filename
let result = await panel.begin()
switch result {
case .OK:
continuation.resume(returning: panel.url)
default:
continuation.resume(returning: nil)
}
}
}
if let url {
try data.write(to: url)
toast("Saved!", .info)
}
} catch {
toast(error.localizedDescription, .error)
}
}
case let .importCommand(url):
if !isFeatureAvailable(\.unlimitedCustomCommands),
settings.customCommands.count >= 10
{
toast("Upgrade to Plus to add more commands", .info)
return .none
}
do {
let data = try Data(contentsOf: url)
var command = try JSONDecoder().decode(CustomCommand.self, from: data)
command.commandId = UUID().uuidString
settings.customCommands.append(command)
toast("Imported custom command \(command.name)!", .info)
} catch {
toast("Failed to import command: \(error.localizedDescription)", .error)
}
return .none
case .importCommandClicked:
return .run { send in
let url = await withCheckedContinuation { continuation in
Task { @MainActor in
let panel = NSOpenPanel()
panel.allowedContentTypes = [.json]
let result = await panel.begin()
if result == .OK {
continuation.resume(returning: panel.url)
} else {
continuation.resume(returning: nil)
}
}
}
if let url {
await send(.importCommand(at: url))
}
}
}
}.ifLet(\.editCustomCommand, action: /Action.editCustomCommand) {
EditCustomCommand(settings: settings)
}
}
}