ios: connection plan improvements; remove browser mode for simplex links (#3237)

This commit is contained in:
spaced4ndy 2023-10-17 12:56:12 +04:00 committed by GitHub
parent 99c458406f
commit d8d47d706d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 88 additions and 39 deletions

View File

@ -292,10 +292,7 @@ struct ContentView: View {
var path = url.path var path = url.path
if (path == "/contact" || path == "/invitation") { if (path == "/contact" || path == "/invitation") {
path.removeFirst() path.removeFirst()
// TODO normalize in backend; revert let link = url.absoluteString.replacingOccurrences(of: "///\(path)", with: "/\(path)")
// let link = url.absoluteString.replacingOccurrences(of: "///\(path)", with: "/\(path)")
var link = url.absoluteString.replacingOccurrences(of: "///\(path)", with: "/\(path)")
link = link.starts(with: "simplex:/") ? link.replacingOccurrences(of: "simplex:/", with: "https://simplex.chat/") : link
planAndConnect( planAndConnect(
link, link,
showAlert: showPlanAndConnectAlert, showAlert: showPlanAndConnectAlert,

View File

@ -168,9 +168,9 @@ struct ChatInfoView: View {
if let contactLink = contact.contactLink { if let contactLink = contact.contactLink {
Section { Section {
QRCode(uri: contactLink) SimpleXLinkQRCode(uri: contactLink)
Button { Button {
showShareSheet(items: [contactLink]) showShareSheet(items: [simplexChatLink(contactLink)])
} label: { } label: {
Label("Share address", systemImage: "square.and.arrow.up") Label("Share address", systemImage: "square.and.arrow.up")
} }

View File

@ -121,13 +121,11 @@ private func formatText(_ ft: FormattedText, _ preview: Bool) -> Text {
case .secret: return Text(t).foregroundColor(.clear).underline(color: .primary) case .secret: return Text(t).foregroundColor(.clear).underline(color: .primary)
case let .colored(color): return Text(t).foregroundColor(color.uiColor) case let .colored(color): return Text(t).foregroundColor(color.uiColor)
case .uri: return linkText(t, t, preview, prefix: "") case .uri: return linkText(t, t, preview, prefix: "")
case let .simplexLink(linkType, simplexUri, trustedUri, smpHosts): case let .simplexLink(linkType, simplexUri, smpHosts):
switch privacySimplexLinkModeDefault.get() { switch privacySimplexLinkModeDefault.get() {
case .description: return linkText(simplexLinkText(linkType, smpHosts), simplexUri, preview, prefix: "") case .description: return linkText(simplexLinkText(linkType, smpHosts), simplexUri, preview, prefix: "")
case .full: return linkText(t, simplexUri, preview, prefix: "") case .full: return linkText(t, simplexUri, preview, prefix: "")
case .browser: return trustedUri case .browser: return linkText(t, simplexUri, preview, prefix: "")
? linkText(t, t, preview, prefix: "")
: linkText(t, t, preview, prefix: "", color: .red, uiColor: .red)
} }
case .email: return linkText(t, t, preview, prefix: "mailto:") case .email: return linkText(t, t, preview, prefix: "mailto:")
case .phone: return linkText(t, t.replacingOccurrences(of: " ", with: ""), preview, prefix: "tel:") case .phone: return linkText(t, t.replacingOccurrences(of: " ", with: ""), preview, prefix: "tel:")

View File

@ -41,9 +41,9 @@ struct GroupLinkView: View {
} }
} }
.frame(height: 36) .frame(height: 36)
QRCode(uri: groupLink) SimpleXLinkQRCode(uri: groupLink)
Button { Button {
showShareSheet(items: [groupLink]) showShareSheet(items: [simplexChatLink(groupLink)])
} label: { } label: {
Label("Share link", systemImage: "square.and.arrow.up") Label("Share link", systemImage: "square.and.arrow.up")
} }

View File

@ -94,9 +94,9 @@ struct GroupMemberInfoView: View {
if let contactLink = member.contactLink { if let contactLink = member.contactLink {
Section { Section {
QRCode(uri: contactLink) SimpleXLinkQRCode(uri: contactLink)
Button { Button {
showShareSheet(items: [contactLink]) showShareSheet(items: [simplexChatLink(contactLink)])
} label: { } label: {
Label("Share address", systemImage: "square.and.arrow.up") Label("Share address", systemImage: "square.and.arrow.up")
} }

View File

@ -61,7 +61,7 @@ struct ContactConnectionInfo: View {
if contactConnection.initiated, if contactConnection.initiated,
let connReqInv = contactConnection.connReqInv { let connReqInv = contactConnection.connReqInv {
QRCode(uri: connReqInv) SimpleXLinkQRCode(uri: simplexChatLink(connReqInv))
incognitoEnabled() incognitoEnabled()
shareLinkButton(connReqInv) shareLinkButton(connReqInv)
oneTimeLinkLearnMoreButton() oneTimeLinkLearnMoreButton()

View File

@ -21,7 +21,7 @@ struct AddContactView: View {
List { List {
Section { Section {
if connReqInvitation != "" { if connReqInvitation != "" {
QRCode(uri: connReqInvitation) SimpleXLinkQRCode(uri: connReqInvitation)
} else { } else {
ProgressView() ProgressView()
.progressViewStyle(.circular) .progressViewStyle(.circular)
@ -99,7 +99,7 @@ func sharedProfileInfo(_ incognito: Bool) -> Text {
func shareLinkButton(_ connReqInvitation: String) -> some View { func shareLinkButton(_ connReqInvitation: String) -> some View {
Button { Button {
showShareSheet(items: [connReqInvitation]) showShareSheet(items: [simplexChatLink(connReqInvitation)])
} label: { } label: {
settingsRow("square.and.arrow.up") { settingsRow("square.and.arrow.up") {
Text("Share 1-time link") Text("Share 1-time link")

View File

@ -62,7 +62,9 @@ enum PlanAndConnectAlert: Identifiable {
case ownInvitationLinkConfirmConnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool) case ownInvitationLinkConfirmConnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool)
case invitationLinkConnecting(connectionLink: String) case invitationLinkConnecting(connectionLink: String)
case ownContactAddressConfirmConnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool) case ownContactAddressConfirmConnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool)
case contactAddressConnectingConfirmReconnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool)
case groupLinkConfirmConnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool) case groupLinkConfirmConnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool)
case groupLinkConnectingConfirmReconnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool)
case groupLinkConnecting(connectionLink: String, groupInfo: GroupInfo?) case groupLinkConnecting(connectionLink: String, groupInfo: GroupInfo?)
var id: String { var id: String {
@ -70,7 +72,9 @@ enum PlanAndConnectAlert: Identifiable {
case let .ownInvitationLinkConfirmConnect(connectionLink, _, _): return "ownInvitationLinkConfirmConnect \(connectionLink)" case let .ownInvitationLinkConfirmConnect(connectionLink, _, _): return "ownInvitationLinkConfirmConnect \(connectionLink)"
case let .invitationLinkConnecting(connectionLink): return "invitationLinkConnecting \(connectionLink)" case let .invitationLinkConnecting(connectionLink): return "invitationLinkConnecting \(connectionLink)"
case let .ownContactAddressConfirmConnect(connectionLink, _, _): return "ownContactAddressConfirmConnect \(connectionLink)" case let .ownContactAddressConfirmConnect(connectionLink, _, _): return "ownContactAddressConfirmConnect \(connectionLink)"
case let .contactAddressConnectingConfirmReconnect(connectionLink, _, _): return "contactAddressConnectingConfirmReconnect \(connectionLink)"
case let .groupLinkConfirmConnect(connectionLink, _, _): return "groupLinkConfirmConnect \(connectionLink)" case let .groupLinkConfirmConnect(connectionLink, _, _): return "groupLinkConfirmConnect \(connectionLink)"
case let .groupLinkConnectingConfirmReconnect(connectionLink, _, _): return "groupLinkConnectingConfirmReconnect \(connectionLink)"
case let .groupLinkConnecting(connectionLink, _): return "groupLinkConnecting \(connectionLink)" case let .groupLinkConnecting(connectionLink, _): return "groupLinkConnecting \(connectionLink)"
} }
} }
@ -103,6 +107,16 @@ func planAndConnectAlert(_ alert: PlanAndConnectAlert, dismiss: Bool) -> Alert {
), ),
secondaryButton: .cancel() secondaryButton: .cancel()
) )
case let .contactAddressConnectingConfirmReconnect(connectionLink, connectionPlan, incognito):
return Alert(
title: Text("Repeat connection request?"),
message: Text("You have already requested connection via this address!"),
primaryButton: .destructive(
Text(incognito ? "Connect incognito" : "Connect"),
action: { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito) }
),
secondaryButton: .cancel()
)
case let .groupLinkConfirmConnect(connectionLink, connectionPlan, incognito): case let .groupLinkConfirmConnect(connectionLink, connectionPlan, incognito):
return Alert( return Alert(
title: Text("Join group?"), title: Text("Join group?"),
@ -113,6 +127,16 @@ func planAndConnectAlert(_ alert: PlanAndConnectAlert, dismiss: Bool) -> Alert {
), ),
secondaryButton: .cancel() secondaryButton: .cancel()
) )
case let .groupLinkConnectingConfirmReconnect(connectionLink, connectionPlan, incognito):
return Alert(
title: Text("Repeat join request?"),
message: Text("You are already joining the group via this link!"),
primaryButton: .destructive(
Text(incognito ? "Join incognito" : "Join"),
action: { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito) }
),
secondaryButton: .cancel()
)
case let .groupLinkConnecting(_, groupInfo): case let .groupLinkConnecting(_, groupInfo):
if let groupInfo = groupInfo { if let groupInfo = groupInfo {
return Alert( return Alert(
@ -130,13 +154,13 @@ func planAndConnectAlert(_ alert: PlanAndConnectAlert, dismiss: Bool) -> Alert {
enum PlanAndConnectActionSheet: Identifiable { enum PlanAndConnectActionSheet: Identifiable {
case askCurrentOrIncognitoProfile(connectionLink: String, connectionPlan: ConnectionPlan?, title: LocalizedStringKey) case askCurrentOrIncognitoProfile(connectionLink: String, connectionPlan: ConnectionPlan?, title: LocalizedStringKey)
case ownLinkAskCurrentOrIncognitoProfile(connectionLink: String, connectionPlan: ConnectionPlan, title: LocalizedStringKey) case askCurrentOrIncognitoProfileDestructive(connectionLink: String, connectionPlan: ConnectionPlan, title: LocalizedStringKey)
case ownGroupLinkConfirmConnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool?, groupInfo: GroupInfo) case ownGroupLinkConfirmConnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool?, groupInfo: GroupInfo)
var id: String { var id: String {
switch self { switch self {
case let .askCurrentOrIncognitoProfile(connectionLink, _, _): return "askCurrentOrIncognitoProfile \(connectionLink)" case let .askCurrentOrIncognitoProfile(connectionLink, _, _): return "askCurrentOrIncognitoProfile \(connectionLink)"
case let .ownLinkAskCurrentOrIncognitoProfile(connectionLink, _, _): return "ownLinkAskCurrentOrIncognitoProfile \(connectionLink)" case let .askCurrentOrIncognitoProfileDestructive(connectionLink, _, _): return "askCurrentOrIncognitoProfileDestructive \(connectionLink)"
case let .ownGroupLinkConfirmConnect(connectionLink, _, _, _): return "ownGroupLinkConfirmConnect \(connectionLink)" case let .ownGroupLinkConfirmConnect(connectionLink, _, _, _): return "ownGroupLinkConfirmConnect \(connectionLink)"
} }
} }
@ -153,7 +177,7 @@ func planAndConnectActionSheet(_ sheet: PlanAndConnectActionSheet, dismiss: Bool
.cancel() .cancel()
] ]
) )
case let .ownLinkAskCurrentOrIncognitoProfile(connectionLink, connectionPlan, title): case let .askCurrentOrIncognitoProfileDestructive(connectionLink, connectionPlan, title):
return ActionSheet( return ActionSheet(
title: Text(title), title: Text(title),
buttons: [ buttons: [
@ -211,7 +235,7 @@ func planAndConnect(
if let incognito = incognito { if let incognito = incognito {
showAlert(.ownInvitationLinkConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito)) showAlert(.ownInvitationLinkConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito))
} else { } else {
showActionSheet(.ownLinkAskCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect to yourself?\nThis is your own one-time link!")) showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect to yourself?\nThis is your own one-time link!"))
} }
case let .connecting(contact_): case let .connecting(contact_):
logger.debug("planAndConnect, .invitationLink, .connecting, incognito=\(incognito?.description ?? "nil")") logger.debug("planAndConnect, .invitationLink, .connecting, incognito=\(incognito?.description ?? "nil")")
@ -238,10 +262,17 @@ func planAndConnect(
if let incognito = incognito { if let incognito = incognito {
showAlert(.ownContactAddressConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito)) showAlert(.ownContactAddressConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito))
} else { } else {
showActionSheet(.ownLinkAskCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect to yourself?\nThis is your own SimpleX address!")) showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect to yourself?\nThis is your own SimpleX address!"))
} }
case let .connecting(contact): case .connectingConfirmReconnect:
logger.debug("planAndConnect, .contactAddress, .connecting, incognito=\(incognito?.description ?? "nil")") logger.debug("planAndConnect, .contactAddress, .connectingConfirmReconnect, incognito=\(incognito?.description ?? "nil")")
if let incognito = incognito {
showAlert(.contactAddressConnectingConfirmReconnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito))
} else {
showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "You have already requested connection!\nRepeat connection request?"))
}
case let .connectingProhibit(contact):
logger.debug("planAndConnect, .contactAddress, .connectingProhibit, incognito=\(incognito?.description ?? "nil")")
openKnownContact(contact, dismiss: dismiss) { AlertManager.shared.showAlert(contactAlreadyConnectingAlert(contact)) } openKnownContact(contact, dismiss: dismiss) { AlertManager.shared.showAlert(contactAlreadyConnectingAlert(contact)) }
case let .known(contact): case let .known(contact):
logger.debug("planAndConnect, .contactAddress, .known, incognito=\(incognito?.description ?? "nil")") logger.debug("planAndConnect, .contactAddress, .known, incognito=\(incognito?.description ?? "nil")")
@ -258,8 +289,15 @@ func planAndConnect(
case let .ownLink(groupInfo): case let .ownLink(groupInfo):
logger.debug("planAndConnect, .groupLink, .ownLink, incognito=\(incognito?.description ?? "nil")") logger.debug("planAndConnect, .groupLink, .ownLink, incognito=\(incognito?.description ?? "nil")")
showActionSheet(.ownGroupLinkConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito, groupInfo: groupInfo)) showActionSheet(.ownGroupLinkConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito, groupInfo: groupInfo))
case let .connecting(groupInfo_): case .connectingConfirmReconnect:
logger.debug("planAndConnect, .groupLink, .connecting, incognito=\(incognito?.description ?? "nil")") logger.debug("planAndConnect, .groupLink, .connectingConfirmReconnect, incognito=\(incognito?.description ?? "nil")")
if let incognito = incognito {
showAlert(.groupLinkConnectingConfirmReconnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito))
} else {
showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "You are already joining the group!\nRepeat join request?"))
}
case let .connectingProhibit(groupInfo_):
logger.debug("planAndConnect, .groupLink, .connectingProhibit, incognito=\(incognito?.description ?? "nil")")
showAlert(.groupLinkConnecting(connectionLink: connectionLink, groupInfo: groupInfo_)) showAlert(.groupLinkConnecting(connectionLink: connectionLink, groupInfo: groupInfo_))
case let .known(groupInfo): case let .known(groupInfo):
logger.debug("planAndConnect, .groupLink, .known, incognito=\(incognito?.description ?? "nil")") logger.debug("planAndConnect, .groupLink, .known, incognito=\(incognito?.description ?? "nil")")

View File

@ -28,6 +28,22 @@ struct MutableQRCode: View {
} }
} }
struct SimpleXLinkQRCode: View {
let uri: String
var withLogo: Bool = true
var tintColor = UIColor(red: 0.023, green: 0.176, blue: 0.337, alpha: 1)
var body: some View {
QRCode(uri: simplexChatLink(uri), withLogo: withLogo, tintColor: tintColor)
}
}
func simplexChatLink(_ uri: String) -> String {
uri.starts(with: "simplex:/")
? uri.replacingOccurrences(of: "simplex:/", with: "https://simplex.chat/")
: uri
}
struct QRCode: View { struct QRCode: View {
let uri: String let uri: String
var withLogo: Bool = true var withLogo: Bool = true

View File

@ -31,7 +31,7 @@ struct CreateSimpleXAddress: View {
Spacer() Spacer()
if let userAddress = m.userAddress { if let userAddress = m.userAddress {
QRCode(uri: userAddress.connReqContact) SimpleXLinkQRCode(uri: userAddress.connReqContact)
.frame(maxHeight: g.size.width) .frame(maxHeight: g.size.width)
shareQRCodeButton(userAddress) shareQRCodeButton(userAddress)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
@ -126,7 +126,7 @@ struct CreateSimpleXAddress: View {
private func shareQRCodeButton(_ userAddress: UserContactLink) -> some View { private func shareQRCodeButton(_ userAddress: UserContactLink) -> some View {
Button { Button {
showShareSheet(items: [userAddress.connReqContact]) showShareSheet(items: [simplexChatLink(userAddress.connReqContact)])
} label: { } label: {
Label("Share", systemImage: "square.and.arrow.up") Label("Share", systemImage: "square.and.arrow.up")
} }

View File

@ -93,7 +93,9 @@ struct PrivacySettings: View {
} }
settingsRow("link") { settingsRow("link") {
Picker("SimpleX links", selection: $simplexLinkMode) { Picker("SimpleX links", selection: $simplexLinkMode) {
ForEach(SimpleXLinkMode.values) { mode in ForEach(
SimpleXLinkMode.values + (SimpleXLinkMode.values.contains(simplexLinkMode) ? [] : [simplexLinkMode])
) { mode in
Text(mode.text) Text(mode.text)
} }
} }
@ -104,10 +106,6 @@ struct PrivacySettings: View {
} }
} header: { } header: {
Text("Chats") Text("Chats")
} footer: {
if case .browser = simplexLinkMode {
Text("Opening the link in the browser may reduce connection privacy and security. Untrusted SimpleX links will be red.")
}
} }
Section { Section {

View File

@ -93,7 +93,7 @@ enum SimpleXLinkMode: String, Identifiable {
case full case full
case browser case browser
static var values: [SimpleXLinkMode] = [.description, .full, .browser] static var values: [SimpleXLinkMode] = [.description, .full]
public var id: Self { self } public var id: Self { self }

View File

@ -190,7 +190,7 @@ struct UserAddressView: View {
@ViewBuilder private func existingAddressView(_ userAddress: UserContactLink) -> some View { @ViewBuilder private func existingAddressView(_ userAddress: UserContactLink) -> some View {
Section { Section {
MutableQRCode(uri: Binding.constant(userAddress.connReqContact)) MutableQRCode(uri: Binding.constant(simplexChatLink(userAddress.connReqContact)))
shareQRCodeButton(userAddress) shareQRCodeButton(userAddress)
if MFMailComposeViewController.canSendMail() { if MFMailComposeViewController.canSendMail() {
shareViaEmailButton(userAddress) shareViaEmailButton(userAddress)
@ -248,7 +248,7 @@ struct UserAddressView: View {
private func shareQRCodeButton(_ userAddress: UserContactLink) -> some View { private func shareQRCodeButton(_ userAddress: UserContactLink) -> some View {
Button { Button {
showShareSheet(items: [userAddress.connReqContact]) showShareSheet(items: [simplexChatLink(userAddress.connReqContact)])
} label: { } label: {
settingsRow("square.and.arrow.up") { settingsRow("square.and.arrow.up") {
Text("Share address") Text("Share address")

View File

@ -886,14 +886,16 @@ public enum InvitationLinkPlan: Decodable {
public enum ContactAddressPlan: Decodable { public enum ContactAddressPlan: Decodable {
case ok case ok
case ownLink case ownLink
case connecting(contact: Contact) case connectingConfirmReconnect
case connectingProhibit(contact: Contact)
case known(contact: Contact) case known(contact: Contact)
} }
public enum GroupLinkPlan: Decodable { public enum GroupLinkPlan: Decodable {
case ok case ok
case ownLink(groupInfo: GroupInfo) case ownLink(groupInfo: GroupInfo)
case connecting(groupInfo_: GroupInfo?) case connectingConfirmReconnect
case connectingProhibit(groupInfo_: GroupInfo?)
case known(groupInfo: GroupInfo) case known(groupInfo: GroupInfo)
} }

View File

@ -3068,7 +3068,7 @@ public enum Format: Decodable, Equatable {
case secret case secret
case colored(color: FormatColor) case colored(color: FormatColor)
case uri case uri
case simplexLink(linkType: SimplexLinkType, simplexUri: String, trustedUri: Bool, smpHosts: [String]) case simplexLink(linkType: SimplexLinkType, simplexUri: String, smpHosts: [String])
case email case email
case phone case phone
} }