Paste link to connect (#551)
* initial implementation of textbox * paste to connect box implemented (and tested) in android * first pass at pastebox in iOS * clean up iOS implementation * put paste link page in for groups in android * initial inclusion in iOS UI * refactor naming * lint kotlin * fix typo * ios: update "connect via link" ui, refactor connecting via link to use the one function * android: update paste link UI * add russian translations * update translations Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com> * update translations Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
This commit is contained in:
co-authored by
JRoberts
Evgeny Poberezkin
parent
48ba6472b6
commit
e87660974e
@@ -496,42 +496,42 @@ func apiAddContact() throws -> String {
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiConnect(connReq: String) async throws -> Bool {
|
||||
func apiConnect(connReq: String) async throws -> ConnReqType? {
|
||||
let r = await chatSendCmd(.connect(connReq: connReq))
|
||||
let am = AlertManager.shared
|
||||
switch r {
|
||||
case .sentConfirmation: return true
|
||||
case .sentInvitation: return true
|
||||
case .sentConfirmation: return .invitation
|
||||
case .sentInvitation: return .contact
|
||||
case let .contactAlreadyExists(contact):
|
||||
am.showAlertMsg(
|
||||
title: "Contact already exists",
|
||||
message: "You are already connected to \(contact.displayName) via this link."
|
||||
)
|
||||
return false
|
||||
return nil
|
||||
case .chatCmdError(.error(.invalidConnReq)):
|
||||
am.showAlertMsg(
|
||||
title: "Invalid connection link",
|
||||
message: "Please check that you used the correct link or ask your contact to send you another one."
|
||||
)
|
||||
return false
|
||||
return nil
|
||||
case .chatCmdError(.errorAgent(.BROKER(.TIMEOUT))):
|
||||
am.showAlertMsg(
|
||||
title: "Connection timeout",
|
||||
message: "Please check your network connection and try again."
|
||||
)
|
||||
return false
|
||||
return nil
|
||||
case .chatCmdError(.errorAgent(.BROKER(.NETWORK))):
|
||||
am.showAlertMsg(
|
||||
title: "Connection error",
|
||||
message: "Please check your network connection and try again."
|
||||
)
|
||||
return false
|
||||
return nil
|
||||
case .chatCmdError(.errorAgent(.SMP(.AUTH))):
|
||||
am.showAlertMsg(
|
||||
title: "Connection error (AUTH)",
|
||||
message: "Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection."
|
||||
)
|
||||
return false
|
||||
return nil
|
||||
default: throw r
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,23 +78,12 @@ struct ChatListView: View {
|
||||
let link = url.absoluteString.replacingOccurrences(of: "///\(path)", with: "/\(path)")
|
||||
let title: LocalizedStringKey
|
||||
if case .contact = action { title = "Connect via contact link?" }
|
||||
else { title = "Connect via invitation link?" }
|
||||
else { title = "Connect via one-time link?" }
|
||||
return Alert(
|
||||
title: Text(title),
|
||||
message: Text("Your profile will be sent to the contact that you received this link from"),
|
||||
primaryButton: .default(Text("Connect")) {
|
||||
DispatchQueue.main.async {
|
||||
Task {
|
||||
do {
|
||||
let ok = try await apiConnect(connReq: link)
|
||||
if ok { connectionReqSentAlert(action) }
|
||||
} catch {
|
||||
let err = error.localizedDescription
|
||||
AlertManager.shared.showAlertMsg(title: "Connection error", message: "Error: \(err)")
|
||||
logger.debug("ChatListView.connectViaUrlAlert: apiConnect error: \(err)")
|
||||
}
|
||||
}
|
||||
}
|
||||
connectViaLink(link)
|
||||
},
|
||||
secondaryButton: .cancel()
|
||||
)
|
||||
|
||||
@@ -11,7 +11,6 @@ import CoreImage.CIFilterBuiltins
|
||||
|
||||
struct AddContactView: View {
|
||||
var connReqInvitation: String
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text("Add contact")
|
||||
|
||||
@@ -12,26 +12,27 @@ struct NewChatButton: View {
|
||||
@State private var showAddChat = false
|
||||
@State private var addContact = false
|
||||
@State private var connReqInvitation: String = ""
|
||||
@State private var connectContact = false
|
||||
@State private var createGroup = false
|
||||
@State private var scanToConnect = false
|
||||
@State private var pasteToConnect = false
|
||||
|
||||
var body: some View {
|
||||
Button { showAddChat = true } label: {
|
||||
Image(systemName: "person.crop.circle.badge.plus")
|
||||
}
|
||||
.confirmationDialog("Start new chat", isPresented: $showAddChat, titleVisibility: .visible) {
|
||||
Button("Add contact") { addContactAction() }
|
||||
Button("Scan QR code") { connectContact = true }
|
||||
Button("Create group") { createGroup = true }
|
||||
.disabled(true)
|
||||
.confirmationDialog("Add contact to start a new chat", isPresented: $showAddChat, titleVisibility: .visible) {
|
||||
Button("Create link / QR code") { addContactAction() }
|
||||
Button("Paste received link") { pasteToConnect = true }
|
||||
Button("Scan QR code") { scanToConnect = true }
|
||||
}
|
||||
.sheet(isPresented: $addContact, content: {
|
||||
AddContactView(connReqInvitation: connReqInvitation)
|
||||
})
|
||||
.sheet(isPresented: $connectContact, content: {
|
||||
connectContactSheet()
|
||||
.sheet(isPresented: $scanToConnect, content: {
|
||||
ScanToConnectView(openedSheet: $scanToConnect)
|
||||
})
|
||||
.sheet(isPresented: $pasteToConnect, content: {
|
||||
PasteToConnectView(openedSheet: $pasteToConnect)
|
||||
})
|
||||
.sheet(isPresented: $createGroup, content: { CreateGroupView() })
|
||||
}
|
||||
|
||||
func addContactAction() {
|
||||
@@ -45,28 +46,6 @@ struct NewChatButton: View {
|
||||
logger.error("NewChatButton.addContactAction apiAddContact error: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
func addContactSheet() -> some View {
|
||||
AddContactView(connReqInvitation: connReqInvitation)
|
||||
}
|
||||
|
||||
func connectContactSheet() -> some View {
|
||||
ConnectContactView(completed: { err in
|
||||
connectContact = false
|
||||
DispatchQueue.global().async {
|
||||
switch (err) {
|
||||
case let .success(ok):
|
||||
if ok { connectionReqSentAlert(.invitation) }
|
||||
case let .failure(error):
|
||||
connectionErrorAlert(error)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func connectionErrorAlert(_ error: Error) {
|
||||
AlertManager.shared.showAlertMsg(title: "Connection error", message: "Error: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
enum ConnReqType: Equatable {
|
||||
@@ -74,6 +53,30 @@ enum ConnReqType: Equatable {
|
||||
case invitation
|
||||
}
|
||||
|
||||
func connectViaLink(_ connectionLink: String, _ openedSheet: Binding<Bool>? = nil) {
|
||||
Task {
|
||||
do {
|
||||
let res = try await apiConnect(connReq: connectionLink)
|
||||
DispatchQueue.main.async {
|
||||
openedSheet?.wrappedValue = false
|
||||
if let connReqType = res {
|
||||
connectionReqSentAlert(connReqType)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
logger.error("connectViaLink apiConnect error: \(responseError(error))")
|
||||
DispatchQueue.main.async {
|
||||
openedSheet?.wrappedValue = false
|
||||
connectionErrorAlert(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func connectionErrorAlert(_ error: Error) {
|
||||
AlertManager.shared.showAlertMsg(title: "Connection error", message: "Error: \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
func connectionReqSentAlert(_ type: ConnReqType) {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title: "Connection request sent!",
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// PasteToConnectView.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by Ian Davies on 22/04/2022.
|
||||
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct PasteToConnectView: View {
|
||||
@Binding var openedSheet: Bool
|
||||
@State private var connectionLink: String = ""
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Text("Connect via link")
|
||||
.font(.title)
|
||||
.padding([.bottom])
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
Text("Paste the link you received into the box below to connect with your contact.")
|
||||
.multilineTextAlignment(.leading)
|
||||
Text("Your profile will be sent to the contact that you received this link from")
|
||||
.multilineTextAlignment(.leading)
|
||||
.padding(.bottom)
|
||||
TextEditor(text: $connectionLink)
|
||||
.onSubmit(connect)
|
||||
.font(.body)
|
||||
.textInputAutocapitalization(.never)
|
||||
.disableAutocorrection(true)
|
||||
.allowsTightening(false)
|
||||
.frame(height: 180)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.strokeBorder(.secondary, lineWidth: 0.3, antialiased: true)
|
||||
)
|
||||
|
||||
HStack(spacing: 20) {
|
||||
if connectionLink == "" {
|
||||
Button {
|
||||
connectionLink = UIPasteboard.general.string ?? ""
|
||||
} label: {
|
||||
Label("Paste", systemImage: "doc.on.clipboard")
|
||||
}
|
||||
} else {
|
||||
Button {
|
||||
connectionLink = ""
|
||||
} label: {
|
||||
Label("Clear", systemImage: "multiply")
|
||||
}
|
||||
|
||||
}
|
||||
Spacer()
|
||||
Button(action: connect, label: {
|
||||
Label("Connect", systemImage: "link")
|
||||
})
|
||||
.disabled(connectionLink == "" || connectionLink.trimmingCharacters(in: .whitespaces).firstIndex(of: " ") != nil)
|
||||
}
|
||||
.frame(height: 48)
|
||||
.padding(.bottom)
|
||||
|
||||
Text("You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button")
|
||||
.multilineTextAlignment(.leading)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
|
||||
private func connect() {
|
||||
connectViaLink(connectionLink.trimmingCharacters(in: .whitespaces), $openedSheet)
|
||||
}
|
||||
}
|
||||
|
||||
struct PasteToConnectView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
@State var openedSheet: Bool = true
|
||||
return PasteToConnectView(openedSheet: $openedSheet)
|
||||
}
|
||||
}
|
||||
+6
-13
@@ -9,8 +9,8 @@
|
||||
import SwiftUI
|
||||
import CodeScanner
|
||||
|
||||
struct ConnectContactView: View {
|
||||
var completed: ((Result<Bool, Error>) -> Void)
|
||||
struct ScanToConnectView: View {
|
||||
@Binding var openedSheet: Bool
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
@@ -37,24 +37,17 @@ struct ConnectContactView: View {
|
||||
func processQRCode(_ resp: Result<ScanResult, ScanError>) {
|
||||
switch resp {
|
||||
case let .success(r):
|
||||
Task {
|
||||
do {
|
||||
let ok = try await apiConnect(connReq: r.string)
|
||||
completed(.success(ok))
|
||||
} catch {
|
||||
logger.error("ConnectContactView.processQRCode apiConnect error: \(error.localizedDescription)")
|
||||
completed(.failure(error))
|
||||
}
|
||||
}
|
||||
Task { connectViaLink(r.string, $openedSheet) }
|
||||
case let .failure(e):
|
||||
logger.error("ConnectContactView.processQRCode QR code error: \(e.localizedDescription)")
|
||||
completed(.failure(e))
|
||||
openedSheet = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ConnectContactView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
return ConnectContactView(completed: {_ in })
|
||||
@State var openedSheet: Bool = true
|
||||
return ScanToConnectView(openedSheet: $openedSheet)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user