From 8b770ca03e0f7b97663a7c76f3028cda9d400459 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Thu, 26 Jun 2025 12:10:06 +0100 Subject: [PATCH] ios: better error handling when connecting via links, improve alerts with chat information (#6012) * simplexmq * ios: error handling * better new chat alerts * vertical buttons in alert when they dont fit * allow messages in prepared groups --- apps/ios/Shared/Model/ChatModel.swift | 13 ++- apps/ios/Shared/Model/SimpleXAPI.swift | 34 +++--- .../Chat/ComposeMessage/ComposeView.swift | 83 +++++++------- .../ContextContactRequestActionsView.swift | 84 +++++++------- .../Views/Chat/Group/GroupChatInfoView.swift | 1 - .../ios/Shared/Views/Helpers/ShareSheet.swift | 106 ++++++++++++------ .../Shared/Views/NewChat/NewChatView.swift | 8 +- apps/ios/SimpleX.xcodeproj/project.pbxproj | 16 +-- apps/ios/SimpleXChat/APITypes.swift | 2 +- apps/ios/SimpleXChat/ChatTypes.swift | 71 ++++++------ .../chat/simplex/common/model/SimpleXAPI.kt | 4 +- cabal.project | 2 +- scripts/nix/sha256map.nix | 2 +- src/Simplex/Chat/Library/Internal.hs | 2 +- src/Simplex/Chat/View.hs | 2 +- 15 files changed, 245 insertions(+), 185 deletions(-) diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index 586421bde1..2c139f8fbc 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -581,15 +581,18 @@ final class ChatModel: ObservableObject { // groups[group.groupInfo.id] = group // } - func addChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) { + func addChatItem(_ chatInfo: ChatInfo, _ cItem: ChatItem) { // updates membersRequireAttention - updateChatInfo(cInfo) - // mark chat non deleted - if case let .direct(contact) = cInfo, contact.chatDeleted { + let cInfo: ChatInfo + if case let .direct(contact) = chatInfo, contact.chatDeleted { + // mark chat non deleted var updatedContact = contact updatedContact.chatDeleted = false - updateContact(updatedContact) + cInfo = .direct(contact: updatedContact) + } else { + cInfo = chatInfo } + updateChatInfo(cInfo) // update chat list if let i = getChatIndex(cInfo.id) { // update preview diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 10fc470fdf..3317d22595 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -899,8 +899,7 @@ func apiConnectPlan(connLink: String) async -> ((CreatedConnLink, ConnectionPlan } let r: APIResult = await chatApiSendCmd(.apiConnectPlan(userId: userId, connLink: connLink)) if case let .result(.connectionPlan(_, connLink, connPlan)) = r { return ((connLink, connPlan), nil) } - let alert = apiConnectResponseAlert(r.unexpected) ?? connectionErrorAlert(r) - return (nil, alert) + return (nil, apiConnectResponseAlert(r)) } func apiConnect(incognito: Bool, connLink: CreatedConnLink) async -> (ConnReqType, PendingContactConnection)? { @@ -933,12 +932,11 @@ func apiConnect_(incognito: Bool, connLink: CreatedConnLink) async -> ((ConnReqT return (nil, alert) default: () } - let alert = apiConnectResponseAlert(r.unexpected) ?? connectionErrorAlert(r) - return (nil, alert) + return (nil, apiConnectResponseAlert(r)) } -private func apiConnectResponseAlert(_ r: ChatError) -> Alert? { - switch r { +private func apiConnectResponseAlert(_ r: APIResult) -> Alert { + switch r.unexpected { case .error(.invalidConnReq): mkAlert( title: "Invalid connection link", @@ -974,12 +972,12 @@ private func apiConnectResponseAlert(_ r: ChatError) -> Alert? { if internalErr == "SEUniqueID" { mkAlert( title: "Already connected?", - message: "It seems like you are already connected via this link. If it is not the case, there was an error (\(responseError(r)))." + message: "It seems like you are already connected via this link. If it is not the case, there was an error (\(internalErr))." ) } else { - nil + connectionErrorAlert(r) } - default: nil + default: connectionErrorAlert(r) } } @@ -1027,16 +1025,18 @@ func apiChangePreparedGroupUser(groupId: Int64, newUserId: Int64) async throws - throw r.unexpected } -func apiConnectPreparedContact(contactId: Int64, incognito: Bool, msg: MsgContent?) async throws -> Contact { - let r: ChatResponse1 = try await chatSendCmd(.apiConnectPreparedContact(contactId: contactId, incognito: incognito, msg: msg)) - if case let .startedConnectionToContact(_, contact) = r { return contact } - throw r.unexpected +func apiConnectPreparedContact(contactId: Int64, incognito: Bool, msg: MsgContent?) async -> Contact? { + let r: APIResult = await chatApiSendCmd(.apiConnectPreparedContact(contactId: contactId, incognito: incognito, msg: msg)) + if case let .result(.startedConnectionToContact(_, contact)) = r { return contact } + AlertManager.shared.showAlert(apiConnectResponseAlert(r)) + return nil } -func apiConnectPreparedGroup(groupId: Int64, incognito: Bool, msg: MsgContent?) async throws -> GroupInfo { - let r: ChatResponse1 = try await chatSendCmd(.apiConnectPreparedGroup(groupId: groupId, incognito: incognito, msg: msg)) - if case let .startedConnectionToGroup(_, groupInfo) = r { return groupInfo } - throw r.unexpected +func apiConnectPreparedGroup(groupId: Int64, incognito: Bool, msg: MsgContent?) async -> GroupInfo? { + let r: APIResult = await chatApiSendCmd(.apiConnectPreparedGroup(groupId: groupId, incognito: incognito, msg: msg)) + if case let .result(.startedConnectionToGroup(_, groupInfo)) = r { return groupInfo } + AlertManager.shared.showAlert(apiConnectResponseAlert(r)) + return nil } func apiConnectContactViaAddress(incognito: Bool, contactId: Int64) async -> (Contact?, Alert?) { diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift index dfd1917196..d617c4f747 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift @@ -405,14 +405,15 @@ struct ComposeView: View { } if chat.chatInfo.groupInfo?.nextConnectPrepared == true { - Button(action: connectPreparedGroup) { - if chat.chatInfo.groupInfo?.businessChat == nil { + if chat.chatInfo.groupInfo?.businessChat == nil { + Button(action: connectPreparedGroup) { Label("Join group", systemImage: "person.2.fill") - } else { - Label("Connect", systemImage: "briefcase.fill") } + .frame(height: 60) + .disabled(composeState.inProgress) + } else { + sendContactRequestView(disableSendButton, icon: "briefcase.fill", sendRequest: connectPreparedGroup) } - .frame(height: 60) } else if contact?.nextSendGrpInv == true { contextSendMessageToConnect("Send direct message to connect") Divider() @@ -428,24 +429,9 @@ struct ComposeView: View { Label("Connect", systemImage: "person.fill.badge.plus") } .frame(height: 60) + .disabled(composeState.inProgress) case .con: - HStack (alignment: .center) { - sendMessageView( - disableSendButton, - placeholder: NSLocalizedString("Add message", comment: "placeholder for sending contact request"), - sendToConnect: sendConnectPreparedContactRequest - ) - if composeState.whitespaceOnly { - Button(action: sendConnectPreparedContactRequest) { - HStack { - Text("Connect").fontWeight(.medium) - Image(systemName: "person.fill.badge.plus") - } - } - .padding(.horizontal, 8) - } - } - .padding(.horizontal, 12) + sendContactRequestView(disableSendButton, icon: "person.fill.badge.plus", sendRequest: sendConnectPreparedContactRequest) } } else if contact?.nextAcceptContactRequest == true, let crId = contact?.contactRequestId { ContextContactRequestActionsView(contactRequestId: crId) @@ -620,6 +606,27 @@ struct ComposeView: View { } } + private func sendContactRequestView(_ disableSendButton: Bool, icon: String, sendRequest: @escaping () -> Void) -> some View { + HStack (alignment: .center) { + sendMessageView( + disableSendButton, + placeholder: NSLocalizedString("Add message", comment: "placeholder for sending contact request"), + sendToConnect: sendRequest + ) + if composeState.whitespaceOnly { + Button(action: sendRequest) { + HStack { + Text("Connect").fontWeight(.medium) + Image(systemName: icon) + } + } + .padding(.horizontal, 8) + .disabled(composeState.inProgress) + } + } + .padding(.horizontal, 12) + } + private func sendMessageView(_ disableSendButton: Bool, placeholder: String? = nil, sendToConnect: (() -> Void)? = nil) -> some View { ZStack(alignment: .leading) { SendMessageView( @@ -695,6 +702,7 @@ struct ComposeView: View { Task { do { if let mc = connectCheckLinkPreview() { + await sending() let contact = try await apiSendMemberContactInvitation(chat.chatInfo.apiId, mc) await MainActor.run { self.chatModel.updateContact(contact) @@ -704,6 +712,7 @@ struct ComposeView: View { AlertManager.shared.showAlertMsg(title: "Empty message!") } } catch { + await MainActor.run { composeState.inProgress = false } logger.error("ChatView.sendMemberContactInvitation error: \(error.localizedDescription)") AlertManager.shared.showAlertMsg(title: "Error sending member contact invitation", message: "Error: \(responseError(error))") } @@ -730,32 +739,30 @@ struct ComposeView: View { private func sendConnectPreparedContact() { Task { - do { - let mc = connectCheckLinkPreview() - let contact = try await apiConnectPreparedContact(contactId: chat.chatInfo.apiId, incognito: incognitoGroupDefault.get(), msg: mc) + await sending() + let mc = connectCheckLinkPreview() + if let contact = await apiConnectPreparedContact(contactId: chat.chatInfo.apiId, incognito: incognitoGroupDefault.get(), msg: mc) { await MainActor.run { self.chatModel.updateContact(contact) clearState() } - } catch { - logger.error("ChatView.sendConnectPreparedContact error: \(error.localizedDescription)") - AlertManager.shared.showAlertMsg(title: "Error connecting with contact", message: "Error: \(responseError(error))") + } else { + await MainActor.run { composeState.inProgress = false } } } } private func connectPreparedGroup() { Task { - do { - let mc = connectCheckLinkPreview() - let groupInfo = try await apiConnectPreparedGroup(groupId: chat.chatInfo.apiId, incognito: incognitoGroupDefault.get(), msg: mc) + await sending() + let mc = connectCheckLinkPreview() + if let groupInfo = await apiConnectPreparedGroup(groupId: chat.chatInfo.apiId, incognito: incognitoGroupDefault.get(), msg: mc) { await MainActor.run { self.chatModel.updateGroup(groupInfo) clearState() } - } catch { - logger.error("ChatView.connectPreparedGroup error: \(error.localizedDescription)") - AlertManager.shared.showAlertMsg(title: "Error joining group", message: "Error: \(responseError(error))") + } else { + await MainActor.run { composeState.inProgress = false } } } } @@ -1094,10 +1101,6 @@ struct ComposeView: View { } } - func sending() async { - await MainActor.run { composeState.inProgress = true } - } - func updateMessage(_ ei: ChatItem, live: Bool) async -> ChatItem? { if let oldMsgContent = ei.content.msgContent { do { @@ -1270,6 +1273,10 @@ struct ComposeView: View { } } + func sending() async { + await MainActor.run { composeState.inProgress = true } + } + private func startVoiceMessageRecording() async { startingRecording = true let fileName = generateNewFileName("voice", "m4a") diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ContextContactRequestActionsView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ContextContactRequestActionsView.swift index ccb43f0ed6..dc9fbff9f6 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ContextContactRequestActionsView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ContextContactRequestActionsView.swift @@ -13,60 +13,66 @@ struct ContextContactRequestActionsView: View { @EnvironmentObject var theme: AppTheme var contactRequestId: Int64 @UserDefault(DEFAULT_TOOLBAR_MATERIAL) private var toolbarMaterial = ToolbarMaterial.defaultMaterial + @State private var inProgress = false var body: some View { HStack(spacing: 0) { - Label("Reject", systemImage: "multiply") - .foregroundColor(.red) - .frame(maxWidth: .infinity) - .contentShape(Rectangle()) - .onTapGesture { - showRejectRequestAlert(contactRequestId) + Button(role: .destructive, action: showRejectRequestAlert) { + Label("Reject", systemImage: "multiply") } + .frame(maxWidth: .infinity, minHeight: 60) - Label("Accept", systemImage: "checkmark").foregroundColor(theme.colors.primary) - .frame(maxWidth: .infinity) - .contentShape(Rectangle()) - .onTapGesture { + Button { if ChatModel.shared.addressShortLinkDataSet { - Task { await acceptContactRequest(incognito: false, contactRequestId: contactRequestId) } + acceptRequest() } else { - showAcceptRequestAlert(contactRequestId) + showAcceptRequestAlert() } + } label: { + Label("Accept", systemImage: "checkmark") } + .frame(maxWidth: .infinity, minHeight: 60) } - .frame(minHeight: 60) + .disabled(inProgress) .frame(maxWidth: .infinity) .background(ToolbarMaterial.material(toolbarMaterial)) } -} -func showRejectRequestAlert(_ contactRequestId: Int64) { - showAlert( - NSLocalizedString("Reject contact request", comment: "alert title"), - message: NSLocalizedString("The sender will NOT be notified", comment: "alert message"), - actions: {[ - UIAlertAction(title: NSLocalizedString("Reject", comment: "alert action"), style: .destructive) { _ in - Task { await rejectContactRequest(contactRequestId, dismissToChatList: true) } - }, - cancelAlertAction - ]} - ) -} + private func showRejectRequestAlert() { + showAlert( + NSLocalizedString("Reject contact request", comment: "alert title"), + message: NSLocalizedString("The sender will NOT be notified", comment: "alert message"), + actions: {[ + UIAlertAction(title: NSLocalizedString("Reject", comment: "alert action"), style: .destructive) { _ in + Task { await rejectContactRequest(contactRequestId, dismissToChatList: true) } + }, + cancelAlertAction + ]} + ) + } -func showAcceptRequestAlert(_ contactRequestId: Int64) { - showAlert( - NSLocalizedString("Accept contact request", comment: "alert title"), - actions: {[ - UIAlertAction(title: NSLocalizedString("Accept", comment: "alert action"), style: .default) { _ in - Task { await acceptContactRequest(incognito: false, contactRequestId: contactRequestId) } - }, - UIAlertAction(title: NSLocalizedString("Accept incognito", comment: "alert action"), style: .default) { _ in - Task { await acceptContactRequest(incognito: true, contactRequestId: contactRequestId) } - }, - cancelAlertAction - ]} - ) + private func showAcceptRequestAlert() { + showAlert( + NSLocalizedString("Accept contact request", comment: "alert title"), + actions: {[ + UIAlertAction(title: NSLocalizedString("Accept", comment: "alert action"), style: .default) { _ in + acceptRequest() + }, + UIAlertAction(title: NSLocalizedString("Accept incognito", comment: "alert action"), style: .default) { _ in + acceptRequest(incognito: true) + }, + cancelAlertAction + ]} + ) + } + + private func acceptRequest(incognito: Bool = false) { + inProgress = true + Task { + await acceptContactRequest(incognito: false, contactRequestId: contactRequestId) + await MainActor.run { inProgress = false } + } + } } #Preview { diff --git a/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift b/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift index a1c511adc0..9f5b687b8a 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift @@ -887,7 +887,6 @@ struct GroupPreferencesButton: View { } } } - } diff --git a/apps/ios/Shared/Views/Helpers/ShareSheet.swift b/apps/ios/Shared/Views/Helpers/ShareSheet.swift index 48da43c87d..86a5dc7aaa 100644 --- a/apps/ios/Shared/Views/Helpers/ShareSheet.swift +++ b/apps/ios/Shared/Views/Helpers/ShareSheet.swift @@ -90,8 +90,15 @@ let okAlertAction = UIAlertAction(title: NSLocalizedString("Ok", comment: "alert let cancelAlertAction = UIAlertAction(title: NSLocalizedString("Cancel", comment: "alert button"), style: .cancel) +let alertProfileImageSize: CGFloat = 103 + +let alertWidth: CGFloat = 270 + +let alertButtonHeight: CGFloat = 44 + class OpenChatAlertViewController: UIViewController { private let profileName: String + private let profileFullName: String private let profileImage: UIView private let cancelTitle: String private let confirmTitle: String @@ -100,6 +107,7 @@ class OpenChatAlertViewController: UIViewController { init( profileName: String, + profileFullName: String, profileImage: UIView, cancelTitle: String = "Cancel", confirmTitle: String = "Open", @@ -107,6 +115,7 @@ class OpenChatAlertViewController: UIViewController { onConfirm: @escaping () -> Void ) { self.profileName = profileName + self.profileFullName = profileFullName self.profileImage = profileImage self.cancelTitle = cancelTitle self.confirmTitle = confirmTitle @@ -135,54 +144,72 @@ class OpenChatAlertViewController: UIViewController { // Profile image sizing profileImage.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ - profileImage.widthAnchor.constraint(equalToConstant: 60), - profileImage.heightAnchor.constraint(equalToConstant: 60) + profileImage.widthAnchor.constraint(equalToConstant: alertProfileImageSize), + profileImage.heightAnchor.constraint(equalToConstant: alertProfileImageSize) ]) // Name label let nameLabel = UILabel() nameLabel.text = profileName - nameLabel.font = UIFont.systemFont(ofSize: 18, weight: .semibold) + nameLabel.font = UIFont.preferredFont(forTextStyle: .headline) nameLabel.textColor = .label nameLabel.numberOfLines = 2 + nameLabel.textAlignment = .center nameLabel.translatesAutoresizingMaskIntoConstraints = false + var profileViews = [profileImage, nameLabel] + + // Full name label + if !profileFullName.isEmpty && profileFullName != profileName { + let fullNameLabel = UILabel() + fullNameLabel.text = profileFullName + fullNameLabel.font = UIFont.preferredFont(forTextStyle: .subheadline) + fullNameLabel.textColor = .label + fullNameLabel.numberOfLines = 2 + fullNameLabel.textAlignment = .center + fullNameLabel.translatesAutoresizingMaskIntoConstraints = false + profileViews.append(fullNameLabel) + } + // Horizontal stack for image + name - let hStack = UIStackView(arrangedSubviews: [profileImage, nameLabel]) - hStack.axis = .horizontal - hStack.spacing = 12 - hStack.alignment = .center - hStack.translatesAutoresizingMaskIntoConstraints = false + let stack = UIStackView(arrangedSubviews: profileViews) + stack.axis = .vertical + stack.spacing = 12 + stack.alignment = .center + stack.translatesAutoresizingMaskIntoConstraints = false let topRowContainer = UIView() topRowContainer.translatesAutoresizingMaskIntoConstraints = false - topRowContainer.addSubview(hStack) + topRowContainer.addSubview(stack) NSLayoutConstraint.activate([ - hStack.topAnchor.constraint(equalTo: topRowContainer.topAnchor), - hStack.bottomAnchor.constraint(equalTo: topRowContainer.bottomAnchor), - hStack.leadingAnchor.constraint(equalTo: topRowContainer.leadingAnchor, constant: 20), - hStack.trailingAnchor.constraint(equalTo: topRowContainer.trailingAnchor, constant: -20) + stack.topAnchor.constraint(equalTo: topRowContainer.topAnchor), + stack.bottomAnchor.constraint(equalTo: topRowContainer.bottomAnchor), + stack.leadingAnchor.constraint(equalTo: topRowContainer.leadingAnchor, constant: 20), + stack.trailingAnchor.constraint(equalTo: topRowContainer.trailingAnchor, constant: -20) ]) // Buttons let cancelButton = UIButton(type: .system) cancelButton.setTitle(cancelTitle, for: .normal) - cancelButton.titleLabel?.font = UIFont.systemFont(ofSize: 15) + let bodyDescr = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body) + cancelButton.titleLabel?.font = UIFont(descriptor: bodyDescr.withSymbolicTraits(.traitBold) ?? bodyDescr, size: 0) cancelButton.addTarget(self, action: #selector(cancelTapped), for: .touchUpInside) let confirmButton = UIButton(type: .system) confirmButton.setTitle(confirmTitle, for: .normal) - confirmButton.titleLabel?.font = UIFont.systemFont(ofSize: 15, weight: .semibold) + confirmButton.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body) confirmButton.addTarget(self, action: #selector(confirmTapped), for: .touchUpInside) + let verticalButtons = cancelButton.intrinsicContentSize.width + 20 >= alertWidth / 2 || confirmButton.intrinsicContentSize.width + 20 >= alertWidth / 2 + // Button stack with equal width buttons - let buttonStack = UIStackView(arrangedSubviews: [cancelButton, confirmButton]) - buttonStack.axis = .horizontal + let buttonStack = UIStackView(arrangedSubviews: verticalButtons ? [confirmButton, cancelButton] : [cancelButton, confirmButton]) + buttonStack.axis = verticalButtons ? .vertical : .horizontal buttonStack.distribution = .fillEqually buttonStack.spacing = 0 // no spacing, use divider instead buttonStack.translatesAutoresizingMaskIntoConstraints = false - buttonStack.heightAnchor.constraint(greaterThanOrEqualToConstant: 50).isActive = true + buttonStack.heightAnchor.constraint(greaterThanOrEqualToConstant: alertButtonHeight * (verticalButtons ? 2 : 1)).isActive = true // Vertical stack containing hStack and buttonStack let vStack = UIStackView(arrangedSubviews: [topRowContainer, buttonStack]) @@ -195,23 +222,38 @@ class OpenChatAlertViewController: UIViewController { // Add horizontal divider above buttons let horizontalDivider = UIView() - horizontalDivider.backgroundColor = UIColor(white: 0.85, alpha: 1) + horizontalDivider.backgroundColor = UIColor.separator horizontalDivider.translatesAutoresizingMaskIntoConstraints = false containerView.addSubview(horizontalDivider) - // Add vertical divider between buttons - let verticalDivider = UIView() - verticalDivider.backgroundColor = UIColor(white: 0.85, alpha: 1) - verticalDivider.translatesAutoresizingMaskIntoConstraints = false - buttonStack.addSubview(verticalDivider) + // Add divider between buttons + let buttonDivider = UIView() + buttonDivider.backgroundColor = UIColor.separator + buttonDivider.translatesAutoresizingMaskIntoConstraints = false + buttonStack.addSubview(buttonDivider) // Constraints + let buttonDividerConstraints = if verticalButtons { + [ + buttonDivider.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), + buttonDivider.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), + buttonDivider.centerYAnchor.constraint(equalTo: buttonStack.centerYAnchor), + buttonDivider.heightAnchor.constraint(equalToConstant: 1 / UIScreen.main.scale) + ] + } else { + [ + buttonDivider.topAnchor.constraint(equalTo: buttonStack.topAnchor), + buttonDivider.bottomAnchor.constraint(equalTo: containerView.bottomAnchor), + buttonDivider.centerXAnchor.constraint(equalTo: buttonStack.centerXAnchor), + buttonDivider.widthAnchor.constraint(equalToConstant: 1 / UIScreen.main.scale) + ] + } NSLayoutConstraint.activate([ // Container view centering and fixed width containerView.centerYAnchor.constraint(equalTo: view.centerYAnchor), containerView.centerXAnchor.constraint(equalTo: view.centerXAnchor), - containerView.widthAnchor.constraint(equalToConstant: 280), + containerView.widthAnchor.constraint(equalToConstant: alertWidth), // Vertical stack padding inside containerView vStack.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 20), @@ -220,20 +262,14 @@ class OpenChatAlertViewController: UIViewController { vStack.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: 0), // Center hStack horizontally inside vStack's padded width - hStack.centerXAnchor.constraint(equalTo: vStack.centerXAnchor), + stack.centerXAnchor.constraint(equalTo: vStack.centerXAnchor), // Horizontal divider above buttons horizontalDivider.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), horizontalDivider.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), horizontalDivider.bottomAnchor.constraint(equalTo: buttonStack.topAnchor), - horizontalDivider.heightAnchor.constraint(equalToConstant: 1 / UIScreen.main.scale), - - // Vertical divider between buttons - verticalDivider.widthAnchor.constraint(equalToConstant: 1 / UIScreen.main.scale), - verticalDivider.topAnchor.constraint(equalTo: buttonStack.topAnchor), - verticalDivider.bottomAnchor.constraint(equalTo: containerView.bottomAnchor), - verticalDivider.centerXAnchor.constraint(equalTo: buttonStack.centerXAnchor) - ]) + horizontalDivider.heightAnchor.constraint(equalToConstant: 1 / UIScreen.main.scale) + ] + buttonDividerConstraints) } @objc private func cancelTapped() { @@ -252,6 +288,7 @@ class OpenChatAlertViewController: UIViewController { func showOpenChatAlert( profileName: String, + profileFullName: String, profileImage: Content, theme: AppTheme, cancelTitle: String = "Cancel", @@ -267,6 +304,7 @@ func showOpenChatAlert( if let topVC = getTopViewController() { let alertVC = OpenChatAlertViewController( profileName: profileName, + profileFullName: profileFullName, profileImage: hostedView, cancelTitle: cancelTitle, confirmTitle: confirmTitle, diff --git a/apps/ios/Shared/Views/NewChat/NewChatView.swift b/apps/ios/Shared/Views/NewChat/NewChatView.swift index d0612f6bd8..cd0a6ec762 100644 --- a/apps/ios/Shared/Views/NewChat/NewChatView.swift +++ b/apps/ios/Shared/Views/NewChat/NewChatView.swift @@ -1014,11 +1014,12 @@ private func showPrepareContactAlert( ) { showOpenChatAlert( profileName: contactShortLinkData.profile.displayName, + profileFullName: contactShortLinkData.profile.fullName, profileImage: ProfileImage( imageStr: contactShortLinkData.profile.image, iconName: contactShortLinkData.business ? "briefcase.circle.fill" : "person.crop.circle.fill", - size: 60 + size: alertProfileImageSize ), theme: theme, cancelTitle: NSLocalizedString("Cancel", comment: "new chat action"), @@ -1054,10 +1055,11 @@ private func showPrepareGroupAlert( ) { showOpenChatAlert( profileName: groupShortLinkData.groupProfile.displayName, - profileImage: ProfileImage(imageStr: groupShortLinkData.groupProfile.image, iconName: "person.2.circle.fill", size: 60), + profileFullName: groupShortLinkData.groupProfile.fullName, + profileImage: ProfileImage(imageStr: groupShortLinkData.groupProfile.image, iconName: "person.2.circle.fill", size: alertProfileImageSize), theme: theme, cancelTitle: NSLocalizedString("Cancel", comment: "new chat action"), - confirmTitle: NSLocalizedString("Open chat", comment: "new chat action"), + confirmTitle: NSLocalizedString("Open group", comment: "new chat action"), onCancel: { cleanup?() }, onConfirm: { Task { diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index d84d449b47..c4cdab85ca 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -178,8 +178,8 @@ 64C3B0212A0D359700E19930 /* CustomTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */; }; 64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829982D54AEED006B9E89 /* libgmp.a */; }; 64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829992D54AEEE006B9E89 /* libffi.a */; }; - 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.4-1Rh0qJZPahP19NRjkSYFiJ-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.4-1Rh0qJZPahP19NRjkSYFiJ-ghc9.6.3.a */; }; - 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.4-1Rh0qJZPahP19NRjkSYFiJ.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.4-1Rh0qJZPahP19NRjkSYFiJ.a */; }; + 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.4-DWeDI2Xa9F86P3Q5uUF2Wy-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.4-DWeDI2Xa9F86P3Q5uUF2Wy-ghc9.6.3.a */; }; + 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.4-DWeDI2Xa9F86P3Q5uUF2Wy.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.4-DWeDI2Xa9F86P3Q5uUF2Wy.a */; }; 64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */; }; 64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */; }; 64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */; }; @@ -543,8 +543,8 @@ 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTimePicker.swift; sourceTree = ""; }; 64C829982D54AEED006B9E89 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; 64C829992D54AEEE006B9E89 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; - 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.4-1Rh0qJZPahP19NRjkSYFiJ-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.4-1Rh0qJZPahP19NRjkSYFiJ-ghc9.6.3.a"; sourceTree = ""; }; - 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.4-1Rh0qJZPahP19NRjkSYFiJ.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.4-1Rh0qJZPahP19NRjkSYFiJ.a"; sourceTree = ""; }; + 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.4-DWeDI2Xa9F86P3Q5uUF2Wy-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.4-DWeDI2Xa9F86P3Q5uUF2Wy-ghc9.6.3.a"; sourceTree = ""; }; + 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.4-DWeDI2Xa9F86P3Q5uUF2Wy.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.4-DWeDI2Xa9F86P3Q5uUF2Wy.a"; sourceTree = ""; }; 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressView.swift; sourceTree = ""; }; 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressLearnMore.swift; sourceTree = ""; }; @@ -704,8 +704,8 @@ 64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */, 64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */, 64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */, - 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.4-1Rh0qJZPahP19NRjkSYFiJ-ghc9.6.3.a in Frameworks */, - 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.4-1Rh0qJZPahP19NRjkSYFiJ.a in Frameworks */, + 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.4-DWeDI2Xa9F86P3Q5uUF2Wy-ghc9.6.3.a in Frameworks */, + 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.4-DWeDI2Xa9F86P3Q5uUF2Wy.a in Frameworks */, CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -790,8 +790,8 @@ 64C829992D54AEEE006B9E89 /* libffi.a */, 64C829982D54AEED006B9E89 /* libgmp.a */, 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */, - 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.4-1Rh0qJZPahP19NRjkSYFiJ-ghc9.6.3.a */, - 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.4-1Rh0qJZPahP19NRjkSYFiJ.a */, + 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.4-DWeDI2Xa9F86P3Q5uUF2Wy-ghc9.6.3.a */, + 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.4-DWeDI2Xa9F86P3Q5uUF2Wy.a */, ); path = Libraries; sourceTree = ""; diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index 3d36481e85..601497ed39 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -819,7 +819,7 @@ public enum SQLiteError: Decodable, Hashable { public enum AgentErrorType: Decodable, Hashable { case CMD(cmdErr: CommandErrorType, errContext: String) - case CONN(connErr: ConnectionErrorType) + case CONN(connErr: ConnectionErrorType, errContext: String) case SMP(serverAddress: String, smpErr: ProtocolErrorType) case NTF(ntfErr: ProtocolErrorType) case XFTP(xftpErr: XFTPErrorType) diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index ca8dd81bd0..71c72f4dc2 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -1372,6 +1372,8 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable { case .some(.memberSupport(groupMember_: .none)): return nil } + } else if groupInfo.nextConnectPrepared { + return nil } else { switch groupInfo.membership.memberStatus { case .memRejected: return ("request to join rejected", nil) @@ -1421,7 +1423,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable { default: false } } - + public var groupInfo: GroupInfo? { switch self { case let .group(groupInfo, _): return groupInfo @@ -1531,7 +1533,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable { public func ntfsEnabled(chatItem: ChatItem) -> Bool { ntfsEnabled(chatItem.meta.userMention) } - + public func ntfsEnabled(_ userMention: Bool) -> Bool { switch self.chatSettings?.enableNtfs { case .all: true @@ -1547,7 +1549,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable { default: return nil } } - + public var nextNtfMode: MsgFilter? { self.chatSettings?.enableNtfs.nextMode(mentions: hasMentions) } @@ -1596,7 +1598,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable { case .invalidJSON: return .now } } - + public func ttl(_ globalTTL: ChatItemTTL) -> ChatTTL { switch self { case let .direct(contact): @@ -1644,7 +1646,7 @@ public struct ChatData: Decodable, Identifiable, Hashable, ChatLike { self.chatItems = chatItems self.chatStats = chatStats } - + public static func invalidJSON(_ json: Data?) -> ChatData { ChatData( chatInfo: .invalidJSON(json: json), @@ -2085,15 +2087,14 @@ public struct GroupInfo: Identifiable, Decodable, NamedChat, Hashable { var createdAt: Date var updatedAt: Date var chatTs: Date? - public var connLinkToConnect: CreatedConnLink? - public var connLinkStartedConnection: Bool + public var preparedGroup: PreparedGroup? public var uiThemes: ThemeModeOverrides? public var membersRequireAttention: Int public var id: ChatId { get { "#\(groupId)" } } public var apiId: Int64 { get { groupId } } public var ready: Bool { get { true } } - public var nextConnectPrepared: Bool { connLinkToConnect != nil && !connLinkStartedConnection } + public var nextConnectPrepared: Bool { if let preparedGroup { !preparedGroup.connLinkStartedConnection } else { false } } public var displayName: String { localAlias == "" ? groupProfile.displayName : localAlias } public var fullName: String { get { groupProfile.fullName } } public var image: String? { get { groupProfile.image } } @@ -2134,13 +2135,17 @@ public struct GroupInfo: Identifiable, Decodable, NamedChat, Hashable { chatSettings: ChatSettings.defaults, createdAt: .now, updatedAt: .now, - connLinkStartedConnection: false, membersRequireAttention: 0, chatTags: [], localAlias: "" ) } +public struct PreparedGroup: Decodable, Hashable { + public var connLinkToConnect: CreatedConnLink? + public var connLinkStartedConnection: Bool +} + public struct GroupRef: Decodable, Hashable { public var groupId: Int64 var localDisplayName: GroupName @@ -2298,7 +2303,7 @@ public struct GroupMember: Identifiable, Decodable, Hashable { ? String.localizedStringWithFormat(NSLocalizedString("Past member %@", comment: "past/unknown group member"), name) : name } - + public var localAliasAndFullName: String { get { let p = memberProfile @@ -2378,7 +2383,7 @@ public struct GroupMember: Identifiable, Decodable, Hashable { return memberStatus != .memRemoved && memberStatus != .memLeft && memberRole < .admin && userRole >= .admin && userRole >= memberRole && groupInfo.membership.memberActive } - + public var canReceiveReports: Bool { memberRole >= .moderator && versionRange.maxVersion >= REPORTS_VERSION } @@ -2390,7 +2395,7 @@ public struct GroupMember: Identifiable, Decodable, Hashable { memberChatVRange } } - + public var memberIncognito: Bool { memberProfile.profileId != memberContactProfileId } @@ -2446,7 +2451,7 @@ public enum GroupMemberRole: String, Identifiable, CaseIterable, Comparable, Cod public var id: Self { self } public static var supportedRoles: [GroupMemberRole] = [.observer, .member, .admin, .owner] - + public var text: String { switch self { case .observer: return NSLocalizedString("observer", comment: "member role") @@ -2602,7 +2607,7 @@ public enum ConnectionEntity: Decodable, Hashable { nil } } - + // public var localDisplayName: String? { // switch self { // case let .rcvDirectMsgConnection(conn, contact): @@ -2643,7 +2648,7 @@ public struct NtfMsgInfo: Decodable, Hashable { public enum RcvNtfMsgInfo: Decodable { case info(ntfMsgInfo: NtfMsgInfo?) case error(ntfMsgError: AgentErrorType) - + @inline(__always) public var noMsg: Bool { if case let .info(msg) = self { msg == nil } else { true } @@ -2703,7 +2708,7 @@ public struct CIMentionMember: Decodable, Hashable { public struct CIMention: Decodable, Hashable { public var memberId: String public var memberRef: CIMentionMember? - + public init(groupMember m: GroupMember) { self.memberId = m.memberId self.memberRef = CIMentionMember( @@ -2954,7 +2959,7 @@ public struct ChatItem: Identifiable, Decodable, Hashable { default: return true } } - + public var isReport: Bool { switch content { case let .sndMsgContent(msgContent), let .rcvMsgContent(msgContent): @@ -3054,14 +3059,14 @@ public struct ChatItem: Identifiable, Decodable, Hashable { file: nil ) } - + public static func getReportSample(text: String, reason: ReportReason, item: ChatItem, sender: GroupMember? = nil) -> ChatItem { let chatDir = if let sender = sender { CIDirection.groupRcv(groupMember: sender) } else { CIDirection.groupSnd } - + return ChatItem( chatDir: chatDir, meta: CIMeta( @@ -3175,7 +3180,7 @@ public enum CIDirection: Decodable, Hashable { } } } - + public func sameDirection(_ dir: CIDirection) -> Bool { switch (self, dir) { case let (.groupRcv(m1), .groupRcv(m2)): m1.groupMemberId == m2.groupMemberId @@ -3311,7 +3316,7 @@ public enum CIStatus: Decodable, Hashable { case .invalid: return "invalid" } } - + public var sent: Bool { switch self { case .sndNew: true @@ -4124,7 +4129,7 @@ public enum FileError: Decodable, Equatable, Hashable { case let .other(fileError): String.localizedStringWithFormat(NSLocalizedString("Error: %@", comment: "file error text"), fileError) } } - + public var moreInfoButton: (label: LocalizedStringKey, link: URL)? { switch self { case .blocked: ("How it works", contentModerationPostLink) @@ -4426,7 +4431,7 @@ public enum ReportReason: Hashable { case profile case other case unknown(type: String) - + public static var supportedReasons: [ReportReason] = [.spam, .illegal, .community, .profile, .other] public var text: String { @@ -4439,7 +4444,7 @@ public enum ReportReason: Hashable { case let .unknown(type): return type } } - + public var attrString: NSAttributedString { let descr = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body) return NSAttributedString(string: text.isEmpty ? self.text : "\(self.text): ", attributes: [ @@ -4486,7 +4491,7 @@ public struct LinkPreview: Codable, Equatable, Hashable { self.description = description self.image = image } - + public var uri: URL public var title: String // TODO remove once optional in haskell @@ -4535,7 +4540,7 @@ public enum NtfTknStatus: String, Decodable, Hashable { case .expired: NSLocalizedString("Expired", comment: "token status text") } } - + public func info(register: Bool) -> String { switch self { case .new: return NSLocalizedString("Please wait for token to be registered.", comment: "token info") @@ -4913,9 +4918,9 @@ public enum ChatItemTTL: Identifiable, Comparable, Hashable { public enum ChatTTL: Identifiable, Hashable { case userDefault(ChatItemTTL) case chat(ChatItemTTL) - + public var id: Self { self } - + public var text: String { switch self { case let .chat(ttl): return ttl.deleteAfterText @@ -4924,21 +4929,21 @@ public enum ChatTTL: Identifiable, Hashable { ttl.deleteAfterText) } } - + public var neverExpires: Bool { switch self { case let .chat(ttl): return ttl.seconds == 0 case let .userDefault(ttl): return ttl.seconds == 0 } } - + public var value: Int64? { switch self { case let .chat(ttl): return ttl.seconds case .userDefault: return nil } } - + public var usingDefault: Bool { switch self { case .userDefault: return true @@ -4951,9 +4956,9 @@ public struct ChatTag: Decodable, Hashable { public var chatTagId: Int64 public var chatTagText: String public var chatTagEmoji: String? - + public var id: Int64 { chatTagId } - + public init(chatTagId: Int64, chatTagText: String, chatTagEmoji: String?) { self.chatTagId = chatTagId self.chatTagText = chatTagText diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt index 538393bb62..e511f30ff7 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt @@ -7055,7 +7055,7 @@ sealed class SQLiteError { sealed class AgentErrorType { val string: String get() = when (this) { is CMD -> "CMD ${cmdErr.string} $errContext" - is CONN -> "CONN ${connErr.string}" + is CONN -> "CONN ${connErr.string} $errContext" is SMP -> "SMP ${smpErr.string}" // is NTF -> "NTF ${ntfErr.string}" is XFTP -> "XFTP ${xftpErr.string}" @@ -7068,7 +7068,7 @@ sealed class AgentErrorType { is INACTIVE -> "INACTIVE" } @Serializable @SerialName("CMD") class CMD(val cmdErr: CommandErrorType, val errContext: String): AgentErrorType() - @Serializable @SerialName("CONN") class CONN(val connErr: ConnectionErrorType): AgentErrorType() + @Serializable @SerialName("CONN") class CONN(val connErr: ConnectionErrorType, val errContext: String): AgentErrorType() @Serializable @SerialName("SMP") class SMP(val serverAddress: String, val smpErr: SMPErrorType): AgentErrorType() // @Serializable @SerialName("NTF") class NTF(val ntfErr: SMPErrorType): AgentErrorType() @Serializable @SerialName("XFTP") class XFTP(val xftpErr: XFTPErrorType): AgentErrorType() diff --git a/cabal.project b/cabal.project index 318ce0f6c4..bb2102dfbf 100644 --- a/cabal.project +++ b/cabal.project @@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: 976bd3a389aaded78b4285541e6dddd6b2766149 + tag: b4bcfd325b43caefd9b649653c1b3ee6920bad61 source-repository-package type: git diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index 408e820e69..a7256204ba 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."976bd3a389aaded78b4285541e6dddd6b2766149" = "06mijsfnb9q9wa0lj49a24ajnw45qash7sc9ah95cd517bj6rnki"; + "https://github.com/simplex-chat/simplexmq.git"."b4bcfd325b43caefd9b649653c1b3ee6920bad61" = "1mg01aj2cafrkiz2pdjp39x8rdbqip4jyn5p1vwbi5rj0fsdifkm"; "https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38"; "https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d"; "https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl"; diff --git a/src/Simplex/Chat/Library/Internal.hs b/src/Simplex/Chat/Library/Internal.hs index e92a896a65..5cbf539972 100644 --- a/src/Simplex/Chat/Library/Internal.hs +++ b/src/Simplex/Chat/Library/Internal.hs @@ -670,7 +670,7 @@ receiveFileEvt' user ft userApprovedRelays rcvInline_ filePath_ = do rctFileCancelled :: ChatError -> Bool rctFileCancelled = \case ChatErrorAgent (SMP _ SMP.AUTH) _ -> True - ChatErrorAgent (CONN DUPLICATE) _ -> True + ChatErrorAgent (CONN DUPLICATE _) _ -> True _ -> False acceptFileReceive :: User -> RcvFileTransfer -> Bool -> Maybe Bool -> Maybe FilePath -> CM AChatItem diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 2a660345af..8602e4670c 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -2477,7 +2477,7 @@ viewChatError isCmd logLevel testView = \case BROKER _ TIMEOUT | not isCmd -> [] AGENT A_DUPLICATE -> [withConnEntity <> "error: AGENT A_DUPLICATE" | logLevel == CLLDebug || isCmd] AGENT (A_PROHIBITED e) -> [withConnEntity <> "error: AGENT A_PROHIBITED, " <> plain e | logLevel <= CLLWarning || isCmd] - CONN NOT_FOUND -> [withConnEntity <> "error: CONN NOT_FOUND" | logLevel <= CLLWarning || isCmd] + CONN NOT_FOUND _ -> [withConnEntity <> "error: CONN NOT_FOUND" | logLevel <= CLLWarning || isCmd] CRITICAL restart e -> [plain $ "critical error: " <> e] <> ["please restart the app" | restart] INTERNAL e -> [plain $ "internal error: " <> e] e -> [withConnEntity <> "smp agent error: " <> sShow e | logLevel <= CLLWarning || isCmd]