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:
hailin 2026-01-01 18:37:18 -08:00
parent ed55be2b86
commit ad79679ee2
2 changed files with 90 additions and 8 deletions

View File

@ -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
}
}

View File

@ -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>
)}