mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-25 02:05:40 +00:00
ios wip
This commit is contained in:
@@ -1053,7 +1053,7 @@ enum ChatEvent: Decodable, ChatAPIResult {
|
||||
case receivedContactRequest(user: UserRef, contactRequest: UserContactRequest, chat_: ChatData?)
|
||||
case contactUpdated(user: UserRef, toContact: Contact)
|
||||
case groupMemberUpdated(user: UserRef, groupInfo: GroupInfo, fromMember: GroupMember, toMember: GroupMember)
|
||||
case networkStatus(networkStatus: NetworkStatus, connections: [String])
|
||||
case subscriptionStatus(subscriptionStatus: SubscriptionStatus, connections: [String])
|
||||
case chatInfoUpdated(user: UserRef, chatInfo: ChatInfo)
|
||||
case newChatItems(user: UserRef, chatItems: [AChatItem])
|
||||
case chatItemsStatusesUpdated(user: UserRef, chatItems: [AChatItem])
|
||||
@@ -1130,7 +1130,7 @@ enum ChatEvent: Decodable, ChatAPIResult {
|
||||
case .receivedContactRequest: "receivedContactRequest"
|
||||
case .contactUpdated: "contactUpdated"
|
||||
case .groupMemberUpdated: "groupMemberUpdated"
|
||||
case .networkStatus: "networkStatus"
|
||||
case .subscriptionStatus: "subscriptionStatus"
|
||||
case .chatInfoUpdated: "chatInfoUpdated"
|
||||
case .newChatItems: "newChatItems"
|
||||
case .chatItemsStatusesUpdated: "chatItemsStatusesUpdated"
|
||||
@@ -1202,7 +1202,7 @@ enum ChatEvent: Decodable, ChatAPIResult {
|
||||
case let .receivedContactRequest(u, contactRequest, chat_): return withUser(u, "contactRequest: \(String(describing: contactRequest))\nchat_: \(String(describing: chat_))")
|
||||
case let .contactUpdated(u, toContact): return withUser(u, String(describing: toContact))
|
||||
case let .groupMemberUpdated(u, groupInfo, fromMember, toMember): return withUser(u, "groupInfo: \(groupInfo)\nfromMember: \(fromMember)\ntoMember: \(toMember)")
|
||||
case let .networkStatus(status, conns): return "networkStatus: \(String(describing: status))\nconnections: \(String(describing: conns))"
|
||||
case let .subscriptionStatus(status, conns): return "subscriptionStatus: \(String(describing: status))\nconnections: \(String(describing: conns))"
|
||||
case let .chatInfoUpdated(u, chatInfo): return withUser(u, String(describing: chatInfo))
|
||||
case let .newChatItems(u, chatItems):
|
||||
let itemsString = chatItems.map { chatItem in String(describing: chatItem) }.joined(separator: "\n")
|
||||
@@ -1362,38 +1362,6 @@ enum ChatDeleteMode: Codable {
|
||||
}
|
||||
}
|
||||
|
||||
enum NetworkStatus: Decodable, Equatable {
|
||||
case unknown
|
||||
case connected
|
||||
case disconnected
|
||||
case error(connectionError: String)
|
||||
|
||||
var statusString: LocalizedStringKey {
|
||||
switch self {
|
||||
case .connected: "connected"
|
||||
case .error: "error"
|
||||
default: "connecting"
|
||||
}
|
||||
}
|
||||
|
||||
var statusExplanation: LocalizedStringKey {
|
||||
switch self {
|
||||
case .connected: "You are connected to the server used to receive messages from this contact."
|
||||
case let .error(err): "Trying to connect to the server used to receive messages from this contact (error: \(err))."
|
||||
default: "Trying to connect to the server used to receive messages from this contact."
|
||||
}
|
||||
}
|
||||
|
||||
var imageName: String {
|
||||
switch self {
|
||||
case .unknown: "circle.dotted"
|
||||
case .connected: "circle.fill"
|
||||
case .disconnected: "ellipsis.circle.fill"
|
||||
case .error: "exclamationmark.circle.fill"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ForwardConfirmation: Decodable, Hashable {
|
||||
case filesNotAccepted(fileIds: [Int64])
|
||||
case filesInProgress(filesCount: Int)
|
||||
|
||||
@@ -351,6 +351,8 @@ final class ChatModel: ObservableObject {
|
||||
@Published var deletedChats: Set<String> = []
|
||||
// current chat
|
||||
@Published var chatId: String?
|
||||
@Published var chatAgentConnId: String?
|
||||
@Published var chatSubStatus: SubscriptionStatus?
|
||||
@Published var openAroundItemId: ChatItem.ID? = nil
|
||||
@Published var chatToTop: String?
|
||||
@Published var groupMembers: [GMember] = []
|
||||
|
||||
@@ -2322,9 +2322,12 @@ func processReceivedMsg(_ res: ChatEvent) async {
|
||||
_ = m.upsertGroupMember(groupInfo, toMember)
|
||||
}
|
||||
}
|
||||
case let .networkStatus(status, connections):
|
||||
// TODO [sub status] update status for current chat in model if id matches
|
||||
return
|
||||
case let .subscriptionStatus(status, connections):
|
||||
if let chatAgentConnId = m.chatAgentConnId, connections.contains(chatAgentConnId) {
|
||||
await MainActor.run {
|
||||
m.chatSubStatus = status
|
||||
}
|
||||
}
|
||||
case let .chatInfoUpdated(user, chatInfo):
|
||||
if active(user) {
|
||||
await MainActor.run {
|
||||
|
||||
@@ -114,7 +114,7 @@ struct ChatInfoView: View {
|
||||
|
||||
enum ChatInfoViewAlert: Identifiable {
|
||||
case clearChatAlert
|
||||
case networkStatusAlert
|
||||
case subStatusAlert(status: SubscriptionStatus)
|
||||
case switchAddressAlert
|
||||
case abortSwitchAddressAlert
|
||||
case syncConnectionForceAlert
|
||||
@@ -125,7 +125,7 @@ struct ChatInfoView: View {
|
||||
var id: String {
|
||||
switch self {
|
||||
case .clearChatAlert: return "clearChatAlert"
|
||||
case .networkStatusAlert: return "networkStatusAlert"
|
||||
case let .subStatusAlert(status): return "subStatusAlert \(status)"
|
||||
case .switchAddressAlert: return "switchAddressAlert"
|
||||
case .abortSwitchAddressAlert: return "abortSwitchAddressAlert"
|
||||
case .syncConnectionForceAlert: return "syncConnectionForceAlert"
|
||||
@@ -235,10 +235,12 @@ struct ChatInfoView: View {
|
||||
|
||||
if contact.ready && contact.active {
|
||||
Section(header: Text("Servers").foregroundColor(theme.colors.secondary)) {
|
||||
networkStatusRow()
|
||||
.onTapGesture {
|
||||
alert = .networkStatusAlert
|
||||
}
|
||||
if let chatSubStatus = chatModel.chatSubStatus {
|
||||
subStatusRow(chatSubStatus)
|
||||
.onTapGesture {
|
||||
alert = .subStatusAlert(status: chatSubStatus)
|
||||
}
|
||||
}
|
||||
if let connStats = connectionStats {
|
||||
Button("Change receiving address") {
|
||||
alert = .switchAddressAlert
|
||||
@@ -324,7 +326,7 @@ struct ChatInfoView: View {
|
||||
.alert(item: $alert) { alertItem in
|
||||
switch(alertItem) {
|
||||
case .clearChatAlert: return clearChatAlert()
|
||||
case .networkStatusAlert: return networkStatusAlert()
|
||||
case let .subStatusAlert(status): return subStatusAlert(status)
|
||||
case .switchAddressAlert: return switchAddressAlert(switchContactAddress)
|
||||
case .abortSwitchAddressAlert: return abortSwitchAddressAlert(abortSwitchContactAddress)
|
||||
case .syncConnectionForceAlert:
|
||||
@@ -545,26 +547,22 @@ struct ChatInfoView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func networkStatusRow() -> some View {
|
||||
private func subStatusRow(_ status: SubscriptionStatus) -> some View {
|
||||
HStack {
|
||||
Text("Network status")
|
||||
Image(systemName: "info.circle")
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.font(.system(size: 14))
|
||||
Spacer()
|
||||
// TODO [sub status] use status from model
|
||||
// Text(networkModel.contactNetworkStatus(contact).statusString)
|
||||
Text(NetworkStatus.connected.statusString)
|
||||
Text(status.statusString)
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
serverImage()
|
||||
serverImage(status)
|
||||
}
|
||||
}
|
||||
|
||||
private func serverImage() -> some View {
|
||||
// TODO [sub status] use status from model
|
||||
let status = NetworkStatus.connected // networkModel.contactNetworkStatus(contact)
|
||||
private func serverImage(_ status: SubscriptionStatus) -> some View {
|
||||
return Image(systemName: status.imageName)
|
||||
.foregroundColor(status == .connected ? .green : theme.colors.secondary)
|
||||
.foregroundColor(status == .active ? .green : theme.colors.secondary)
|
||||
.font(.system(size: 12))
|
||||
}
|
||||
|
||||
@@ -607,11 +605,10 @@ struct ChatInfoView: View {
|
||||
)
|
||||
}
|
||||
|
||||
private func networkStatusAlert() -> Alert {
|
||||
private func subStatusAlert(_ status: SubscriptionStatus) -> Alert {
|
||||
Alert(
|
||||
title: Text("Network status"),
|
||||
// TODO [sub status] use status from model
|
||||
message: Text("") // Text(networkModel.contactNetworkStatus(contact).statusExplanation)
|
||||
message: Text(status.statusExplanation)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -393,6 +393,8 @@ struct ChatView: View {
|
||||
if chatModel.chatId == cInfo.id && !presentationMode.wrappedValue.isPresented {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
|
||||
if chatModel.chatId == nil {
|
||||
chatModel.chatAgentConnId = nil
|
||||
chatModel.chatSubStatus = nil
|
||||
im.reversedChatItems = []
|
||||
im.chatState.clear()
|
||||
chatModel.groupMembers = []
|
||||
@@ -657,18 +659,30 @@ struct ChatView: View {
|
||||
private func initChatView() {
|
||||
let cInfo = chat.chatInfo
|
||||
// This check prevents the call to apiContactInfo after the app is suspended, and the database is closed.
|
||||
if case .active = scenePhase,
|
||||
case let .direct(contact) = cInfo {
|
||||
Task {
|
||||
do {
|
||||
let (stats, _) = try await apiContactInfo(chat.chatInfo.apiId)
|
||||
await MainActor.run {
|
||||
if let s = stats {
|
||||
chatModel.updateContactConnectionStats(contact, s)
|
||||
if case .active = scenePhase {
|
||||
if case let .direct(contact) = cInfo {
|
||||
Task {
|
||||
do {
|
||||
let (stats, _) = try await apiContactInfo(chat.chatInfo.apiId)
|
||||
await MainActor.run {
|
||||
if let s = stats {
|
||||
chatModel.updateContactConnectionStats(contact, s)
|
||||
if let contactConn = contact.activeConn {
|
||||
chatModel.chatAgentConnId = contactConn.agentConnId
|
||||
chatModel.chatSubStatus = s.subStatus
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch let error {
|
||||
logger.error("apiContactInfo error: \(responseError(error))")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Task {
|
||||
await MainActor.run {
|
||||
chatModel.chatAgentConnId = nil
|
||||
chatModel.chatSubStatus = nil
|
||||
}
|
||||
} catch let error {
|
||||
logger.error("apiContactInfo error: \(responseError(error))")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,13 +539,13 @@ public enum MsgFilter: String, Codable, Hashable {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO [sub status] add SubscriptionStatus, QueueStatus
|
||||
public struct ConnectionStats: Decodable, Hashable {
|
||||
public var connAgentVersion: Int
|
||||
public var rcvQueuesInfo: [RcvQueueInfo]
|
||||
public var sndQueuesInfo: [SndQueueInfo]
|
||||
public var ratchetSyncState: RatchetSyncState
|
||||
public var ratchetSyncSupported: Bool
|
||||
public var subStatus: SubscriptionStatus?
|
||||
|
||||
public var ratchetSyncAllowed: Bool {
|
||||
ratchetSyncSupported && [.allowed, .required].contains(ratchetSyncState)
|
||||
@@ -560,25 +560,28 @@ public struct ConnectionStats: Decodable, Hashable {
|
||||
}
|
||||
}
|
||||
|
||||
public struct RcvQueueInfo: Codable, Hashable {
|
||||
public struct RcvQueueInfo: Decodable, Hashable {
|
||||
public var rcvServer: String
|
||||
public var status: QueueStatus
|
||||
public var rcvSwitchStatus: RcvSwitchStatus?
|
||||
public var canAbortSwitch: Bool
|
||||
public var subStatus: SubscriptionStatus
|
||||
}
|
||||
|
||||
public enum RcvSwitchStatus: String, Codable, Hashable {
|
||||
public enum RcvSwitchStatus: String, Decodable, Hashable {
|
||||
case switchStarted = "switch_started"
|
||||
case sendingQADD = "sending_qadd"
|
||||
case sendingQUSE = "sending_quse"
|
||||
case receivedMessage = "received_message"
|
||||
}
|
||||
|
||||
public struct SndQueueInfo: Codable, Hashable {
|
||||
public struct SndQueueInfo: Decodable, Hashable {
|
||||
public var sndServer: String
|
||||
public var status: QueueStatus
|
||||
public var sndSwitchStatus: SndSwitchStatus?
|
||||
}
|
||||
|
||||
public enum SndSwitchStatus: String, Codable, Hashable {
|
||||
public enum SndSwitchStatus: String, Decodable, Hashable {
|
||||
case sendingQKEY = "sending_qkey"
|
||||
case sendingQTEST = "sending_qtest"
|
||||
}
|
||||
@@ -607,6 +610,48 @@ public enum RatchetSyncState: String, Decodable {
|
||||
case agreed
|
||||
}
|
||||
|
||||
public enum QueueStatus: String, Decodable, Hashable {
|
||||
case new
|
||||
case confirmed
|
||||
case secured
|
||||
case active
|
||||
case disabled
|
||||
}
|
||||
|
||||
public enum SubscriptionStatus: Decodable, Hashable {
|
||||
case active
|
||||
case pending
|
||||
case removed(subError: String)
|
||||
case noSub
|
||||
|
||||
public var statusString: LocalizedStringKey {
|
||||
switch self {
|
||||
case .active: "connected"
|
||||
case .pending: "connecting"
|
||||
case .removed: "error"
|
||||
case .noSub: "no subscription"
|
||||
}
|
||||
}
|
||||
|
||||
public var statusExplanation: LocalizedStringKey {
|
||||
switch self {
|
||||
case .active: "You are connected to the server used to receive messages from this contact."
|
||||
case .pending: "Trying to connect to the server used to receive messages from this contact."
|
||||
case let .removed(err): "Error connecting to the server used to receive messages from this contact: \(err)."
|
||||
case .noSub: "You are not connected to the server used to receive messages from this contact (no subscription)."
|
||||
}
|
||||
}
|
||||
|
||||
public var imageName: String {
|
||||
switch self {
|
||||
case .active: "circle.fill"
|
||||
case .pending: "ellipsis.circle.fill"
|
||||
case .removed: "exclamationmark.circle.fill"
|
||||
case .noSub: "circle.dotted"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public protocol SelectableItem: Identifiable, Equatable {
|
||||
var label: LocalizedStringKey { get }
|
||||
static var values: [Self] { get }
|
||||
|
||||
Reference in New Issue
Block a user