diff --git a/apps/ios/Shared/Views/ChatList/ServersSummaryView.swift b/apps/ios/Shared/Views/ChatList/ServersSummaryView.swift index a6a353c068..d0f955863b 100644 --- a/apps/ios/Shared/Views/ChatList/ServersSummaryView.swift +++ b/apps/ios/Shared/Views/ChatList/ServersSummaryView.swift @@ -15,6 +15,7 @@ struct ServersSummaryView: View { @State private var selectedServerType: PresentedServerType = .smp @State private var selectedSMPServer: String? = nil @State private var selectedXFTPServer: String? = nil + @State private var timer: Timer? = nil @State private var alert: SomeAlert? enum PresentedUserCategory { @@ -33,9 +34,6 @@ struct ServersSummaryView: View { .navigationTitle("Servers info") .navigationBarTitleDisplayMode(.inline) .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { - reloadButton() - } ToolbarItem(placement: .navigationBarTrailing) { shareButton() } @@ -43,10 +41,25 @@ struct ServersSummaryView: View { } .onAppear { getServersSummary() + startTimer() + } + .onDisappear { + stopTimer() } .alert(item: $alert) { $0.alert } } + private func startTimer() { + timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in + getServersSummary() + } + } + + func stopTimer() { + timer?.invalidate() + timer = nil + } + private func shareButton() -> some View { Button { if let serversSummary = serversSummary { @@ -65,14 +78,6 @@ struct ServersSummaryView: View { return String(decoding: data, as: UTF8.self) } - private func reloadButton() -> some View { - Button { - getServersSummary() - } label: { - Image(systemName: "arrow.counterclockwise") - } - } - @ViewBuilder private func viewBody() -> some View { if let summ = serversSummary { List { @@ -84,8 +89,8 @@ struct ServersSummaryView: View { .pickerStyle(.segmented) Picker("Server type", selection: $selectedServerType) { - Text("SMP").tag(PresentedServerType.smp) - Text("XFTP").tag(PresentedServerType.xftp) + Text("Messages").tag(PresentedServerType.smp) + Text("Files").tag(PresentedServerType.xftp) } .pickerStyle(.segmented) } @@ -95,59 +100,71 @@ struct ServersSummaryView: View { switch (selectedUserCategory, selectedServerType) { case (.allUsers, .smp): - if summ.allUsedSMP.count > 0 || summ.allPrevSMP.count > 0 || summ.allProxSMP.count > 0 { - if summ.allUsedSMP.count > 0 { - smpServersListView(summ.allUsedSMP, showReconnectButton: true, summ.statsStartedAt, "Current session") - } - if summ.allPrevSMP.count > 0 { - smpServersListView(summ.allPrevSMP, showReconnectButton: false, summ.statsStartedAt, "Previously used") - } - if summ.allProxSMP.count > 0 { - smpServersListView(summ.allProxSMP, showReconnectButton: false, summ.statsStartedAt, "Proxied", "You are not connected to these servers directly.") - } - resetStatsButtonSection() - } else { - noCategoryInfoText() + + SMPSubsView(subs: summ.allSMPTotal.subs) + + ServerSessionsView(sess: summ.allSMPTotal.sessions) + + if summ.allUsedSMP.count > 0 { + smpServersListView(summ.allUsedSMP, showReconnectButton: true, summ.statsStartedAt, "Current app session") } + if summ.allPrevSMP.count > 0 { + smpServersListView(summ.allPrevSMP, showReconnectButton: false, summ.statsStartedAt, "Previously used") + } + if summ.allProxSMP.count > 0 { + smpServersListView(summ.allProxSMP, showReconnectButton: false, summ.statsStartedAt, "Proxied", "You are not connected to these servers directly.") + } + + SMPStatsView(stats: summ.allSMPTotal.stats, statsStartedAt: summ.statsStartedAt) + + resetStatsButtonSection() case (.currentUser, .smp): - if summ.userUsedSMP.count > 0 || summ.userPrevSMP.count > 0 || summ.userProxSMP.count > 0 { - if summ.userUsedSMP.count > 0 { - smpServersListView(summ.userUsedSMP, showReconnectButton: true, summ.statsStartedAt, "Current session") - } - if summ.userPrevSMP.count > 0 { - smpServersListView(summ.userPrevSMP, showReconnectButton: false, summ.statsStartedAt, "Previously used") - } - if summ.userProxSMP.count > 0 { - smpServersListView(summ.userProxSMP, showReconnectButton: false, summ.statsStartedAt, "Proxied", "You are not connected to these servers directly.") - } - resetStatsButtonSection() - } else { - noCategoryInfoText() + + SMPSubsView(subs: summ.userSMPTotal.subs) + + ServerSessionsView(sess: summ.userSMPTotal.sessions) + + if summ.userUsedSMP.count > 0 { + smpServersListView(summ.userUsedSMP, showReconnectButton: true, summ.statsStartedAt, "Current app session") } + if summ.userPrevSMP.count > 0 { + smpServersListView(summ.userPrevSMP, showReconnectButton: false, summ.statsStartedAt, "Previously used") + } + if summ.userProxSMP.count > 0 { + smpServersListView(summ.userProxSMP, showReconnectButton: false, summ.statsStartedAt, "Proxied", "You are not connected to these servers directly.") + } + + SMPStatsView(stats: summ.userSMPTotal.stats, statsStartedAt: summ.statsStartedAt) + + resetStatsButtonSection() case (.allUsers, .xftp): - if summ.allUsedXFTP.count > 0 || summ.allPrevXFTP.count > 0 { - if summ.allUsedXFTP.count > 0 { - xftpServersListView(summ.allUsedXFTP, summ.statsStartedAt, "Current session") - } - if summ.allPrevXFTP.count > 0 { - xftpServersListView(summ.allPrevXFTP, summ.statsStartedAt, "Previously used") - } - resetStatsButtonSection() - } else { - noCategoryInfoText() + + ServerSessionsView(sess: summ.allXFTPTotal.sessions) + + if summ.allUsedXFTP.count > 0 { + xftpServersListView(summ.allUsedXFTP, summ.statsStartedAt, "Current app session") } + if summ.allPrevXFTP.count > 0 { + xftpServersListView(summ.allPrevXFTP, summ.statsStartedAt, "Previously used") + } + + XFTPStatsView(stats: summ.allXFTPTotal.stats, statsStartedAt: summ.statsStartedAt) + + resetStatsButtonSection() case (.currentUser, .xftp): - if summ.userUsedXFTP.count > 0 || summ.userPrevXFTP.count > 0 { - if summ.userUsedXFTP.count > 0 { - xftpServersListView(summ.userUsedXFTP, summ.statsStartedAt, "Current session") - } - if summ.userPrevXFTP.count > 0 { - xftpServersListView(summ.userPrevXFTP, summ.statsStartedAt, "Previously used") - } - resetStatsButtonSection() - } else { - noCategoryInfoText() + + ServerSessionsView(sess: summ.userXFTPTotal.sessions) + + if summ.userUsedXFTP.count > 0 { + xftpServersListView(summ.userUsedXFTP, summ.statsStartedAt, "Current app session") } + if summ.userPrevXFTP.count > 0 { + xftpServersListView(summ.userPrevXFTP, summ.statsStartedAt, "Previously used") + } + + XFTPStatsView(stats: summ.userXFTPTotal.stats, statsStartedAt: summ.statsStartedAt) + + resetStatsButtonSection() } } } else { @@ -240,18 +257,6 @@ struct ServersSummaryView: View { } } - private func noCategoryInfoText() -> some View { - ZStack { - Rectangle() - .aspectRatio(contentMode: .fill) - .foregroundColor(Color.clear) - Text("No info") - .foregroundColor(.secondary) - } - .listRowBackground(Color.clear) - .listRowSeparator(.hidden) - } - private func resetStatsButtonSection() -> some View { Section { Button { @@ -383,13 +388,13 @@ struct SMPServerSummaryView: View { Section { Text(summary.smpServer) .textSelection(.enabled) - if let known = summary.known, !known { - Button { - // TODO - } label: { - Text("TODO Add as known") - } - } +// if let known = summary.known, !known { +// Button { +// // TODO +// } label: { +// Text("Add as known") +// } +// } } header: { Text("Server address") } footer: { @@ -403,15 +408,15 @@ struct SMPServerSummaryView: View { } if let subs = summary.subs { - subsSection(subs) + SMPSubsView(subs: subs) } if let sess = summary.sessions { - sessionsSection(sess) + ServerSessionsView(sess: sess) } if let stats = summary.stats { - statsSection(stats) + SMPStatsView(stats: stats, statsStartedAt: statsStartedAt) } } .alert(item: $alert) { $0.alert } @@ -448,28 +453,42 @@ struct SMPServerSummaryView: View { } } } +} - private func subsSection(_ subs: SMPServerSubs) -> some View { +struct SMPSubsView: View { + var subs: SMPServerSubs + + var body: some View { Section { infoRow("Active", "\(subs.ssActive)") infoRow("Pending", "\(subs.ssPending)") + infoRow("Total", "\(subs.ssActive + subs.ssPending)") } header: { HStack { - Text("Subscriptions") + Text("Message subscriptions") SubscriptionStatusView(activeSubs: subs.ssActive, pendingSubs: subs.ssPending) } } } +} - private func sessionsSection(_ sess: ServerSessions) -> some View { - Section("Sessions") { +struct ServerSessionsView: View { + var sess: ServerSessions + + var body: some View { + Section("Transport sessions") { infoRow("Connected", "\(sess.ssConnected)") infoRow("Errors", "\(sess.ssErrors)") infoRow("Connecting", "\(sess.ssConnecting)") } } +} - private func statsSection(_ stats: AgentSMPServerStatsData) -> some View { +struct SMPStatsView: View { + var stats: AgentSMPServerStatsData + var statsStartedAt: Date + + var body: some View { Section("Statistics") { infoRow("Starting from", localTimestamp(statsStartedAt)) infoRow("Messages sent directly", "\(stats._sentDirect)") @@ -488,10 +507,14 @@ struct SMPServerSummaryView: View { indentedInfoRow("other errors", "\(stats._recvErrs)") infoRow("Messages acknowledged", "\(stats._ackMsgs)") indentedInfoRow("attempts", "\(stats._ackAttempts)") + indentedInfoRow("NO_MSG errors", "\(stats._ackNoMsgErrs)") + indentedInfoRow("other errors", "\(stats._ackOtherErrs)") infoRow("Connections created", "\(stats._connCreated)") indentedInfoRow("secured", "\(stats._connSecured)") indentedInfoRow("completed", "\(stats._connCompleted)") infoRow("Connections deleted", "\(stats._connDeleted)") + indentedInfoRow("attempts", "\(stats._connDelAttempts)") + indentedInfoRow("errors", "\(stats._connDelErrs)") infoRow("Connections subscribed", "\(stats._connSubscribed)") indentedInfoRow("attempts", "\(stats._connSubAttempts)") indentedInfoRow("errors", "\(stats._connSubErrs)") @@ -518,13 +541,13 @@ struct XFTPServerSummaryView: View { Section { Text(summary.xftpServer) .textSelection(.enabled) - if let known = summary.known, !known { - Button { - // TODO - } label: { - Text("TODO Add as known") - } - } +// if let known = summary.known, !known { +// Button { +// // TODO +// } label: { +// Text("Add as known") +// } +// } } header: { Text("Server address") } footer: { @@ -534,25 +557,17 @@ struct XFTPServerSummaryView: View { } if let sess = summary.sessions { - sessionsSection(sess) + ServerSessionsView(sess: sess) } inProgressSection() if let stats = summary.stats { - statsSection(stats) + XFTPStatsView(stats: stats, statsStartedAt: statsStartedAt) } } } - private func sessionsSection(_ sess: ServerSessions) -> some View { - Section("Sessions") { - infoRow("Connected", "\(sess.ssConnected)") - infoRow("Errors", "\(sess.ssErrors)") - infoRow("Connecting", "\(sess.ssConnecting)") - } - } - private func inProgressSection() -> some View { Section("In progress") { localizedInfoRow("Download", boolYesNo(summary.rcvInProgress)) @@ -564,14 +579,24 @@ struct XFTPServerSummaryView: View { private func boolYesNo(_ b: Bool) -> LocalizedStringKey { b ? "yes" : "no" } +} - private func statsSection(_ stats: AgentXFTPServerStatsData) -> some View { +struct XFTPStatsView: View { + var stats: AgentXFTPServerStatsData + var statsStartedAt: Date + + var body: some View { + let kb: Int64 = 1024 + let prettyUploadsSize = ByteCountFormatter.string(fromByteCount: stats._uploadsSize * kb, countStyle: .binary) + let prettyDownloadsSize = ByteCountFormatter.string(fromByteCount: stats._downloadsSize * kb, countStyle: .binary) Section("Statistics") { infoRow("Starting from", localTimestamp(statsStartedAt)) infoRow("Chunks uploaded", "\(stats._uploads)") + indentedInfoRow("size", prettyUploadsSize) indentedInfoRow("attempts", "\(stats._uploadAttempts)") indentedInfoRow("errors", "\(stats._uploadErrs)") infoRow("Chunks downloaded", "\(stats._downloads)") + indentedInfoRow("size", prettyDownloadsSize) indentedInfoRow("attempts", "\(stats._downloadAttempts)") indentedInfoRow("AUTH errors", "\(stats._downloadAuthErrs)") indentedInfoRow("other errors", "\(stats._downloadErrs)") diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index 9dacedb31b..3087d1fcca 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -2255,29 +2255,41 @@ public struct PresentedServersSummary: Codable { public var currentUserServers: ServersSummary public var allUsersServers: ServersSummary + public var allSMPTotal: SMPTotals { self.allUsersServers.smpTotals } public var allUsedSMP: [SMPServerSummary] { self.allUsersServers.currentlyUsedSMPServers } public var allPrevSMP: [SMPServerSummary] { self.allUsersServers.previouslyUsedSMPServers } public var allProxSMP: [SMPServerSummary] { self.allUsersServers.onlyProxiedSMPServers } + public var userSMPTotal: SMPTotals { self.currentUserServers.smpTotals } public var userUsedSMP: [SMPServerSummary] { self.currentUserServers.currentlyUsedSMPServers } public var userPrevSMP: [SMPServerSummary] { self.currentUserServers.previouslyUsedSMPServers } public var userProxSMP: [SMPServerSummary] { self.currentUserServers.onlyProxiedSMPServers } + public var allXFTPTotal: XFTPTotals { self.allUsersServers.xftpTotals } public var allUsedXFTP: [XFTPServerSummary] { self.allUsersServers.currentlyUsedXFTPServers } public var allPrevXFTP: [XFTPServerSummary] { self.allUsersServers.previouslyUsedXFTPServers } + public var userXFTPTotal: XFTPTotals { self.currentUserServers.xftpTotals } public var userUsedXFTP: [XFTPServerSummary] { self.currentUserServers.currentlyUsedXFTPServers } public var userPrevXFTP: [XFTPServerSummary] { self.currentUserServers.previouslyUsedXFTPServers } } public struct ServersSummary: Codable { + public var smpTotals: SMPTotals public var currentlyUsedSMPServers: [SMPServerSummary] public var previouslyUsedSMPServers: [SMPServerSummary] public var onlyProxiedSMPServers: [SMPServerSummary] + public var xftpTotals: XFTPTotals public var currentlyUsedXFTPServers: [XFTPServerSummary] public var previouslyUsedXFTPServers: [XFTPServerSummary] } +public struct SMPTotals: Codable { + public var sessions: ServerSessions + public var subs: SMPServerSubs + public var stats: AgentSMPServerStatsData +} + public struct SMPServerSummary: Codable, Identifiable { public var smpServer: String public var known: Bool? @@ -2305,29 +2317,38 @@ public struct SMPServerSubs: Codable { } public struct AgentSMPServerStatsData: Codable { - public var _sentDirect: Int - public var _sentViaProxy: Int - public var _sentProxied: Int - public var _sentDirectAttempts: Int - public var _sentViaProxyAttempts: Int - public var _sentProxiedAttempts: Int - public var _sentAuthErrs: Int - public var _sentQuotaErrs: Int - public var _sentExpiredErrs: Int - public var _sentOtherErrs: Int - public var _recvMsgs: Int - public var _recvDuplicates: Int - public var _recvCryptoErrs: Int - public var _recvErrs: Int - public var _ackMsgs: Int - public var _ackAttempts: Int - public var _connCreated: Int - public var _connSecured: Int - public var _connCompleted: Int - public var _connDeleted: Int - public var _connSubscribed: Int - public var _connSubAttempts: Int - public var _connSubErrs: Int + public var _sentDirect: Int64 + public var _sentViaProxy: Int64 + public var _sentProxied: Int64 + public var _sentDirectAttempts: Int64 + public var _sentViaProxyAttempts: Int64 + public var _sentProxiedAttempts: Int64 + public var _sentAuthErrs: Int64 + public var _sentQuotaErrs: Int64 + public var _sentExpiredErrs: Int64 + public var _sentOtherErrs: Int64 + public var _recvMsgs: Int64 + public var _recvDuplicates: Int64 + public var _recvCryptoErrs: Int64 + public var _recvErrs: Int64 + public var _ackMsgs: Int64 + public var _ackAttempts: Int64 + public var _ackNoMsgErrs: Int64 + public var _ackOtherErrs: Int64 + public var _connCreated: Int64 + public var _connSecured: Int64 + public var _connCompleted: Int64 + public var _connDeleted: Int64 + public var _connDelAttempts: Int64 + public var _connDelErrs: Int64 + public var _connSubscribed: Int64 + public var _connSubAttempts: Int64 + public var _connSubErrs: Int64 +} + +public struct XFTPTotals: Codable { + public var sessions: ServerSessions + public var stats: AgentXFTPServerStatsData } public struct XFTPServerSummary: Codable, Identifiable { @@ -2343,14 +2364,16 @@ public struct XFTPServerSummary: Codable, Identifiable { } public struct AgentXFTPServerStatsData: Codable { - public var _uploads: Int - public var _uploadAttempts: Int - public var _uploadErrs: Int - public var _downloads: Int - public var _downloadAttempts: Int - public var _downloadAuthErrs: Int - public var _downloadErrs: Int - public var _deletions: Int - public var _deleteAttempts: Int - public var _deleteErrs: Int + public var _uploads: Int64 + public var _uploadsSize: Int64 + public var _uploadAttempts: Int64 + public var _uploadErrs: Int64 + public var _downloads: Int64 + public var _downloadsSize: Int64 + public var _downloadAttempts: Int64 + public var _downloadAuthErrs: Int64 + public var _downloadErrs: Int64 + public var _deletions: Int64 + public var _deleteAttempts: Int64 + public var _deleteErrs: Int64 }