mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-03 04:40:09 +00:00
rework indicator
This commit is contained in:
@@ -264,7 +264,8 @@ struct ChatListView: View {
|
||||
}
|
||||
|
||||
struct SubsStatusIndicator: View {
|
||||
@State private var subs: SMPServerSubs = SMPServerSubs(ssActive: 0, ssPending: 0)
|
||||
@State private var subs: SMPServerSubs = SMPServerSubs.newSMPServerSubs
|
||||
@State private var sess: ServerSessions = ServerSessions.newServerSessions
|
||||
@State private var timer: Timer? = nil
|
||||
@State private var timerCounter = 0
|
||||
@State private var showServersSummary = false
|
||||
@@ -278,7 +279,7 @@ struct SubsStatusIndicator: View {
|
||||
Button {
|
||||
showServersSummary = true
|
||||
} label: {
|
||||
SubscriptionStatusView(subs: subs)
|
||||
SubscriptionStatusView(subs: subs, sess: sess)
|
||||
}
|
||||
.onAppear {
|
||||
startInitialTimer()
|
||||
@@ -293,7 +294,7 @@ struct SubsStatusIndicator: View {
|
||||
|
||||
private func startInitialTimer() {
|
||||
timer = Timer.scheduledTimer(withTimeInterval: initialInterval, repeats: true) { _ in
|
||||
getSubsSummary()
|
||||
getServersSummary()
|
||||
timerCounter += 1
|
||||
// Switch to the regular timer after the initial phase
|
||||
if timerCounter * Int(initialInterval) >= Int(initialPhaseDuration) {
|
||||
@@ -305,7 +306,7 @@ struct SubsStatusIndicator: View {
|
||||
func switchToRegularTimer() {
|
||||
timer?.invalidate()
|
||||
timer = Timer.scheduledTimer(withTimeInterval: regularInterval, repeats: true) { _ in
|
||||
getSubsSummary()
|
||||
getServersSummary()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,11 +315,12 @@ struct SubsStatusIndicator: View {
|
||||
timer = nil
|
||||
}
|
||||
|
||||
private func getSubsSummary() {
|
||||
private func getServersSummary() {
|
||||
do {
|
||||
subs = try getAgentSubsSummary()
|
||||
let summ = try getAgentServersSummary()
|
||||
(subs, sess) = (summ.allUsersSMP.smpTotals.subs, summ.allUsersSMP.smpTotals.sessions)
|
||||
} catch let error {
|
||||
logger.error("getAgentSubsSummary error: \(responseError(error))")
|
||||
logger.error("getAgentServersSummary error: \(responseError(error))")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,10 +207,10 @@ struct ServersSummaryView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func smpServerView(_ server: SMPServerSummary, _ showReconnectButton: Bool, _ statsStartedAt: Date) -> some View {
|
||||
NavigationLink(tag: server.id, selection: $selectedSMPServer) {
|
||||
private func smpServerView(_ srvSumm: SMPServerSummary, _ showReconnectButton: Bool, _ statsStartedAt: Date) -> some View {
|
||||
NavigationLink(tag: srvSumm.id, selection: $selectedSMPServer) {
|
||||
SMPServerSummaryView(
|
||||
summary: server,
|
||||
summary: srvSumm,
|
||||
showReconnectButton: showReconnectButton,
|
||||
statsStartedAt: statsStartedAt
|
||||
)
|
||||
@@ -218,12 +218,15 @@ struct ServersSummaryView: View {
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
} label: {
|
||||
HStack {
|
||||
if let subs = server.subs {
|
||||
SubscriptionStatusView(subs: subs)
|
||||
.frame(width: 16, alignment: .center)
|
||||
.padding(.trailing, 4)
|
||||
if srvSumm.connected {
|
||||
SubscriptionStatusView(
|
||||
subs: srvSumm.subsOrNew,
|
||||
sess: srvSumm.sessionsOrNew
|
||||
)
|
||||
.frame(width: 16, alignment: .center)
|
||||
.padding(.trailing, 4)
|
||||
}
|
||||
Text(serverAddress(server.smpServer))
|
||||
Text(serverAddress(srvSumm.smpServer))
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
@@ -255,16 +258,16 @@ struct ServersSummaryView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func xftpServerView(_ server: XFTPServerSummary, _ statsStartedAt: Date) -> some View {
|
||||
NavigationLink(tag: server.id, selection: $selectedXFTPServer) {
|
||||
private func xftpServerView(_ srvSumm: XFTPServerSummary, _ statsStartedAt: Date) -> some View {
|
||||
NavigationLink(tag: srvSumm.id, selection: $selectedXFTPServer) {
|
||||
XFTPServerSummaryView(
|
||||
summary: server,
|
||||
summary: srvSumm,
|
||||
statsStartedAt: statsStartedAt
|
||||
)
|
||||
.navigationBarTitle("XFTP server")
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
} label: {
|
||||
Text(serverAddress(server.xftpServer))
|
||||
Text(serverAddress(srvSumm.xftpServer))
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
@@ -314,43 +317,50 @@ struct ServersSummaryView: View {
|
||||
struct SubscriptionStatusView: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
var subs: SMPServerSubs
|
||||
var sess: ServerSessions
|
||||
|
||||
var body: some View {
|
||||
let netInfo = m.networkInfo
|
||||
if netInfo.online {
|
||||
let (color, variableValue, opacity) = onlineIconColor
|
||||
if #available(iOS 16.0, *) {
|
||||
Image(systemName: "dot.radiowaves.up.forward", variableValue: variableValue)
|
||||
.foregroundColor(color)
|
||||
} else {
|
||||
Image(systemName: "dot.radiowaves.up.forward")
|
||||
.foregroundColor(color.opacity(opacity))
|
||||
}
|
||||
let (color, variableValue, opacity) = iconColor
|
||||
if #available(iOS 16.0, *) {
|
||||
Image(systemName: "dot.radiowaves.up.forward", variableValue: variableValue)
|
||||
.foregroundColor(color)
|
||||
} else {
|
||||
Image(systemName: "dot.radiowaves.up.forward")
|
||||
.foregroundColor(Color(uiColor: .tertiaryLabel))
|
||||
.foregroundColor(color.opacity(opacity))
|
||||
}
|
||||
}
|
||||
|
||||
var onlineIconColor: (Color, Double, Double) {
|
||||
if subs.ssActive > 0 {
|
||||
let variableValue = roundToQuarter()
|
||||
return (.accentColor, variableValue, activeSubsPercentage)
|
||||
} else {
|
||||
return (Color(uiColor: .tertiaryLabel), 1, 1)
|
||||
}
|
||||
private var iconColor: (Color, Double, Double) {
|
||||
m.networkInfo.online && (subs.total > 0 || sess.total > 0)
|
||||
? ( // Status to be displayed based on subs
|
||||
subs.total > 0
|
||||
? (
|
||||
subs.ssActive == 0
|
||||
? (
|
||||
sess.ssConnected == 0 ? noConnColor : (.accentColor, activeSubsRounded, subs.shareOfActive)
|
||||
)
|
||||
: ( // ssActive > 0
|
||||
sess.ssConnected == 0
|
||||
? (.orange, activeSubsRounded, subs.shareOfActive) // This would mean implementation error
|
||||
: (.accentColor, activeSubsRounded, subs.shareOfActive)
|
||||
)
|
||||
)
|
||||
// subs.total == 0 and sess.total > 0; Status to be displayed based on sessions
|
||||
: (.accentColor, connectedSessRounded, sess.shareOfConnected)
|
||||
)
|
||||
: noConnColor
|
||||
}
|
||||
|
||||
func roundToQuarter() -> Double {
|
||||
activeSubsPercentage >= 1 ? 1
|
||||
: activeSubsPercentage <= 0 ? 0
|
||||
: (activeSubsPercentage * 4).rounded() / 4
|
||||
}
|
||||
private var noConnColor: (Color, Double, Double) { (Color(uiColor: .tertiaryLabel), 1, 1) }
|
||||
|
||||
var activeSubsPercentage: Double {
|
||||
let total = subs.ssActive + subs.ssPending
|
||||
guard total != 0 else { return 0.0 }
|
||||
return Double(subs.ssActive) / Double(total)
|
||||
private var activeSubsRounded: Double { roundedToQuarter(subs.shareOfActive) }
|
||||
|
||||
private var connectedSessRounded: Double { roundedToQuarter(sess.shareOfConnected) }
|
||||
|
||||
private func roundedToQuarter(_ n: Double) -> Double {
|
||||
n >= 1 ? 1
|
||||
: n <= 0 ? 0
|
||||
: (n * 4).rounded() / 4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,8 +390,17 @@ struct SMPServerSummaryView: View {
|
||||
}
|
||||
}
|
||||
|
||||
if showReconnectButton {
|
||||
reconnectButtonSection()
|
||||
if summary.connected && showReconnectButton {
|
||||
Section {
|
||||
connectionStatusRow()
|
||||
reconnectButtonSection()
|
||||
} footer: {
|
||||
if summary.subs != nil {
|
||||
Text("Connection status is displayed based on Message subscriptions.")
|
||||
} else if summary.sessions != nil {
|
||||
Text("Connection status is displayed based on Transport sessions.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let subs = summary.subs {
|
||||
@@ -399,35 +418,44 @@ struct SMPServerSummaryView: View {
|
||||
.alert(item: $alert) { $0.alert }
|
||||
}
|
||||
|
||||
private func connectionStatusRow() -> some View {
|
||||
HStack {
|
||||
Text("Connection status")
|
||||
Spacer()
|
||||
SubscriptionStatusView(
|
||||
subs: summary.subsOrNew,
|
||||
sess: summary.sessionsOrNew
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func reconnectButtonSection() -> some View {
|
||||
Section {
|
||||
Button {
|
||||
alert = SomeAlert(
|
||||
alert: Alert(
|
||||
title: Text("Reconnect server?"),
|
||||
message: Text("Reconnect server to force message delivery. It uses additional traffic."),
|
||||
primaryButton: .default(Text("Ok")) {
|
||||
Task {
|
||||
do {
|
||||
try await reconnectServer(smpServer: summary.smpServer)
|
||||
} catch let error {
|
||||
alert = SomeAlert(
|
||||
alert: mkAlert(
|
||||
title: "Error reconnecting server",
|
||||
message: "\(responseError(error))"
|
||||
),
|
||||
id: "error reconnecting server"
|
||||
)
|
||||
}
|
||||
Button {
|
||||
alert = SomeAlert(
|
||||
alert: Alert(
|
||||
title: Text("Reconnect server?"),
|
||||
message: Text("Reconnect server to force message delivery. It uses additional traffic."),
|
||||
primaryButton: .default(Text("Ok")) {
|
||||
Task {
|
||||
do {
|
||||
try await reconnectServer(smpServer: summary.smpServer)
|
||||
} catch let error {
|
||||
alert = SomeAlert(
|
||||
alert: mkAlert(
|
||||
title: "Error reconnecting server",
|
||||
message: "\(responseError(error))"
|
||||
),
|
||||
id: "error reconnecting server"
|
||||
)
|
||||
}
|
||||
},
|
||||
secondaryButton: .cancel()
|
||||
),
|
||||
id: "reconnect server question"
|
||||
)
|
||||
} label: {
|
||||
Text("Reconnect")
|
||||
}
|
||||
}
|
||||
},
|
||||
secondaryButton: .cancel()
|
||||
),
|
||||
id: "reconnect server question"
|
||||
)
|
||||
} label: {
|
||||
Text("Reconnect")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -437,15 +465,12 @@ struct SMPSubsView: View {
|
||||
var showPending: Bool
|
||||
|
||||
var body: some View {
|
||||
Section {
|
||||
Section("Message subscriptions") {
|
||||
infoRow("Active", "\(subs.ssActive)")
|
||||
infoRow("Pending", "\(subs.ssPending)")
|
||||
infoRow("Total", "\(subs.ssActive + subs.ssPending)")
|
||||
} header: {
|
||||
HStack {
|
||||
Text("Message subscriptions")
|
||||
SubscriptionStatusView(subs: subs)
|
||||
if showPending {
|
||||
infoRow("Pending", "\(subs.ssPending)")
|
||||
}
|
||||
infoRow("Total", "\(subs.total)")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -458,6 +483,7 @@ struct ServerSessionsView: View {
|
||||
infoRow("Connected", "\(sess.ssConnected)")
|
||||
infoRow("Errors", "\(sess.ssErrors)")
|
||||
infoRow("Connecting", "\(sess.ssConnecting)")
|
||||
infoRow("Total", "\(sess.total)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2281,12 +2281,35 @@ public struct SMPServerSummary: Codable, Identifiable {
|
||||
public var id: String { smpServer }
|
||||
|
||||
public var connected: Bool { sessions != nil || subs != nil }
|
||||
|
||||
public var sessionsOrNew: ServerSessions { sessions ?? ServerSessions.newServerSessions }
|
||||
|
||||
public var subsOrNew: SMPServerSubs { subs ?? SMPServerSubs.newSMPServerSubs }
|
||||
}
|
||||
|
||||
public struct ServerSessions: Codable {
|
||||
public var ssConnected: Int
|
||||
public var ssErrors: Int
|
||||
public var ssConnecting: Int
|
||||
|
||||
public init(ssConnected: Int, ssErrors: Int, ssConnecting: Int) {
|
||||
self.ssConnected = ssConnected
|
||||
self.ssErrors = ssErrors
|
||||
self.ssConnecting = ssConnecting
|
||||
}
|
||||
|
||||
static public var newServerSessions = ServerSessions(
|
||||
ssConnected: 0,
|
||||
ssErrors: 0,
|
||||
ssConnecting: 0
|
||||
)
|
||||
|
||||
public var total: Int { ssConnected + ssErrors + ssConnecting }
|
||||
|
||||
public var shareOfConnected: Double {
|
||||
guard total != 0 else { return 0.0 }
|
||||
return Double(ssConnected) / Double(total)
|
||||
}
|
||||
}
|
||||
|
||||
public struct SMPServerSubs: Codable {
|
||||
@@ -2297,6 +2320,18 @@ public struct SMPServerSubs: Codable {
|
||||
self.ssActive = ssActive
|
||||
self.ssPending = ssPending
|
||||
}
|
||||
|
||||
static public var newSMPServerSubs = SMPServerSubs(
|
||||
ssActive: 0,
|
||||
ssPending: 0
|
||||
)
|
||||
|
||||
public var total: Int { ssActive + ssPending }
|
||||
|
||||
public var shareOfActive: Double {
|
||||
guard total != 0 else { return 0.0 }
|
||||
return Double(ssActive) / Double(total)
|
||||
}
|
||||
}
|
||||
|
||||
public struct AgentSMPServerStatsData: Codable {
|
||||
|
||||
Reference in New Issue
Block a user