diff --git a/capacitor.config.json b/capacitor.config.json index dba3e9d8..52a86ff4 100644 --- a/capacitor.config.json +++ b/capacitor.config.json @@ -1,5 +1,5 @@ { - "appId": "app.timesafari", + "appId": "app.trentlarson.timesafari", "appName": "TimeSafari", "webDir": "dist", "bundledWebRuntime": false, diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj index d7b84932..320f7c3b 100644 --- a/ios/App/App.xcodeproj/project.pbxproj +++ b/ios/App/App.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 48; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -123,8 +123,9 @@ 504EC2FC1FED79650016851F /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 920; - LastUpgradeCheck = 920; + LastUpgradeCheck = 1630; TargetAttributes = { 504EC3031FED79650016851F = { CreatedOnToolsVersion = 9.2; @@ -268,6 +269,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -275,8 +277,10 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -286,8 +290,10 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 7XVXYPEQYJ; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -325,6 +331,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -332,8 +339,10 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -343,8 +352,10 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = 7XVXYPEQYJ; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -356,7 +367,8 @@ IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; name = Release; @@ -368,18 +380,22 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 18; - DEVELOPMENT_TEAM = GM3FS5JQPH; + ENABLE_APP_SANDBOX = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; INFOPLIST_FILE = App/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); MARKETING_VERSION = 0.4.7; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; - PRODUCT_BUNDLE_IDENTIFIER = app.timesafari; + PRODUCT_BUNDLE_IDENTIFIER = app.trentlarson.timesafari; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; /* allows agvtool to set *_VERSION settings, but new-marketing-version sets MARKETING_VERSION in Info.plist instead */ + VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; }; @@ -390,17 +406,21 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 18; - DEVELOPMENT_TEAM = GM3FS5JQPH; + ENABLE_APP_SANDBOX = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; INFOPLIST_FILE = App/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); MARKETING_VERSION = 0.4.7; - PRODUCT_BUNDLE_IDENTIFIER = app.timesafari; + PRODUCT_BUNDLE_IDENTIFIER = app.trentlarson.timesafari; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; /* allows agvtool to set *_VERSION settings, but new-marketing-version sets MARKETING_VERSION in Info.plist instead */ + VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; diff --git a/src/views/ContactQRScanFullView.vue b/src/views/ContactQRScanFullView.vue index ae90b964..02e228a5 100644 --- a/src/views/ContactQRScanFullView.vue +++ b/src/views/ContactQRScanFullView.vue @@ -1,24 +1,104 @@ @@ -33,18 +113,29 @@ import { NotificationIface } from "../constants/app"; import { db } from "../db/index"; import { Contact } from "../db/tables/contacts"; import { getContactJwtFromJwtUrl } from "../libs/crypto"; -import { decodeEndorserJwt } from "../libs/crypto/vc"; +import { decodeEndorserJwt, ETHR_DID_PREFIX } from "../libs/crypto/vc"; import { retrieveSettingsForActiveAccount } from "../db/index"; import { setVisibilityUtil } from "../libs/endorserServer"; +import { useClipboard } from "@vueuse/core"; +import QRCodeVue3 from "qr-code-generator-vue3"; +import UserNameDialog from "../components/UserNameDialog.vue"; +import { generateEndorserJwtUrlForAccount } from "../libs/endorserServer"; +import { retrieveAccountMetadata } from "../libs/util"; interface QRScanResult { rawValue?: string; barcode?: string; } +interface IUserNameDialog { + open: (callback: (name: string) => void) => void; +} + @Component({ components: { QuickNav, + QRCodeVue3, + UserNameDialog, }, }) export default class ContactQRScan extends Vue { @@ -55,11 +146,14 @@ export default class ContactQRScan extends Vue { error: string | null = null; activeDid = ""; apiServer = ""; + givenName = ""; + qrValue = ""; + ETHR_DID_PREFIX = ETHR_DID_PREFIX; // Add new properties to track scanning state private lastScannedValue: string = ""; private lastScanTime: number = 0; - private readonly SCAN_DEBOUNCE_MS = 2000; // Prevent duplicate scans within 2 seconds + private readonly SCAN_DEBOUNCE_MS = 5000; // Increased from 2000 to 5000ms to better handle mobile scanning // Add cleanup tracking private isCleaningUp = false; @@ -70,6 +164,21 @@ export default class ContactQRScan extends Vue { const settings = await retrieveSettingsForActiveAccount(); this.activeDid = settings.activeDid || ""; this.apiServer = settings.apiServer || ""; + this.givenName = settings.firstName || ""; + + const account = await retrieveAccountMetadata(this.activeDid); + if (account) { + const name = + (settings.firstName || "") + + (settings.lastName ? ` ${settings.lastName}` : ""); + this.qrValue = await generateEndorserJwtUrlForAccount( + account, + !!settings.isRegistered, + name, + settings.profileImageUrl || "", + false, + ); + } } catch (error) { logger.error("Error initializing component:", { error: error instanceof Error ? error.message : String(error), @@ -270,14 +379,12 @@ export default class ContactQRScan extends Vue { notes: contactInfo.notes || "", }; - // Add contact and stop scanning + // Add contact but keep scanning logger.info("Adding new contact to database:", { did: contact.did, name: contact.name, }); await this.addNewContact(contact); - await this.stopScanning(); - this.$router.back(); // Return to previous view after successful scan } catch (error) { logger.error("Error processing contact QR code:", { error: error instanceof Error ? error.message : String(error), @@ -420,6 +527,56 @@ export default class ContactQRScan extends Vue { await this.cleanupScanner(); this.$router.back(); } + + toastQRCodeHelp() { + this.$notify( + { + group: "alert", + type: "info", + title: "QR Code Help", + text: "Click the QR code to copy your contact info to your clipboard.", + }, + 5000, + ); + } + + onCopyUrlToClipboard() { + useClipboard() + .copy(this.qrValue) + .then(() => { + this.$notify( + { + group: "alert", + type: "toast", + title: "Copied", + text: "Contact URL was copied to clipboard.", + }, + 2000, + ); + }); + } + + onCopyDidToClipboard() { + useClipboard() + .copy(this.activeDid) + .then(() => { + this.$notify( + { + group: "alert", + type: "info", + title: "Copied", + text: "Your DID was copied to the clipboard. Have them paste it in the box on their 'People' screen to add you.", + }, + 5000, + ); + }); + } + + openUserNameDialog() { + (this.$refs.userNameDialog as IUserNameDialog).open((name: string) => { + this.givenName = name; + }); + } } diff --git a/src/views/ContactQRScanShowView.vue b/src/views/ContactQRScanShowView.vue index d4018c25..85b032c2 100644 --- a/src/views/ContactQRScanShowView.vue +++ b/src/views/ContactQRScanShowView.vue @@ -42,7 +42,7 @@
-