mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-09-09 16:06:09 +00:00
An invitation being accepted is a fact about the invitation, not about the view that shows it, but each view kept its own flag. The chat list row, the long-press menu, the accept alert, the composer banner, the chat item "tap to join" and the notification action therefore disagreed: accepting from one left the others fully enabled, and the second accept reached the core with an invitation the first one had already used. ChatModel now records which contact requests are being accepted and which groups are being joined. The shared acceptContactRequest and the new joinGroup maintain it, so every caller participates without changing its call site, and every view derives from it instead of owning a flag. The record is taken before the request is sent, so a second tap in the same view is blocked as well. The connect composer no longer renders in a group's support scope: on desktop that is a second view of the same group next to the main one, and it offered its own Join button.
92 lines
3.1 KiB
Swift
92 lines
3.1 KiB
Swift
//
|
|
// ContextContactRequestActionsView.swift
|
|
// SimpleX (iOS)
|
|
//
|
|
// Created by spaced4ndy on 02.05.2025.
|
|
// Copyright © 2025 SimpleX Chat. All rights reserved.
|
|
//
|
|
|
|
import SwiftUI
|
|
import SimpleXChat
|
|
|
|
struct ContextContactRequestActionsView: View {
|
|
@EnvironmentObject var chatModel: ChatModel
|
|
@EnvironmentObject var theme: AppTheme
|
|
var contactRequestId: Int64
|
|
@UserDefault(DEFAULT_TOOLBAR_MATERIAL) private var toolbarMaterial = ToolbarMaterial.defaultMaterial
|
|
@State private var progressByTimeout = false
|
|
|
|
private var inProgress: Bool { chatModel.acceptingContactRequests.contains(contactRequestId) }
|
|
|
|
var body: some View {
|
|
HStack(spacing: 0) {
|
|
Button(role: .destructive, action: showRejectRequestAlert) {
|
|
Label("Reject", systemImage: "multiply")
|
|
}
|
|
.frame(maxWidth: .infinity, minHeight: 60)
|
|
|
|
Button {
|
|
if ChatModel.shared.addressShortLinkDataSet {
|
|
acceptRequest()
|
|
} else {
|
|
showAcceptRequestAlert()
|
|
}
|
|
} label: {
|
|
Label("Accept", systemImage: "checkmark")
|
|
}
|
|
.frame(maxWidth: .infinity, minHeight: 60)
|
|
}
|
|
.disabled(inProgress)
|
|
.frame(maxWidth: .infinity)
|
|
.background(ToolbarMaterial.material(toolbarMaterial))
|
|
.opacity(progressByTimeout ? 0.4 : 1)
|
|
.overlay {
|
|
if progressByTimeout {
|
|
ProgressView()
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
}
|
|
}
|
|
.progressByTimeout(inProgress, $progressByTimeout)
|
|
}
|
|
|
|
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
|
|
]}
|
|
)
|
|
}
|
|
|
|
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) {
|
|
Task {
|
|
await acceptContactRequest(incognito: incognito, contactRequestId: contactRequestId)
|
|
}
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
ContextContactRequestActionsView(
|
|
contactRequestId: 1
|
|
)
|
|
}
|