Import Contact App Shortcut and add a share url button to the page

This commit is contained in:
Benjamin Faershtein 2025-05-13 19:56:06 -07:00
parent d5ce82347e
commit be36dc6a30
4 changed files with 132 additions and 46 deletions

View file

@ -7201,6 +7201,9 @@
}
}
}
},
"Contact URL" : {
},
"Contacts (%@)" : {
"localizations" : {
@ -9994,6 +9997,9 @@
}
}
}
},
"Done" : {
},
"Double Tap as Button" : {
"localizations" : {
@ -15082,6 +15088,12 @@
}
}
}
},
"Import Contact" : {
},
"Import Meshtastic Node %@ as a contact" : {
},
"Import Route" : {
"localizations" : {
@ -22193,6 +22205,52 @@
}
}
},
"Position config received: %@" : {
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Positionskonfiguration empfangen: %@"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Configuration de la position reçue : %@"
}
},
"he" : {
"stringUnit" : {
"state" : "translated",
"value" : "הגדרות מיקום התקבלו: %@"
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
"value" : "Configurazione della posizione ricevuta: %@"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
"value" : "Odebrano konfigurację pozycji: %@"
}
},
"se" : {
"stringUnit" : {
"state" : "translated",
"value" : "Positionskonfiguration mottagen: %@"
}
},
"sr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Конфигурација позиције примљена: %@"
}
}
}
},
"Position Exchange Failed" : {
"localizations" : {
"it" : {
@ -22467,52 +22525,6 @@
}
}
},
"Position config received: %@" : {
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Positionskonfiguration empfangen: %@"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Configuration de la position reçue : %@"
}
},
"he" : {
"stringUnit" : {
"state" : "translated",
"value" : "הגדרות מיקום התקבלו: %@"
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
"value" : "Configurazione della posizione ricevuta: %@"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
"value" : "Odebrano konfigurację pozycji: %@"
}
},
"se" : {
"stringUnit" : {
"state" : "translated",
"value" : "Positionskonfiguration mottagen: %@"
}
},
"sr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Конфигурација позиције примљена: %@"
}
}
}
},
"Power" : {
"localizations" : {
"de" : {
@ -26021,6 +26033,9 @@
}
}
}
},
"Scan this QR code to add %@ to another device." : {
},
"Screen on for" : {
"localizations" : {
@ -28033,6 +28048,9 @@
}
}
}
},
"Share Contact QR" : {
},
"Share QR Code" : {
"localizations" : {
@ -29711,6 +29729,9 @@
}
}
}
},
"Takes a Meshtastic contact URL and saves it to the nodes database" : {
},
"Tapback" : {
"localizations" : {
@ -30891,6 +30912,9 @@
}
}
}
},
"The URL for the node to import" : {
},
"There has been no response to a request for device metadata over the admin channel for this node." : {
"localizations" : {

View file

@ -58,6 +58,7 @@
8D3F8A412D44C2A6009EAAA4 /* PowerMetricsLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D3F8A402D44C2A6009EAAA4 /* PowerMetricsLog.swift */; };
B399E8A42B6F486400E4488E /* RetryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B399E8A32B6F486400E4488E /* RetryButton.swift */; };
B3E905B12B71F7F300654D07 /* TextMessageField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E905B02B71F7F300654D07 /* TextMessageField.swift */; };
BC10380F2DD4334400B00BFA /* AddContactIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC10380E2DD4333C00B00BFA /* AddContactIntent.swift */; };
BC47C2EF2CE0017D008245CA /* MessageNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC47C2EE2CE0017D008245CA /* MessageNodeIntent.swift */; };
BC6B45FF2CB2F98900723CEB /* SaveChannelSettingsIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC6B45FE2CB2F98900723CEB /* SaveChannelSettingsIntent.swift */; };
BCB613812C67290800485544 /* SendWaypointIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613802C67290800485544 /* SendWaypointIntent.swift */; };
@ -322,6 +323,7 @@
8D3F8A402D44C2A6009EAAA4 /* PowerMetricsLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PowerMetricsLog.swift; sourceTree = "<group>"; };
B399E8A32B6F486400E4488E /* RetryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RetryButton.swift; sourceTree = "<group>"; };
B3E905B02B71F7F300654D07 /* TextMessageField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextMessageField.swift; sourceTree = "<group>"; };
BC10380E2DD4333C00B00BFA /* AddContactIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactIntent.swift; sourceTree = "<group>"; };
BC47C2EE2CE0017D008245CA /* MessageNodeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageNodeIntent.swift; sourceTree = "<group>"; };
BC5EBA3B2D002A2000C442FF /* MessageNodeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageNodeIntent.swift; sourceTree = "<group>"; };
BC6B45FE2CB2F98900723CEB /* SaveChannelSettingsIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveChannelSettingsIntent.swift; sourceTree = "<group>"; };
@ -676,6 +678,7 @@
BCB6137F2C6728E700485544 /* AppIntents */ = {
isa = PBXGroup;
children = (
BC10380E2DD4333C00B00BFA /* AddContactIntent.swift */,
BC5EBA3B2D002A2000C442FF /* MessageNodeIntent.swift */,
BCB613802C67290800485544 /* SendWaypointIntent.swift */,
BCB613822C672A2600485544 /* MessageChannelIntent.swift */,
@ -1551,6 +1554,7 @@
2344A2AB2D66974300170A77 /* ManagedAttributePropertyWrapper.swift in Sources */,
BCB613832C672A2600485544 /* MessageChannelIntent.swift in Sources */,
D93068D52B812B700066FBC8 /* MessageDestination.swift in Sources */,
BC10380F2DD4334400B00BFA /* AddContactIntent.swift in Sources */,
DDA9515E2BC6F56F00CEA535 /* IndoorAirQuality.swift in Sources */,
DDDB444E29F8AB0E00EE2349 /* Int.swift in Sources */,
DD3CC6BC28E366DF00FA9159 /* Meshtastic.xcdatamodeld in Sources */,

View file

@ -0,0 +1,51 @@
//
// AddContactIntent.swift
// Meshtastic
//
// Created by Benjamin Faershtein on 5/13/25.
//
import AppIntents
import MeshtasticProtobufs
struct AddContactIntent: AppIntent {
static var title: LocalizedStringResource = "Import Contact"
static var description: IntentDescription = "Takes a Meshtastic contact URL and saves it to the nodes database"
@Parameter(title: "Contact URL", description: "The URL for the node to import")
var contactUrl: URL
// Define the function that performs the main logic
func perform() async throws -> some IntentResult {
// Ensure the BLE Manager is connected
if !BLEManager.shared.isConnected {
throw AppIntentErrors.AppIntentError.notConnected
}
if contactUrl.absoluteString.lowercased().contains("meshtastic.org/v/#") {
let components = self.contactUrl.absoluteString.components(separatedBy: "#")
// Extract contact information from the URL
if let contactData = components.last {
let decodedString = contactData.base64urlToBase64()
if let decodedData = Data(base64Encoded: decodedString) {
do {
let success = BLEManager.shared.addContactFromURL(base64UrlString: contactData)
if !success {
throw AppIntentErrors.AppIntentError.message("Failed to import contact")
}
} catch {
throw AppIntentErrors.AppIntentError.message("Failed to parse contact data: \(error.localizedDescription)")
}
}
}
// Return a success result
return .result()
} else {
throw AppIntentErrors.AppIntentError.message("The URL is not a valid Meshtastic contact link")
}
}
}

View file

@ -61,6 +61,13 @@ struct ShareContactQRDialog: View {
.font(.subheadline)
.multilineTextAlignment(.center)
.foregroundColor(.secondary)
ShareLink("Share QR Code & Link",
item: Image(uiImage: qrImage),
subject: Text("Import Meshtastic Node \(node.user.shortName) as a contact"),
message: Text(qrString),
preview: SharePreview("Import Meshtastic Node \(node.user.shortName) as a contact",
image: Image(uiImage: qrImage))
)
Button("Done") { dismiss() }
.buttonStyle(.borderedProminent)
.padding(.bottom)