address review: migration close, pause scanner on wrong-type, drop dead strIsSimplexLink

- MigrateToDevice passes the real `close` (not `{}`), so the wrong-type Connect
  button would dismiss correctly if the view is ever opened with an active user.
- Keep the connect/migration scanners on .continuous, but pause the scanner while
  the wrong-type alert is shown and resume on dismissal (handleScan takes an
  optional scannerPaused Binding; the alert's dismiss buttons clear it). This
  fixes the alert re-firing every scanInterval without changing the scan mode.
  The oncePerCode scanners pass nil — no-op for them.
- Remove the now-uncalled strIsSimplexLink on both platforms (the classifier
  inlines parseToMarkdown / parseSimpleXMarkdown; its only caller, verifyOnly,
  was already removed).
- Capitalise "Your servers" in the server-scan breadcrumb to match the actual
  UI label (your_servers = "Your servers"), consistent with the other labels.
This commit is contained in:
Narasimha-sc
2026-06-22 16:23:06 +00:00
parent 7581713f02
commit c4d7065051
8 changed files with 36 additions and 40 deletions
@@ -103,6 +103,7 @@ struct MigrateToDevice: View {
// Prevent from hiding the view until migration is finished or app deleted
@State private var backDisabled: Bool = false
@State private var showQRCodeScanner: Bool = true
@State private var scannerPaused: Bool = false
@State private var pasteboardHasStrings = UIPasteboard.general.hasStrings
@State private var importingArchiveFromFileProgressIndicator = false
@@ -201,10 +202,10 @@ struct MigrateToDevice: View {
ZStack {
List {
Section(header: Text("Scan QR code").foregroundColor(theme.colors.secondary)) {
ScannerInView(showQRCodeScanner: $showQRCodeScanner) { resp in
ScannerInView(showQRCodeScanner: $showQRCodeScanner, scannerPaused: $scannerPaused) { resp in
switch resp {
case let .success(r):
handleScan(r.string, expected: .migrationLink, theme: theme) { qr in
handleScan(r.string, expected: .migrationLink, theme: theme, scannerPaused: $scannerPaused) { qr in
migrationState = .linkDownloading(link: qr.text)
}
case let .failure(e):
@@ -700,7 +700,7 @@ private struct ConnectView: View {
private func processQRCode(_ resp: Result<ScanResult, ScanError>) {
switch resp {
case let .success(r):
handleScan(r.string, expected: .connectionLink, theme: theme) { qr in connect(qr.text) }
handleScan(r.string, expected: .connectionLink, theme: theme, scannerPaused: $scannerPaused) { qr in connect(qr.text) }
case let .failure(e):
logger.error("processQRCode QR code error: \(e.localizedDescription)")
alert = .newChatSomeAlert(alert: SomeAlert(
@@ -849,16 +849,6 @@ struct InfoSheetButton<Content: View>: View {
}
}
func strIsSimplexLink(_ str: String) -> Bool {
if let parsedMd = parseSimpleXMarkdown(str),
parsedMd.count == 1,
case .simplexLink = parsedMd[0].format {
return true
} else {
return false
}
}
enum ConnectTarget {
case link(text: String, linkType: SimplexLinkType, linkText: String)
case name(SimplexNameInfo)
+25 -15
View File
@@ -14,21 +14,25 @@ import SimpleXChat
// The shared gate every scanner routes through. If the scan is the accepted kind, run the
// screen's onMatch; otherwise show the one wrong-type alert. iOS has no Bool return / de-dup
// (the scanner controls re-scanning via scanMode / scannerPaused).
func handleScan(_ raw: String, expected: QRCodeKind, theme: AppTheme, onMatch: (QRCodeType) -> Void) {
func handleScan(_ raw: String, expected: QRCodeKind, theme: AppTheme, scannerPaused: Binding<Bool>? = nil, onMatch: (QRCodeType) -> Void) {
let qr = parseQRCode(raw)
if qr.kind == expected {
onMatch(qr)
} else {
showWrongQRCodeAlert(qr, expected: expected, theme: theme)
// Pause a continuous scanner while the wrong-type alert is up so it doesn't re-fire the
// alert every scanInterval; resume on dismissal. oncePerCode scanners pass nil (no-op).
scannerPaused?.wrappedValue = true
showWrongQRCodeAlert(qr, expected: expected, theme: theme) { scannerPaused?.wrappedValue = false }
}
}
private func showWrongQRCodeAlert(_ scanned: QRCodeType, expected: QRCodeKind, theme: AppTheme) {
private func showWrongQRCodeAlert(_ scanned: QRCodeType, expected: QRCodeKind, theme: AppTheme, onDismiss: @escaping () -> Void) {
// Relay link: it cannot be used to connect reuse the existing relay alert (title + message).
if case .connectionLink(_, .relay) = scanned {
AlertManager.shared.showAlert(mkAlert(
title: "Relay address",
message: "This is a chat relay address, it cannot be used to connect."
AlertManager.shared.showAlert(Alert(
title: Text("Relay address"),
message: Text("This is a chat relay address, it cannot be used to connect."),
dismissButton: .default(Text("Ok"), action: onDismiss)
))
return
}
@@ -36,14 +40,18 @@ private func showWrongQRCodeAlert(_ scanned: QRCodeType, expected: QRCodeKind, t
// the server screen says "Invalid server address!", the verify screen says "Incorrect security
// code!" (it scans a code, not a link), everything else the generic "not a SimpleX link".
if case .unknown = scanned {
let title: LocalizedStringKey
let message: LocalizedStringKey?
switch expected {
case .serverAddress:
AlertManager.shared.showAlert(mkAlert(title: "Invalid server address!", message: "Check server address and try again."))
case .securityCode:
AlertManager.shared.showAlert(mkAlert(title: "Incorrect security code!"))
default:
AlertManager.shared.showAlert(mkAlert(title: "Invalid QR code", message: "The code you scanned is not a SimpleX link QR code."))
case .serverAddress: title = "Invalid server address!"; message = "Check server address and try again."
case .securityCode: title = "Incorrect security code!"; message = nil
default: title = "Invalid QR code"; message = "The code you scanned is not a SimpleX link QR code."
}
AlertManager.shared.showAlert(Alert(
title: Text(title),
message: message.map { Text($0) },
dismissButton: .default(Text("Ok"), action: onDismiss)
))
return
}
// Recognised wrong kind: "<what it is>\n\n<where to scan it>".
@@ -58,14 +66,16 @@ private func showWrongQRCodeAlert(_ scanned: QRCodeType, expected: QRCodeKind, t
title: Text("Wrong QR code"),
message: Text(verbatim: message),
primaryButton: .default(Text("Connect")) {
onDismiss()
planAndConnect(text, theme: theme, dismiss: true)
},
secondaryButton: .cancel()
secondaryButton: .cancel(onDismiss)
))
} else {
AlertManager.shared.showAlert(Alert(
title: Text("Wrong QR code"),
message: Text(verbatim: message)
message: Text(verbatim: message),
dismissButton: .default(Text("Ok"), action: onDismiss)
))
}
}
@@ -91,7 +101,7 @@ private extension QRCodeType {
case let .connectionLink(_, linkType):
return linkType == .relay ? nil : NSLocalizedString("Open New chat, then scan or paste the link.", comment: "qr where to scan")
case .serverAddress:
return NSLocalizedString("Open Settings, Network & servers, your servers, then Scan server QR code.", comment: "qr where to scan")
return NSLocalizedString("Open Settings, Network & servers, Your servers, then Scan server QR code.", comment: "qr where to scan")
case .migrationLink:
return NSLocalizedString("On the new device, when first setting up the app, choose Migrate from another device.", comment: "qr where to scan")
case .desktopAddress:
@@ -65,10 +65,10 @@ func parseQRCode(_ raw: String) -> QRCodeType {
let t = raw.trimmingCharacters(in: .whitespacesAndNewlines)
if strHasSimplexFileLink(t) { return .migrationLink(text: t) }
if t.hasPrefix(desktopAddressScheme) { return .desktopAddress(text: t) }
// Match only when the WHOLE string is exactly one SimpleX link same as `strIsSimplexLink`
// and the master connect scanner not a link embedded among other text. A SimpleX *name*
// (Format.simplexName) is therefore not a connectionLink here; it falls through to .unknown,
// exactly as the master scanner treats it (names are handled only by the paste path).
// Match only when the WHOLE string is exactly one SimpleX link not a link embedded among
// other text. A SimpleX *name* (Format.simplexName) is therefore not a connectionLink here; it
// falls through to .unknown, the same "not a SimpleX link" the connect scanner showed before
// this change (names are handled only by the paste path, via strConnectTarget).
if let md = parseSimpleXMarkdown(t), md.count == 1,
case let .simplexLink(_, linkType, _, _) = md[0].format {
return .connectionLink(text: t, linkType: linkType)
@@ -206,7 +206,7 @@ private fun MutableState<MigrationToState?>.PasteOrScanLinkView(close: () -> Uni
if (appPlatform.isAndroid) {
SectionView(stringResource(MR.strings.scan_QR_code).replace('\n', ' ')) {
QRCodeScanner(showQRCodeScanner = remember { mutableStateOf(true) }) { text ->
handleScan(null, text, QRCodeType.MigrationLink::class, close = {}) { checkUserLink(it.text) }
handleScan(null, text, QRCodeType.MigrationLink::class, close) { checkUserLink(it.text) }
}
}
SectionSpacer()
@@ -801,11 +801,6 @@ private fun createInvitation(
}
}
fun strIsSimplexLink(str: String): Boolean {
val parsedMd = parseToMarkdown(str)
return parsedMd != null && parsedMd.size == 1 && parsedMd[0].format is Format.SimplexLink
}
sealed class ConnectTarget {
class Link(val text: String, val linkType: SimplexLinkType, val linkText: String) : ConnectTarget()
class Name(val nameInfo: SimplexNameInfo) : ConnectTarget()
@@ -778,7 +778,7 @@
<string name="qr_type_desktop_address">This is an address for linking a mobile to a SimpleX desktop app.</string>
<string name="qr_type_security_code">This is a contact\'s security code.</string>
<string name="qr_where_connection">Tap New chat, then Paste link / Scan.</string>
<string name="qr_where_server">Tap your profile image, then Settings, Network &amp; servers, your servers, Scan server QR code.</string>
<string name="qr_where_server">Tap your profile image, then Settings, Network &amp; servers, Your servers, Scan server QR code.</string>
<string name="qr_where_desktop">Tap your profile image, then Use from desktop, Scan QR code from desktop.</string>
<string name="qr_where_migration">On the new device, when first setting up the app, choose Migrate from another device.</string>
<string name="qr_where_security_code">Open the chat, tap the contact\'s name, then Verify security code.</string>
+1 -1
View File
@@ -270,7 +270,7 @@ qr_type_migration_link = "This is a link to migrate to another device."
qr_type_desktop_address = "This is an address for linking a mobile to a SimpleX desktop app."
qr_type_security_code = "This is a contact's security code."
qr_where_connection = "Tap New chat, then Paste link / Scan."
qr_where_server = "Tap your profile image, then Settings, Network & servers, your servers, Scan server QR code."
qr_where_server = "Tap your profile image, then Settings, Network & servers, Your servers, Scan server QR code."
qr_where_desktop = "Tap your profile image, then Use from desktop, Scan QR code from desktop."
qr_where_migration = "On the new device, when first setting up the app, choose Migrate from another device."
qr_where_security_code = "Open the chat, tap the contact's name, then Verify security code."