mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 10:29:58 +02:00
feat(network): support custom relay servers
Add strict custom Iroh relay profiles with safe restart and rollback across the Rust core, Compose apps, and Apple apps. Preserve multi-relay invitations and fail closed on configuration or recovery mismatches.
This commit is contained in:
@@ -43,12 +43,14 @@ struct TransferDetailsView: View {
|
||||
count: pendingReceivers + completedReceivers,
|
||||
onTap: model.openReceivers
|
||||
)
|
||||
DetailDestination(
|
||||
title: String(localized: "transfer_share_title"),
|
||||
description: String(localized: "transfer_share_description"),
|
||||
count: 0,
|
||||
onTap: model.openShare
|
||||
)
|
||||
if transfer.invitationPresentation != .unavailable {
|
||||
DetailDestination(
|
||||
title: String(localized: "transfer_share_title"),
|
||||
description: String(localized: "transfer_share_description"),
|
||||
count: 0,
|
||||
onTap: model.openShare
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if isActiveShare {
|
||||
@@ -276,24 +278,41 @@ struct TransferSharePanel: View {
|
||||
|
||||
var body: some View {
|
||||
PanelContainer(title: String(localized: "transfer_share_title")) {
|
||||
if let ticket = transfer.ticket {
|
||||
qrCard(ticket: ticket)
|
||||
Text(LocalizedStringKey("transfer_scan_qr"))
|
||||
.font(VniType.bodySmall).foregroundStyle(colors.foregroundLighter)
|
||||
.frame(maxWidth: .infinity)
|
||||
switch transfer.invitationPresentation {
|
||||
case .ready(let ticket):
|
||||
let qrImage = QRCode.generate(from: ticket)
|
||||
qrCard(image: qrImage)
|
||||
if qrImage != nil {
|
||||
Text(LocalizedStringKey("transfer_scan_qr"))
|
||||
.font(VniType.bodySmall).foregroundStyle(colors.foregroundLighter)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
ShareActionsView(model: model, transfer: transfer, ticket: ticket)
|
||||
} else {
|
||||
case .preparing:
|
||||
Text(LocalizedStringKey("transfer_event_preparing")).foregroundStyle(colors.foregroundLighter)
|
||||
case .unavailable:
|
||||
Text(LocalizedStringKey(
|
||||
transfer.status == .failed ? "transfer_event_failed" : "transfer_event_stopped"
|
||||
))
|
||||
.foregroundStyle(colors.foregroundLighter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func qrCard(ticket: String) -> some View {
|
||||
private func qrCard(image: Image?) -> some View {
|
||||
ZStack {
|
||||
if let qr = QRCode.generate(from: ticket) {
|
||||
qr.interpolation(.none).resizable().scaledToFit().padding(14)
|
||||
if let image {
|
||||
image.interpolation(.none).resizable().scaledToFit().padding(14)
|
||||
} else {
|
||||
ProgressView()
|
||||
VStack(spacing: 10) {
|
||||
Image(systemName: "qrcode")
|
||||
.font(.system(size: 36, weight: .medium))
|
||||
Text(LocalizedStringKey("transfer_qr_unavailable"))
|
||||
.font(VniType.bodySmall)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
.foregroundStyle(.black.opacity(0.72))
|
||||
.padding(22)
|
||||
}
|
||||
}
|
||||
.frame(width: 268, height: 268)
|
||||
@@ -302,6 +321,26 @@ struct TransferSharePanel: View {
|
||||
}
|
||||
}
|
||||
|
||||
enum TransferInvitationPresentation: Equatable {
|
||||
case preparing
|
||||
case ready(String)
|
||||
case unavailable
|
||||
}
|
||||
|
||||
extension Transfer {
|
||||
var invitationPresentation: TransferInvitationPresentation {
|
||||
switch status {
|
||||
case .importing:
|
||||
return .preparing
|
||||
case .sharing:
|
||||
guard let ticket, !ticket.isEmpty else { return .preparing }
|
||||
return .ready(ticket)
|
||||
case .receiving, .done, .failed, .cancelled, .stopped:
|
||||
return .unavailable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - QR generation (CoreImage)
|
||||
|
||||
enum QRCode {
|
||||
|
||||
Reference in New Issue
Block a user