From fd11c9d0b3128b2de2f047e6374c7d248032959c Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Wed, 26 Jun 2024 10:58:26 +0400 Subject: [PATCH] reconnect server button --- apps/ios/Shared/Model/SimpleXAPI.swift | 5 + .../Views/ChatList/ServersSummaryView.swift | 190 +++++++++++------- apps/ios/SimpleXChat/APITypes.swift | 3 + 3 files changed, 128 insertions(+), 70 deletions(-) diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index bc1f2b5859..b59e055ff3 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -541,6 +541,11 @@ func reconnectAllServers() async throws { try await sendCommandOkResp(.reconnectAllServers) } +func reconnectServer(smpServer: String) async throws { + let userId = try currentUserId("reconnectServer") + try await sendCommandOkResp(.reconnectServer(userId: userId, smpServer: smpServer)) +} + func apiSetChatSettings(type: ChatType, id: Int64, chatSettings: ChatSettings) async throws { try await sendCommandOkResp(.apiSetChatSettings(type: type, id: id, chatSettings: chatSettings)) } diff --git a/apps/ios/Shared/Views/ChatList/ServersSummaryView.swift b/apps/ios/Shared/Views/ChatList/ServersSummaryView.swift index e100220fb5..6680a2310a 100644 --- a/apps/ios/Shared/Views/ChatList/ServersSummaryView.swift +++ b/apps/ios/Shared/Views/ChatList/ServersSummaryView.swift @@ -329,6 +329,7 @@ struct SMPServerSummaryView: View { var summary: SMPServerSummary var showReconnectButton: Bool var statsStartedAt: Date + @State private var alert: SomeAlert? var body: some View { List { @@ -346,63 +347,101 @@ struct SMPServerSummaryView: View { Text("Server address") } footer: { if let known = summary.known, known { - // TODO open settings? - Text("Server is configured in **Settings** → **Network & servers** → **SMP servers**.") + Text("Server is configured in **Settings** → **Network & servers**.") } } if showReconnectButton { - Section { - Button { - // TODO - } label: { - Text("TODO Reconnect") - } - } - } - - if let sess = summary.sessions { - Section("Sessions") { - infoRow("Connected", "\(sess.ssConnected)") - infoRow("Errors", "\(sess.ssErrors)") - infoRow("Connecting", "\(sess.ssConnecting)") - } + reconnectButtonSection() } if let subs = summary.subs { - Section("Subscriptions") { - infoRow("Active", "\(subs.ssActive)") - infoRow("Pending", "\(subs.ssPending)") - } + subsSection(subs) + } + + if let sess = summary.sessions { + sessionsSection(sess) } if let stats = summary.stats { - Section("Statistics") { - infoRow("Messages sent directly", "\(stats._sentDirect)") - infoRow(" attempts", "\(stats._sentDirectAttempts)") - infoRow("Messages sent via proxy", "\(stats._sentViaProxy)") - infoRow(" attempts", "\(stats._sentViaProxyAttempts)") - infoRow("Messages sent to proxy", "\(stats._sentProxied)") - infoRow(" attempts", "\(stats._sentProxiedAttempts)") - infoRow("Send AUTH errors", "\(stats._sentAuthErrs)") - infoRow(" QUOTA errors", "\(stats._sentQuotaErrs)") - infoRow(" expired", "\(stats._sentExpiredErrs)") - infoRow(" other errors", "\(stats._sentOtherErrs)") - infoRow("Messages received", "\(stats._recvMsgs)") - infoRow(" duplicates", "\(stats._recvDuplicates)") - infoRow(" decryption", "\(stats._recvCryptoErrs)") - infoRow(" other errors", "\(stats._recvErrs)") - infoRow("Connections created", "\(stats._connCreated)") - infoRow(" secured", "\(stats._connSecured)") - infoRow(" completed", "\(stats._connCompleted)") - infoRow("Connections deleted", "\(stats._connDeleted)") - infoRow("Connections subscribed", "\(stats._connSubscribed)") - infoRow(" attempts", "\(stats._connSubAttempts)") - infoRow(" errors", "\(stats._connSubErrs)") - infoRow("From", localTimestamp(statsStartedAt)) - } + statsSection(stats) } } + .alert(item: $alert) { $0.alert } + } + + 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" + ) + } + } + }, + secondaryButton: .cancel() + ), + id: "reconnect server question" + ) + } label: { + Text("Reconnect") + } + } + } + + private func subsSection(_ subs: SMPServerSubs) -> some View { + Section("Subscriptions") { + infoRow("Active", "\(subs.ssActive)") + infoRow("Pending", "\(subs.ssPending)") + } + } + + private func sessionsSection(_ sess: ServerSessions) -> some View { + Section("Sessions") { + infoRow("Connected", "\(sess.ssConnected)") + infoRow("Errors", "\(sess.ssErrors)") + infoRow("Connecting", "\(sess.ssConnecting)") + } + } + + private func statsSection(_ stats: AgentSMPServerStatsData) -> some View { + Section("Statistics") { + infoRow("Starting from", localTimestamp(statsStartedAt)) + infoRow("Messages sent directly", "\(stats._sentDirect)") + infoRow(" attempts", "\(stats._sentDirectAttempts)") + infoRow("Messages sent via proxy", "\(stats._sentViaProxy)") + infoRow(" attempts", "\(stats._sentViaProxyAttempts)") + infoRow("Messages sent to proxy", "\(stats._sentProxied)") + infoRow(" attempts", "\(stats._sentProxiedAttempts)") + infoRow("Send AUTH errors", "\(stats._sentAuthErrs)") + infoRow(" QUOTA errors", "\(stats._sentQuotaErrs)") + infoRow(" expired", "\(stats._sentExpiredErrs)") + infoRow(" other errors", "\(stats._sentOtherErrs)") + infoRow("Messages received", "\(stats._recvMsgs)") + infoRow(" duplicates", "\(stats._recvDuplicates)") + infoRow(" decryption", "\(stats._recvCryptoErrs)") + infoRow(" other errors", "\(stats._recvErrs)") + infoRow("Connections created", "\(stats._connCreated)") + infoRow(" secured", "\(stats._connSecured)") + infoRow(" completed", "\(stats._connCompleted)") + infoRow("Connections deleted", "\(stats._connDeleted)") + infoRow("Connections subscribed", "\(stats._connSubscribed)") + infoRow(" attempts", "\(stats._connSubAttempts)") + infoRow(" errors", "\(stats._connSubErrs)") + } } } @@ -426,46 +465,57 @@ struct XFTPServerSummaryView: View { Text("Server address") } footer: { if let known = summary.known, known { - // TODO open settings? - Text("Server can be configured in **Settings** → **Network & servers** → **XFTP servers**") + Text("Server is configured in **Settings** → **Network & servers**.") } } if let sess = summary.sessions { - Section("Sessions") { - infoRow("Connected", "\(sess.ssConnected)") - infoRow("Errors", "\(sess.ssErrors)") - infoRow("Connecting", "\(sess.ssConnecting)") - } + sessionsSection(sess) } - Section("In progress") { - localizedInfoRow("Download", boolYesNo(summary.rcvInProgress)) - localizedInfoRow("Upload", boolYesNo(summary.sndInProgress)) - localizedInfoRow("Deletion", boolYesNo(summary.delInProgress)) - } + inProgressSection() if let stats = summary.stats { - Section("Statistics") { - infoRow("Chunks uploaded", "\(stats._uploads)") - infoRow(" attempts", "\(stats._uploadAttempts)") - infoRow(" errors", "\(stats._uploadErrs)") - infoRow("Chunks downloaded", "\(stats._downloads)") - infoRow(" attempts", "\(stats._downloadAttempts)") - infoRow(" AUTH errors", "\(stats._downloadAuthErrs)") - infoRow(" other errors", "\(stats._downloadErrs)") - infoRow("Chunks deleted", "\(stats._deletions)") - infoRow(" attempts", "\(stats._deleteAttempts)") - infoRow(" errors", "\(stats._deleteErrs)") - infoRow("From", localTimestamp(statsStartedAt)) - } + statsSection(stats) } } } + 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)) + localizedInfoRow("Upload", boolYesNo(summary.sndInProgress)) + localizedInfoRow("Deletion", boolYesNo(summary.delInProgress)) + } + } + private func boolYesNo(_ b: Bool) -> LocalizedStringKey { b ? "yes" : "no" } + + private func statsSection(_ stats: AgentXFTPServerStatsData) -> some View { + Section("Statistics") { + infoRow("Starting from", localTimestamp(statsStartedAt)) + infoRow("Chunks uploaded", "\(stats._uploads)") + infoRow(" attempts", "\(stats._uploadAttempts)") + infoRow(" errors", "\(stats._uploadErrs)") + infoRow("Chunks downloaded", "\(stats._downloads)") + infoRow(" attempts", "\(stats._downloadAttempts)") + infoRow(" AUTH errors", "\(stats._downloadAuthErrs)") + infoRow(" other errors", "\(stats._downloadErrs)") + infoRow("Chunks deleted", "\(stats._deletions)") + infoRow(" attempts", "\(stats._deleteAttempts)") + infoRow(" errors", "\(stats._deleteErrs)") + } + } } #Preview { diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index ec7ec60801..939b5f0528 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -78,6 +78,7 @@ public enum ChatCommand { case apiGetNetworkConfig case apiSetNetworkInfo(networkInfo: UserNetworkInfo) case reconnectAllServers + case reconnectServer(userId: Int64, smpServer: String) case apiSetChatSettings(type: ChatType, id: Int64, chatSettings: ChatSettings) case apiSetMemberSettings(groupId: Int64, groupMemberId: Int64, memberSettings: GroupMemberSettings) case apiContactInfo(contactId: Int64) @@ -228,6 +229,7 @@ public enum ChatCommand { case .apiGetNetworkConfig: return "/network" case let .apiSetNetworkInfo(networkInfo): return "/_network info \(encodeJSON(networkInfo))" case .reconnectAllServers: return "/reconnect" + case let .reconnectServer(userId, smpServer): return "/reconnect \(userId) \(smpServer)" case let .apiSetChatSettings(type, id, chatSettings): return "/_settings \(ref(type, id)) \(encodeJSON(chatSettings))" case let .apiSetMemberSettings(groupId, groupMemberId, memberSettings): return "/_member settings #\(groupId) \(groupMemberId) \(encodeJSON(memberSettings))" case let .apiContactInfo(contactId): return "/_info @\(contactId)" @@ -378,6 +380,7 @@ public enum ChatCommand { case .apiGetNetworkConfig: return "apiGetNetworkConfig" case .apiSetNetworkInfo: return "apiSetNetworkInfo" case .reconnectAllServers: return "reconnectAllServers" + case .reconnectServer: return "reconnectServer" case .apiSetChatSettings: return "apiSetChatSettings" case .apiSetMemberSettings: return "apiSetMemberSettings" case .apiContactInfo: return "apiContactInfo"