@@ -19,7 +19,6 @@ import androidx.compose.foundation.verticalScroll
1919import androidx.compose.material.icons.Icons
2020import androidx.compose.material.icons.automirrored.filled.ArrowBack
2121import androidx.compose.material.icons.filled.ContentCopy
22- import androidx.compose.material.icons.filled.Keyboard
2322import androidx.compose.material.icons.filled.Link
2423import androidx.compose.material.icons.filled.QrCode
2524import androidx.compose.material.icons.filled.Refresh
@@ -55,7 +54,6 @@ import androidx.compose.ui.platform.LocalClipboardManager
5554import androidx.compose.ui.platform.LocalContext
5655import androidx.compose.ui.text.AnnotatedString
5756import androidx.compose.ui.text.font.FontWeight
58- import androidx.compose.ui.text.input.PasswordVisualTransformation
5957import androidx.compose.ui.text.style.TextAlign
6058import androidx.compose.ui.unit.dp
6159import kotlinx.coroutines.delay
@@ -70,6 +68,8 @@ import to.bitkit.ui.theme.Colors
7068fun PubkyRingAuthScreen (
7169 onNavigateBack : () -> Unit ,
7270 onSessionReceived : (PubkySession ) -> Unit ,
71+ onNavigateToScanner : (() -> Unit )? = null,
72+ scannedQrCode : String? = null,
7373 modifier : Modifier = Modifier ,
7474) {
7575 val context = LocalContext .current
@@ -85,10 +85,27 @@ fun PubkyRingAuthScreen(
8585 var manualPubkey by remember { mutableStateOf(" " ) }
8686 var manualSessionSecret by remember { mutableStateOf(" " ) }
8787 var timeRemaining by remember { mutableLongStateOf(0L ) }
88+ var pastedUrl by remember { mutableStateOf(" " ) }
8889
8990 val isPubkyRingInstalled = remember { bridge.isPubkyRingInstalled(context) }
9091 val recommendedMethod = remember { bridge.getRecommendedAuthMethod(context) }
9192
93+ // Handle scanned QR code
94+ LaunchedEffect (scannedQrCode) {
95+ scannedQrCode?.let { qrCode ->
96+ if (qrCode.contains(" pubky://" ) || qrCode.contains(" pubkyring://" )) {
97+ try {
98+ val session = bridge.handleAuthUrl(qrCode)
99+ onSessionReceived(session)
100+ } catch (e: Exception ) {
101+ errorMessage = e.message ? : " Failed to process QR code"
102+ }
103+ } else {
104+ pastedUrl = qrCode
105+ }
106+ }
107+ }
108+
92109 // Set initial tab based on availability
93110 LaunchedEffect (Unit ) {
94111 if (! isPubkyRingInstalled) {
@@ -117,9 +134,9 @@ fun PubkyRingAuthScreen(
117134 }
118135
119136 val tabs = if (isPubkyRingInstalled) {
120- listOf (" Same Device" , " QR Code " , " Manual " )
137+ listOf (" Same Device" , " Show QR " , " Scan/Paste " )
121138 } else {
122- listOf (" QR Code " , " Manual " )
139+ listOf (" Show QR " , " Scan/Paste " )
123140 }
124141
125142 Scaffold (
@@ -194,17 +211,24 @@ fun PubkyRingAuthScreen(
194211 }
195212 },
196213 )
197- 2 -> ManualEntryTabContent (
198- pubkey = manualPubkey,
199- onPubkeyChange = { manualPubkey = it },
200- sessionSecret = manualSessionSecret,
201- onSessionSecretChange = { manualSessionSecret = it },
202- onImport = {
203- val session = bridge.importSession(
204- pubkey = manualPubkey.trim(),
205- sessionSecret = manualSessionSecret.trim(),
206- )
207- onSessionReceived(session)
214+ 2 -> ScanPasteTabContent (
215+ pastedUrl = pastedUrl,
216+ onPastedUrlChange = { pastedUrl = it },
217+ onScanClick = onNavigateToScanner,
218+ onPasteFromClipboard = {
219+ clipboardManager.getText()?.text?.let { text ->
220+ pastedUrl = text
221+ }
222+ },
223+ onConnect = {
224+ scope.launch {
225+ try {
226+ val session = bridge.handleAuthUrl(pastedUrl.trim())
227+ onSessionReceived(session)
228+ } catch (e: Exception ) {
229+ errorMessage = e.message ? : " Invalid Pubky Ring URL"
230+ }
231+ }
208232 },
209233 )
210234 }
@@ -425,12 +449,12 @@ private fun CrossDeviceTabContent(
425449}
426450
427451@Composable
428- private fun ManualEntryTabContent (
429- pubkey : String ,
430- onPubkeyChange : (String ) -> Unit ,
431- sessionSecret : String ,
432- onSessionSecretChange : (String ) -> Unit ,
433- onImport : () -> Unit ,
452+ private fun ScanPasteTabContent (
453+ pastedUrl : String ,
454+ onPastedUrlChange : (String ) -> Unit ,
455+ onScanClick : (() -> Unit ) ? ,
456+ onPasteFromClipboard : () -> Unit ,
457+ onConnect : () -> Unit ,
434458 modifier : Modifier = Modifier ,
435459) {
436460 Column (
@@ -441,7 +465,7 @@ private fun ManualEntryTabContent(
441465 horizontalAlignment = Alignment .CenterHorizontally ,
442466 ) {
443467 Icon (
444- imageVector = Icons .Default .Keyboard ,
468+ imageVector = Icons .Default .QrCode ,
445469 contentDescription = null ,
446470 modifier = Modifier .size(60 .dp),
447471 tint = Colors .Brand ,
@@ -450,54 +474,71 @@ private fun ManualEntryTabContent(
450474 Spacer (modifier = Modifier .height(24 .dp))
451475
452476 Text (
453- text = " Manual Entry " ,
477+ text = " Scan or Paste " ,
454478 style = MaterialTheme .typography.headlineSmall,
455479 fontWeight = FontWeight .Bold ,
456480 )
457481
458482 Spacer (modifier = Modifier .height(8 .dp))
459483
460484 Text (
461- text = " Enter your Pubky credentials manually if other methods aren't available ." ,
485+ text = " Scan a Pubky Ring QR code or paste the connection URL ." ,
462486 style = MaterialTheme .typography.bodyMedium,
463487 color = Colors .White64 ,
464488 textAlign = TextAlign .Center ,
465489 )
466490
467491 Spacer (modifier = Modifier .height(32 .dp))
468492
469- OutlinedTextField (
470- value = pubkey,
471- onValueChange = onPubkeyChange,
472- label = { Text (" Public Key (z-base32)" ) },
473- placeholder = { Text (" e.g., z6mk..." ) },
474- modifier = Modifier .fillMaxWidth(),
475- singleLine = true ,
476- )
493+ if (onScanClick != null ) {
494+ Button (
495+ onClick = onScanClick,
496+ modifier = Modifier .fillMaxWidth(),
497+ colors = ButtonDefaults .buttonColors(
498+ containerColor = MaterialTheme .colorScheme.secondary,
499+ ),
500+ ) {
501+ Icon (Icons .Default .QrCode , contentDescription = null )
502+ Spacer (modifier = Modifier .width(8 .dp))
503+ Text (" Scan QR Code" )
504+ }
505+
506+ Spacer (modifier = Modifier .height(16 .dp))
477507
478- Spacer (modifier = Modifier .height(16 .dp))
508+ Text (
509+ text = " or paste a URL" ,
510+ style = MaterialTheme .typography.bodySmall,
511+ color = Colors .White64 ,
512+ )
513+
514+ Spacer (modifier = Modifier .height(16 .dp))
515+ }
479516
480517 OutlinedTextField (
481- value = sessionSecret ,
482- onValueChange = onSessionSecretChange ,
483- label = { Text (" Session Secret " ) },
484- placeholder = { Text (" Secret from Pubky-ring " ) },
518+ value = pastedUrl ,
519+ onValueChange = onPastedUrlChange ,
520+ label = { Text (" Pubky Ring URL " ) },
521+ placeholder = { Text (" pubky://... or pubkyring://... " ) },
485522 modifier = Modifier .fillMaxWidth(),
486523 singleLine = true ,
487- visualTransformation = PasswordVisualTransformation (),
524+ trailingIcon = {
525+ IconButton (onClick = onPasteFromClipboard) {
526+ Icon (Icons .Default .ContentCopy , contentDescription = " Paste from clipboard" )
527+ }
528+ },
488529 )
489530
490531 Spacer (modifier = Modifier .height(32 .dp))
491532
492533 Button (
493- onClick = onImport ,
534+ onClick = onConnect ,
494535 modifier = Modifier .fillMaxWidth(),
495- enabled = pubkey.isNotBlank() && sessionSecret .isNotBlank(),
536+ enabled = pastedUrl .isNotBlank(),
496537 colors = ButtonDefaults .buttonColors(
497538 containerColor = Colors .Brand ,
498539 ),
499540 ) {
500- Text (" Import Session " )
541+ Text (" Connect " )
501542 }
502543 }
503544}
0 commit comments