feat(ui): add QR code display for invite code in signing session
Android TransferScreen: - Add QR code display above invite code text - Import QRCodeWriter and related components - Add generateInviteQRCode helper function - Update hint text to mention scanning Electron CoSignSession: - Import QRCodeSVG from qrcode.react - Add QR code above invite code text with proper styling - Center QR code and update hint text 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
ed55be2b86
commit
ad79679ee2
|
|
@ -23,10 +23,16 @@ import androidx.compose.ui.text.input.KeyboardType
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import android.graphics.Bitmap
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import com.durian.tssparty.domain.model.NetworkType
|
||||
import com.durian.tssparty.domain.model.SessionStatus
|
||||
import com.durian.tssparty.domain.model.ShareRecord
|
||||
import com.durian.tssparty.util.TransactionUtils
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.google.zxing.qrcode.QRCodeWriter
|
||||
import com.journeyapps.barcodescanner.ScanContract
|
||||
import com.journeyapps.barcodescanner.ScanOptions
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -650,20 +656,48 @@ private fun SigningScreen(
|
|||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
// Invite code (if waiting)
|
||||
// Invite code with QR code (if waiting)
|
||||
if (sessionStatus == SessionStatus.WAITING && inviteCode != null) {
|
||||
Card(
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant
|
||||
)
|
||||
) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
text = "邀请码",
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// QR Code for invite code
|
||||
val qrBitmap = remember(inviteCode) {
|
||||
generateInviteQRCode(inviteCode, 200)
|
||||
}
|
||||
qrBitmap?.let { bitmap ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(200.dp)
|
||||
.background(Color.White),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Image(
|
||||
bitmap = bitmap.asImageBitmap(),
|
||||
contentDescription = "邀请码二维码",
|
||||
modifier = Modifier.size(200.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// Invite code text with copy button
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
|
|
@ -679,11 +713,15 @@ private fun SigningScreen(
|
|||
Text("复制")
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Text(
|
||||
text = "将此邀请码分享给其他签名参与者",
|
||||
text = "扫描二维码或分享邀请码给其他签名参与者",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1089,3 +1127,24 @@ private fun InfoRow(label: String, value: String) {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate QR code bitmap for invite code
|
||||
*/
|
||||
private fun generateInviteQRCode(content: String, size: Int): Bitmap? {
|
||||
return try {
|
||||
val writer = QRCodeWriter()
|
||||
val bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, size, size)
|
||||
val width = bitMatrix.width
|
||||
val height = bitMatrix.height
|
||||
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
|
||||
for (x in 0 until width) {
|
||||
for (y in 0 until height) {
|
||||
bitmap.setPixel(x, y, if (bitMatrix[x, y]) android.graphics.Color.BLACK else android.graphics.Color.WHITE)
|
||||
}
|
||||
}
|
||||
bitmap
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { QRCodeSVG } from 'qrcode.react';
|
||||
import styles from './Session.module.css';
|
||||
import {
|
||||
finalizeTransaction,
|
||||
|
|
@ -346,6 +347,28 @@ export default function CoSignSession() {
|
|||
{session.inviteCode && session.status === 'waiting' && (
|
||||
<div className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>邀请码</h3>
|
||||
|
||||
{/* QR Code */}
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
marginBottom: 'var(--spacing-md)',
|
||||
}}>
|
||||
<div style={{
|
||||
padding: 'var(--spacing-md)',
|
||||
backgroundColor: 'white',
|
||||
borderRadius: 'var(--radius-md)',
|
||||
border: '1px solid var(--border-color)',
|
||||
}}>
|
||||
<QRCodeSVG
|
||||
value={session.inviteCode}
|
||||
size={180}
|
||||
level="M"
|
||||
includeMargin={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.publicKeyWrapper}>
|
||||
<code className={styles.publicKey}>{session.inviteCode}</code>
|
||||
<button
|
||||
|
|
@ -361,8 +384,8 @@ export default function CoSignSession() {
|
|||
复制
|
||||
</button>
|
||||
</div>
|
||||
<p style={{ fontSize: '12px', color: 'var(--text-secondary)', marginTop: 'var(--spacing-xs)' }}>
|
||||
将此邀请码分享给其他参与方,他们可以使用此码加入签名
|
||||
<p style={{ fontSize: '12px', color: 'var(--text-secondary)', marginTop: 'var(--spacing-xs)', textAlign: 'center' }}>
|
||||
扫描二维码或分享邀请码给其他参与方加入签名
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Reference in New Issue