mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-04-10 18:55:52 +00:00
276 lines
11 KiB
Swift
276 lines
11 KiB
Swift
//
|
|
// NetworkServersView.swift
|
|
// SimpleX (iOS)
|
|
//
|
|
// Created by Evgeny on 02/08/2022.
|
|
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
|
//
|
|
|
|
import SwiftUI
|
|
import SimpleXChat
|
|
|
|
private enum NetworkAlert: Identifiable {
|
|
case updateOnionHosts(hosts: OnionHosts)
|
|
case updateSessionMode(mode: TransportSessionMode)
|
|
case updateSMPProxyMode(proxyMode: SMPProxyMode)
|
|
case updateSMPProxyFallback(proxyFallback: SMPProxyFallback)
|
|
case error(err: String)
|
|
|
|
var id: String {
|
|
switch self {
|
|
case let .updateOnionHosts(hosts): return "updateOnionHosts \(hosts)"
|
|
case let .updateSessionMode(mode): return "updateSessionMode \(mode)"
|
|
case let .updateSMPProxyMode(proxyMode): return "updateSMPProxyMode \(proxyMode)"
|
|
case let .updateSMPProxyFallback(proxyFallback): return "updateSMPProxyFallback \(proxyFallback)"
|
|
case let .error(err): return "error \(err)"
|
|
}
|
|
}
|
|
}
|
|
|
|
struct NetworkAndServers: View {
|
|
@EnvironmentObject var m: ChatModel
|
|
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
|
|
@AppStorage(DEFAULT_SHOW_SENT_VIA_RPOXY) private var showSentViaProxy = true
|
|
@State private var cfgLoaded = false
|
|
@State private var currentNetCfg = NetCfg.defaults
|
|
@State private var netCfg = NetCfg.defaults
|
|
@State private var onionHosts: OnionHosts = .no
|
|
@State private var sessionMode: TransportSessionMode = .user
|
|
@State private var proxyMode: SMPProxyMode = .never
|
|
@State private var proxyFallback: SMPProxyFallback = .allow
|
|
@State private var alert: NetworkAlert?
|
|
|
|
var body: some View {
|
|
VStack {
|
|
List {
|
|
Section {
|
|
NavigationLink {
|
|
ProtocolServersView(serverProtocol: .smp)
|
|
.navigationTitle("Your SMP servers")
|
|
} label: {
|
|
Text("SMP servers")
|
|
}
|
|
|
|
NavigationLink {
|
|
ProtocolServersView(serverProtocol: .xftp)
|
|
.navigationTitle("Your XFTP servers")
|
|
} label: {
|
|
Text("XFTP servers")
|
|
}
|
|
|
|
Picker("Use .onion hosts", selection: $onionHosts) {
|
|
ForEach(OnionHosts.values, id: \.self) { Text($0.text) }
|
|
}
|
|
.frame(height: 36)
|
|
|
|
if developerTools {
|
|
Picker("Transport isolation", selection: $sessionMode) {
|
|
ForEach(TransportSessionMode.values, id: \.self) { Text($0.text) }
|
|
}
|
|
.frame(height: 36)
|
|
}
|
|
|
|
NavigationLink {
|
|
AdvancedNetworkSettings()
|
|
.navigationTitle("Network settings")
|
|
} label: {
|
|
Text("Advanced network settings")
|
|
}
|
|
} header: {
|
|
Text("Messages & files")
|
|
} footer: {
|
|
Text("Using .onion hosts requires compatible VPN provider.")
|
|
}
|
|
|
|
Section {
|
|
Picker("Private routing", selection: $proxyMode) {
|
|
ForEach(SMPProxyMode.values, id: \.self) { Text($0.text) }
|
|
}
|
|
.frame(height: 36)
|
|
|
|
Picker("Allow downgrade", selection: $proxyFallback) {
|
|
ForEach(SMPProxyFallback.values, id: \.self) { Text($0.text) }
|
|
}
|
|
.disabled(proxyMode == .never)
|
|
.frame(height: 36)
|
|
|
|
Toggle("Show message status", isOn: $showSentViaProxy)
|
|
} header: {
|
|
Text("Private message routing")
|
|
} footer: {
|
|
VStack(alignment: .leading) {
|
|
Text("To protect your IP address, private routing uses your SMP servers to deliver messages.")
|
|
if showSentViaProxy {
|
|
Text("Show → on messages sent via private routing.")
|
|
}
|
|
}
|
|
}
|
|
|
|
Section("Calls") {
|
|
NavigationLink {
|
|
RTCServers()
|
|
.navigationTitle("Your ICE servers")
|
|
} label: {
|
|
Text("WebRTC ICE servers")
|
|
}
|
|
}
|
|
|
|
Section("Network connection") {
|
|
HStack {
|
|
Text(m.networkInfo.networkType.text)
|
|
Spacer()
|
|
Image(systemName: "circle.fill").foregroundColor(m.networkInfo.online ? .green : .red)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.onAppear {
|
|
if cfgLoaded { return }
|
|
cfgLoaded = true
|
|
currentNetCfg = getNetCfg()
|
|
resetNetCfgView()
|
|
}
|
|
.onChange(of: onionHosts) { hosts in
|
|
if hosts != OnionHosts(netCfg: currentNetCfg) {
|
|
alert = .updateOnionHosts(hosts: hosts)
|
|
}
|
|
}
|
|
.onChange(of: sessionMode) { mode in
|
|
if mode != netCfg.sessionMode {
|
|
alert = .updateSessionMode(mode: mode)
|
|
}
|
|
}
|
|
.onChange(of: proxyMode) { mode in
|
|
if mode != netCfg.smpProxyMode {
|
|
alert = .updateSMPProxyMode(proxyMode: mode)
|
|
}
|
|
}
|
|
.onChange(of: proxyFallback) { fallbackMode in
|
|
if fallbackMode != netCfg.smpProxyFallback {
|
|
alert = .updateSMPProxyFallback(proxyFallback: fallbackMode)
|
|
}
|
|
}
|
|
.alert(item: $alert) { a in
|
|
switch a {
|
|
case let .updateOnionHosts(hosts):
|
|
return Alert(
|
|
title: Text("Update .onion hosts setting?"),
|
|
message: Text(onionHostsInfo(hosts)) + Text("\n") + Text("Updating this setting will re-connect the client to all servers."),
|
|
primaryButton: .default(Text("Ok")) {
|
|
let (hostMode, requiredHostMode) = hosts.hostMode
|
|
netCfg.hostMode = hostMode
|
|
netCfg.requiredHostMode = requiredHostMode
|
|
saveNetCfg()
|
|
},
|
|
secondaryButton: .cancel() {
|
|
resetNetCfgView()
|
|
}
|
|
)
|
|
case let .updateSessionMode(mode):
|
|
return Alert(
|
|
title: Text("Update transport isolation mode?"),
|
|
message: Text(sessionModeInfo(mode)) + Text("\n") + Text("Updating this setting will re-connect the client to all servers."),
|
|
primaryButton: .default(Text("Ok")) {
|
|
netCfg.sessionMode = mode
|
|
saveNetCfg()
|
|
},
|
|
secondaryButton: .cancel() {
|
|
resetNetCfgView()
|
|
}
|
|
)
|
|
case let .updateSMPProxyMode(proxyMode):
|
|
return Alert(
|
|
title: Text("Message routing mode"),
|
|
message: Text(proxyModeInfo(proxyMode)) + Text("\n") + Text("Updating this setting will re-connect the client to all servers."),
|
|
primaryButton: .default(Text("Ok")) {
|
|
netCfg.smpProxyMode = proxyMode
|
|
saveNetCfg()
|
|
},
|
|
secondaryButton: .cancel() {
|
|
resetNetCfgView()
|
|
}
|
|
)
|
|
case let .updateSMPProxyFallback(proxyFallback):
|
|
return Alert(
|
|
title: Text("Message routing fallback"),
|
|
message: Text(proxyFallbackInfo(proxyFallback)) + Text("\n") + Text("Updating this setting will re-connect the client to all servers."),
|
|
primaryButton: .default(Text("Ok")) {
|
|
netCfg.smpProxyFallback = proxyFallback
|
|
saveNetCfg()
|
|
},
|
|
secondaryButton: .cancel() {
|
|
resetNetCfgView()
|
|
}
|
|
)
|
|
case let .error(err):
|
|
return Alert(
|
|
title: Text("Error updating settings"),
|
|
message: Text(err)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func saveNetCfg() {
|
|
do {
|
|
let def = netCfg.hostMode == .onionHost ? NetCfg.proxyDefaults : NetCfg.defaults
|
|
netCfg.tcpConnectTimeout = def.tcpConnectTimeout
|
|
netCfg.tcpTimeout = def.tcpTimeout
|
|
try setNetworkConfig(netCfg)
|
|
currentNetCfg = netCfg
|
|
setNetCfg(netCfg)
|
|
} catch let error {
|
|
let err = responseError(error)
|
|
resetNetCfgView()
|
|
alert = .error(err: err)
|
|
logger.error("\(err)")
|
|
}
|
|
}
|
|
|
|
private func resetNetCfgView() {
|
|
netCfg = currentNetCfg
|
|
onionHosts = OnionHosts(netCfg: netCfg)
|
|
sessionMode = netCfg.sessionMode
|
|
proxyMode = netCfg.smpProxyMode
|
|
proxyFallback = netCfg.smpProxyFallback
|
|
}
|
|
|
|
private func onionHostsInfo(_ hosts: OnionHosts) -> LocalizedStringKey {
|
|
switch hosts {
|
|
case .no: return "Onion hosts will not be used."
|
|
case .prefer: return "Onion hosts will be used when available. Requires enabling VPN."
|
|
case .require: return "Onion hosts will be required for connection. Requires enabling VPN."
|
|
}
|
|
}
|
|
|
|
private func sessionModeInfo(_ mode: TransportSessionMode) -> LocalizedStringKey {
|
|
switch mode {
|
|
case .user: return "A separate TCP connection will be used **for each chat profile you have in the app**."
|
|
case .entity: return "A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail."
|
|
}
|
|
}
|
|
|
|
private func proxyModeInfo(_ mode: SMPProxyMode) -> LocalizedStringKey {
|
|
switch mode {
|
|
case .always: return "Always use private routing."
|
|
case .unknown: return "Use private routing with unknown servers."
|
|
case .unprotected: return "Use private routing with unknown servers when IP address is not protected."
|
|
case .never: return "Do NOT use private routing."
|
|
}
|
|
}
|
|
|
|
private func proxyFallbackInfo(_ proxyFallback: SMPProxyFallback) -> LocalizedStringKey {
|
|
switch proxyFallback {
|
|
case .allow: return "Send messages directly when your or destination server does not support private routing."
|
|
case .allowProtected: return "Send messages directly when IP address is protected and your or destination server does not support private routing."
|
|
case .prohibit: return "Do NOT send messages directly, even if your or destination server does not support private routing."
|
|
}
|
|
}
|
|
}
|
|
|
|
struct NetworkServersView_Previews: PreviewProvider {
|
|
static var previews: some View {
|
|
NetworkAndServers()
|
|
}
|
|
}
|