Refactor to use consolidated CommandClient
This commit is contained in:
@@ -3,7 +3,7 @@ import SwiftUI
|
|||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
public struct ConnectionListView: View {
|
public struct ConnectionListView: View {
|
||||||
@Environment(\.scenePhase) private var scenePhase
|
@EnvironmentObject private var commandClient: CommandClient
|
||||||
@StateObject private var viewModel = ConnectionListViewModel()
|
@StateObject private var viewModel = ConnectionListViewModel()
|
||||||
|
|
||||||
public init() {}
|
public init() {}
|
||||||
@@ -56,18 +56,12 @@ public struct ConnectionListView: View {
|
|||||||
#endif
|
#endif
|
||||||
.alertBinding($viewModel.alert)
|
.alertBinding($viewModel.alert)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
|
viewModel.setCommandClient(commandClient)
|
||||||
viewModel.connect()
|
viewModel.connect()
|
||||||
}
|
}
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
viewModel.disconnect()
|
viewModel.disconnect()
|
||||||
}
|
}
|
||||||
.onChangeCompat(of: scenePhase) { newValue in
|
|
||||||
if newValue == .active {
|
|
||||||
viewModel.connect()
|
|
||||||
} else {
|
|
||||||
viewModel.disconnect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
.background(Color(uiColor: .systemGroupedBackground))
|
.background(Color(uiColor: .systemGroupedBackground))
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ public class ConnectionListViewModel: ObservableObject {
|
|||||||
@Published public var alert: Alert?
|
@Published public var alert: Alert?
|
||||||
@Published public var connectionStateFilter: ConnectionStateFilter {
|
@Published public var connectionStateFilter: ConnectionStateFilter {
|
||||||
didSet {
|
didSet {
|
||||||
commandClient.connectionStateFilter = connectionStateFilter
|
commandClient?.connectionStateFilter = connectionStateFilter
|
||||||
commandClient.filterConnectionsNow()
|
commandClient?.filterConnectionsNow()
|
||||||
saveStateFilterTask?.cancel()
|
saveStateFilterTask?.cancel()
|
||||||
saveStateFilterTask = Task {
|
saveStateFilterTask = Task {
|
||||||
await SharedPreferences.connectionStateFilter.set(connectionStateFilter.rawValue)
|
await SharedPreferences.connectionStateFilter.set(connectionStateFilter.rawValue)
|
||||||
@@ -22,8 +22,8 @@ public class ConnectionListViewModel: ObservableObject {
|
|||||||
|
|
||||||
@Published public var connectionSort: ConnectionSort {
|
@Published public var connectionSort: ConnectionSort {
|
||||||
didSet {
|
didSet {
|
||||||
commandClient.connectionSort = connectionSort
|
commandClient?.connectionSort = connectionSort
|
||||||
commandClient.filterConnectionsNow()
|
commandClient?.filterConnectionsNow()
|
||||||
saveSortTask?.cancel()
|
saveSortTask?.cancel()
|
||||||
saveSortTask = Task {
|
saveSortTask = Task {
|
||||||
await SharedPreferences.connectionSort.set(connectionSort.rawValue)
|
await SharedPreferences.connectionSort.set(connectionSort.rawValue)
|
||||||
@@ -31,7 +31,7 @@ public class ConnectionListViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private let commandClient = CommandClient(.connections)
|
private var commandClient: CommandClient?
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
private var connectTask: Task<Void, Never>?
|
private var connectTask: Task<Void, Never>?
|
||||||
private var saveStateFilterTask: Task<Void, Never>?
|
private var saveStateFilterTask: Task<Void, Never>?
|
||||||
@@ -40,8 +40,11 @@ public class ConnectionListViewModel: ObservableObject {
|
|||||||
public init() {
|
public init() {
|
||||||
connectionStateFilter = .active
|
connectionStateFilter = .active
|
||||||
connectionSort = .byDate
|
connectionSort = .byDate
|
||||||
|
}
|
||||||
|
|
||||||
commandClient.$connections
|
public func setCommandClient(_ client: CommandClient) {
|
||||||
|
commandClient = client
|
||||||
|
client.$connections
|
||||||
.compactMap { $0 }
|
.compactMap { $0 }
|
||||||
.sink { [weak self] goConnections in
|
.sink { [weak self] goConnections in
|
||||||
self?.setConnections(goConnections)
|
self?.setConnections(goConnections)
|
||||||
@@ -60,7 +63,6 @@ public class ConnectionListViewModel: ObservableObject {
|
|||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
await self.loadPreferences()
|
await self.loadPreferences()
|
||||||
if Task.isCancelled { return }
|
if Task.isCancelled { return }
|
||||||
self.commandClient.connect()
|
|
||||||
self.connectTask = nil
|
self.connectTask = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,7 +81,6 @@ public class ConnectionListViewModel: ObservableObject {
|
|||||||
saveStateFilterTask = nil
|
saveStateFilterTask = nil
|
||||||
saveSortTask?.cancel()
|
saveSortTask?.cancel()
|
||||||
saveSortTask = nil
|
saveSortTask = nil
|
||||||
commandClient.disconnect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func closeAllConnections() {
|
public func closeAllConnections() {
|
||||||
|
|||||||
@@ -74,6 +74,31 @@ public struct ActiveDashboardView: View {
|
|||||||
OverviewView($viewModel.profileList, $viewModel.selectedProfileID, $viewModel.systemProxyAvailable, $viewModel.systemProxyEnabled)
|
OverviewView($viewModel.profileList, $viewModel.selectedProfileID, $viewModel.systemProxyAvailable, $viewModel.systemProxyEnabled)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
.environmentObject(viewModel.dashboardClient)
|
||||||
|
.onAppear {
|
||||||
|
if ApplicationLibrary.inPreview || profile.status.isConnected {
|
||||||
|
viewModel.dashboardClient.connect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onDisappear {
|
||||||
|
viewModel.dashboardClient.disconnect()
|
||||||
|
}
|
||||||
|
.onChangeCompat(of: scenePhase) { newPhase in
|
||||||
|
if newPhase == .active {
|
||||||
|
if profile.status.isConnected {
|
||||||
|
viewModel.dashboardClient.connect()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
viewModel.dashboardClient.disconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onChangeCompat(of: profile.status) { newStatus in
|
||||||
|
if newStatus.isConnected {
|
||||||
|
viewModel.dashboardClient.connect()
|
||||||
|
} else {
|
||||||
|
viewModel.dashboardClient.disconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
.onReceive(environments.profileUpdate) { _ in
|
.onReceive(environments.profileUpdate) { _ in
|
||||||
Task {
|
Task {
|
||||||
await viewModel.reload()
|
await viewModel.reload()
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ final class ActiveDashboardViewModel: ObservableObject {
|
|||||||
@Published var selection = DashboardPage.overview
|
@Published var selection = DashboardPage.overview
|
||||||
@Published var systemProxyAvailable = false
|
@Published var systemProxyAvailable = false
|
||||||
@Published var systemProxyEnabled = false
|
@Published var systemProxyEnabled = false
|
||||||
|
@Published var dashboardClient = CommandClient([.status, .groups, .clashMode, .connections])
|
||||||
|
|
||||||
var onEmptyProfilesChange: ((Bool) -> Void)?
|
var onEmptyProfilesChange: ((Bool) -> Void)?
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import SwiftUI
|
|||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
public struct ClashModeView: View {
|
public struct ClashModeView: View {
|
||||||
@Environment(\.scenePhase) private var scenePhase
|
@EnvironmentObject private var commandClient: CommandClient
|
||||||
@StateObject private var viewModel = ClashModeViewModel()
|
@StateObject private var viewModel = ClashModeViewModel()
|
||||||
|
|
||||||
public init() {}
|
public init() {}
|
||||||
@@ -29,13 +29,7 @@ public struct ClashModeView: View {
|
|||||||
}
|
}
|
||||||
.padding([.leading, .trailing])
|
.padding([.leading, .trailing])
|
||||||
.onAppear {
|
.onAppear {
|
||||||
viewModel.connect()
|
viewModel.setCommandClient(commandClient)
|
||||||
}
|
|
||||||
.onDisappear {
|
|
||||||
viewModel.disconnect()
|
|
||||||
}
|
|
||||||
.onChangeCompat(of: scenePhase) { newValue in
|
|
||||||
viewModel.handleScenePhase(newValue)
|
|
||||||
}
|
}
|
||||||
.alertBinding($viewModel.alert)
|
.alertBinding($viewModel.alert)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,37 +7,22 @@ final class ClashModeViewModel: ObservableObject {
|
|||||||
@Published var clashMode = ""
|
@Published var clashMode = ""
|
||||||
@Published var alert: Alert?
|
@Published var alert: Alert?
|
||||||
|
|
||||||
private let commandClient = CommandClient(.clashMode)
|
var commandClient: CommandClient?
|
||||||
|
|
||||||
var clashModeList: [String] {
|
var clashModeList: [String] {
|
||||||
commandClient.clashModeList
|
commandClient?.clashModeList ?? []
|
||||||
}
|
}
|
||||||
|
|
||||||
var shouldShowPicker: Bool {
|
var shouldShowPicker: Bool {
|
||||||
commandClient.clashModeList.count > 1
|
(commandClient?.clashModeList.count ?? 0) > 1
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
func setCommandClient(_ client: CommandClient) {
|
||||||
commandClient.$clashMode
|
commandClient = client
|
||||||
|
client.$clashMode
|
||||||
.assign(to: &$clashMode)
|
.assign(to: &$clashMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func connect() {
|
|
||||||
commandClient.connect()
|
|
||||||
}
|
|
||||||
|
|
||||||
func disconnect() {
|
|
||||||
commandClient.disconnect()
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleScenePhase(_ phase: ScenePhase) {
|
|
||||||
if phase == .active {
|
|
||||||
connect()
|
|
||||||
} else {
|
|
||||||
disconnect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nonisolated func setClashMode(_ newMode: String) async {
|
nonisolated func setClashMode(_ newMode: String) async {
|
||||||
do {
|
do {
|
||||||
try LibboxNewStandaloneCommandClient()!.setClashMode(newMode)
|
try LibboxNewStandaloneCommandClient()!.setClashMode(newMode)
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import SwiftUI
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
public struct ExtensionStatusView: View {
|
public struct ExtensionStatusView: View {
|
||||||
@Environment(\.scenePhase) private var scenePhase
|
@EnvironmentObject private var commandClient: CommandClient
|
||||||
@StateObject private var commandClient = CommandClient(.status)
|
|
||||||
|
|
||||||
@State private var columnCount: Int = 4
|
@State private var columnCount: Int = 4
|
||||||
@State private var alert: Alert?
|
@State private var alert: Alert?
|
||||||
@@ -92,19 +91,6 @@ public struct ExtensionStatusView: View {
|
|||||||
.frame(alignment: .topLeading)
|
.frame(alignment: .topLeading)
|
||||||
.padding([.top, .leading, .trailing])
|
.padding([.top, .leading, .trailing])
|
||||||
}
|
}
|
||||||
.onAppear {
|
|
||||||
commandClient.connect()
|
|
||||||
}
|
|
||||||
.onDisappear {
|
|
||||||
commandClient.disconnect()
|
|
||||||
}
|
|
||||||
.onChangeCompat(of: scenePhase) { newValue in
|
|
||||||
if newValue == .active {
|
|
||||||
commandClient.connect()
|
|
||||||
} else {
|
|
||||||
commandClient.disconnect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.alertBinding($alert)
|
.alertBinding($alert)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import Library
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
public struct GroupListView: View {
|
public struct GroupListView: View {
|
||||||
@Environment(\.scenePhase) private var scenePhase
|
@EnvironmentObject private var commandClient: CommandClient
|
||||||
@StateObject private var viewModel = GroupListViewModel()
|
@StateObject private var viewModel = GroupListViewModel()
|
||||||
|
|
||||||
public init() {}
|
public init() {}
|
||||||
@@ -23,17 +23,8 @@ public struct GroupListView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
|
viewModel.setCommandClient(commandClient)
|
||||||
viewModel.connect()
|
viewModel.connect()
|
||||||
}
|
}
|
||||||
.onDisappear {
|
|
||||||
viewModel.disconnect()
|
|
||||||
}
|
|
||||||
.onChangeCompat(of: scenePhase) { newValue in
|
|
||||||
if newValue == .active {
|
|
||||||
viewModel.connect()
|
|
||||||
} else {
|
|
||||||
viewModel.disconnect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,14 @@ public class GroupListViewModel: ObservableObject {
|
|||||||
@Published public var isLoading = true
|
@Published public var isLoading = true
|
||||||
@Published public var groups: [OutboundGroup] = []
|
@Published public var groups: [OutboundGroup] = []
|
||||||
|
|
||||||
private let commandClient = CommandClient(.groups)
|
private var commandClient: CommandClient?
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
public init() {
|
public init() {}
|
||||||
commandClient.$groups
|
|
||||||
|
public func setCommandClient(_ client: CommandClient) {
|
||||||
|
commandClient = client
|
||||||
|
client.$groups
|
||||||
.compactMap { $0 }
|
.compactMap { $0 }
|
||||||
.sink { [weak self] goGroups in
|
.sink { [weak self] goGroups in
|
||||||
self?.setGroups(goGroups)
|
self?.setGroups(goGroups)
|
||||||
@@ -34,15 +37,9 @@ public class GroupListViewModel: ObservableObject {
|
|||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
isLoading = false
|
isLoading = false
|
||||||
} else {
|
|
||||||
commandClient.connect()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func disconnect() {
|
|
||||||
commandClient.disconnect()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setGroups(_ goGroups: [LibboxOutboundGroup]) {
|
private func setGroups(_ goGroups: [LibboxOutboundGroup]) {
|
||||||
var groups = [OutboundGroup]()
|
var groups = [OutboundGroup]()
|
||||||
for goGroup in goGroups {
|
for goGroup in goGroups {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public class CommandClient: ObservableObject {
|
|||||||
case connections
|
case connections
|
||||||
}
|
}
|
||||||
|
|
||||||
private let connectionType: ConnectionType
|
private let connectionTypes: [ConnectionType]
|
||||||
private let logMaxLines: Int
|
private let logMaxLines: Int
|
||||||
private var commandClient: LibboxCommandClient?
|
private var commandClient: LibboxCommandClient?
|
||||||
private var connectTask: Task<Void, Error>?
|
private var connectTask: Task<Void, Error>?
|
||||||
@@ -27,8 +27,8 @@ public class CommandClient: ObservableObject {
|
|||||||
@Published public var connections: [LibboxConnection]?
|
@Published public var connections: [LibboxConnection]?
|
||||||
public var rawConnections: LibboxConnections?
|
public var rawConnections: LibboxConnections?
|
||||||
|
|
||||||
public init(_ connectionType: ConnectionType, logMaxLines: Int = 300) {
|
public init(_ connectionTypes: [ConnectionType], logMaxLines: Int = 3000) {
|
||||||
self.connectionType = connectionType
|
self.connectionTypes = connectionTypes
|
||||||
self.logMaxLines = logMaxLines
|
self.logMaxLines = logMaxLines
|
||||||
logList = []
|
logList = []
|
||||||
clashModeList = []
|
clashModeList = []
|
||||||
@@ -36,6 +36,10 @@ public class CommandClient: ObservableObject {
|
|||||||
isConnected = false
|
isConnected = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public convenience init(_ connectionType: ConnectionType, logMaxLines: Int = 300) {
|
||||||
|
self.init([connectionType], logMaxLines: logMaxLines)
|
||||||
|
}
|
||||||
|
|
||||||
public func connect() {
|
public func connect() {
|
||||||
if isConnected {
|
if isConnected {
|
||||||
return
|
return
|
||||||
@@ -94,27 +98,28 @@ public class CommandClient: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private nonisolated func connect0() async {
|
private nonisolated func connect0() async {
|
||||||
if connectionType == .connections {
|
if connectionTypes.contains(.connections) {
|
||||||
await initializeConnectionFilterState()
|
await initializeConnectionFilterState()
|
||||||
}
|
}
|
||||||
|
|
||||||
let clientOptions = LibboxCommandClientOptions()
|
let clientOptions = LibboxCommandClientOptions()
|
||||||
|
for connectionType in connectionTypes {
|
||||||
switch connectionType {
|
switch connectionType {
|
||||||
case .status:
|
case .status:
|
||||||
clientOptions.command = LibboxCommandStatus
|
clientOptions.addCommand(LibboxCommandStatus)
|
||||||
case .groups:
|
case .groups:
|
||||||
clientOptions.command = LibboxCommandGroup
|
clientOptions.addCommand(LibboxCommandGroup)
|
||||||
case .log:
|
case .log:
|
||||||
clientOptions.command = LibboxCommandLog
|
clientOptions.addCommand(LibboxCommandLog)
|
||||||
case .clashMode:
|
case .clashMode:
|
||||||
clientOptions.command = LibboxCommandClashMode
|
clientOptions.addCommand(LibboxCommandClashMode)
|
||||||
case .connections:
|
case .connections:
|
||||||
clientOptions.command = LibboxCommandConnections
|
clientOptions.addCommand(LibboxCommandConnections)
|
||||||
}
|
}
|
||||||
switch connectionType {
|
}
|
||||||
case .log:
|
if connectionTypes.contains(.log) {
|
||||||
clientOptions.statusInterval = Int64(500 * NSEC_PER_MSEC)
|
clientOptions.statusInterval = Int64(500 * NSEC_PER_MSEC)
|
||||||
default:
|
} else {
|
||||||
clientOptions.statusInterval = Int64(NSEC_PER_SEC)
|
clientOptions.statusInterval = Int64(NSEC_PER_SEC)
|
||||||
}
|
}
|
||||||
let client = LibboxNewCommandClient(clientHandler(self), clientOptions)!
|
let client = LibboxNewCommandClient(clientHandler(self), clientOptions)!
|
||||||
@@ -145,7 +150,7 @@ public class CommandClient: ObservableObject {
|
|||||||
|
|
||||||
func connected() {
|
func connected() {
|
||||||
DispatchQueue.main.async { [self] in
|
DispatchQueue.main.async { [self] in
|
||||||
if commandClient.connectionType == .log {
|
if commandClient.connectionTypes.contains(.log) {
|
||||||
commandClient.logList = []
|
commandClient.logList = []
|
||||||
}
|
}
|
||||||
commandClient.isConnected = true
|
commandClient.isConnected = true
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ open class ExtensionProvider: NEPacketTunnelProvider {
|
|||||||
options.basePath = FilePath.sharedDirectory.relativePath
|
options.basePath = FilePath.sharedDirectory.relativePath
|
||||||
options.workingPath = FilePath.workingDirectory.relativePath
|
options.workingPath = FilePath.workingDirectory.relativePath
|
||||||
options.tempPath = FilePath.cacheDirectory.relativePath
|
options.tempPath = FilePath.cacheDirectory.relativePath
|
||||||
|
options.logMaxLines = 3000
|
||||||
var setupError: NSError?
|
var setupError: NSError?
|
||||||
LibboxSetup(options, &setupError)
|
LibboxSetup(options, &setupError)
|
||||||
if let setupError {
|
if let setupError {
|
||||||
|
|||||||
Reference in New Issue
Block a user