Skip to content

Commit 364a94c

Browse files
committed
Updated sheet for the "Extend public key" on the "Tools" page.
1 parent 7a7ad35 commit 364a94c

9 files changed

Lines changed: 290 additions & 5 deletions

File tree

DashWallet.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,7 @@
823823
C917024129D462C6008C034D /* PayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C917024029D462C6008C034D /* PayViewController.swift */; };
824824
C91E919729FBACE6003E7883 /* ExtendedPublicKeysModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C91E919629FBACE6003E7883 /* ExtendedPublicKeysModel.swift */; };
825825
C91E91AE29FFC8A1003E7883 /* ExtendedPublicKeysViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C91E91AD29FFC8A1003E7883 /* ExtendedPublicKeysViewController.swift */; };
826+
B3A1F2C4D5E6078901234568 /* ExtendedPublicKeySheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3A1F2C4D5E6078901234567 /* ExtendedPublicKeySheet.swift */; };
826827
C93078482A6AD4F500906E4B /* ConfirmPaymentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C956AF222A5C7D93002FAB75 /* ConfirmPaymentViewController.swift */; };
827828
C93078492A6AD4FC00906E4B /* UpholdConfirmViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C956AF2E2A5D5F6B002FAB75 /* UpholdConfirmViewController.swift */; };
828829
C930784A2A6AD52400906E4B /* UpholdConfirmTransferModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C956AF302A5D601C002FAB75 /* UpholdConfirmTransferModel.swift */; };
@@ -1099,6 +1100,7 @@
10991100
C9D2C6D42A320AA000D15901 /* DWRecoverTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A74EFFA2305464C00C475EB /* DWRecoverTextView.m */; };
11001101
C9D2C6D52A320AA000D15901 /* TransactionMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4709C314287EA11900B4BD48 /* TransactionMetadata.swift */; };
11011102
C9D2C6D62A320AA000D15901 /* ExtendedPublicKeysViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C91E91AD29FFC8A1003E7883 /* ExtendedPublicKeysViewController.swift */; };
1103+
B3A1F2C4D5E6078901234569 /* ExtendedPublicKeySheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3A1F2C4D5E6078901234567 /* ExtendedPublicKeySheet.swift */; };
11021104
C9D2C6D82A320AA000D15901 /* AtmListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47AE8BDD28C1305E00490F5E /* AtmListViewController.swift */; };
11031105
C9D2C6D92A320AA000D15901 /* TxDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47A5145F2848F75B005A8E3E /* TxDetailViewController.swift */; };
11041106
C9D2C6DA2A320AA000D15901 /* NSAttributedString+Builder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 472D13E9299E5396006903F1 /* NSAttributedString+Builder.swift */; };
@@ -2698,6 +2700,7 @@
26982700
C917024029D462C6008C034D /* PayViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayViewController.swift; sourceTree = "<group>"; };
26992701
C91E919629FBACE6003E7883 /* ExtendedPublicKeysModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtendedPublicKeysModel.swift; sourceTree = "<group>"; };
27002702
C91E91AD29FFC8A1003E7883 /* ExtendedPublicKeysViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtendedPublicKeysViewController.swift; sourceTree = "<group>"; };
2703+
B3A1F2C4D5E6078901234567 /* ExtendedPublicKeySheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtendedPublicKeySheet.swift; sourceTree = "<group>"; };
27012704
C943B2BA2A408CEC00AF23C5 /* DWUpdatingUserProfileView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWUpdatingUserProfileView.h; sourceTree = "<group>"; };
27022705
C943B2BB2A408CEC00AF23C5 /* DWCurrentUserProfileView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWCurrentUserProfileView.h; sourceTree = "<group>"; };
27032706
C943B2BC2A408CEC00AF23C5 /* DWErrorUpdatingUserProfileView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DWErrorUpdatingUserProfileView.m; sourceTree = "<group>"; };
@@ -4466,6 +4469,7 @@
44664469
children = (
44674470
C91E919629FBACE6003E7883 /* ExtendedPublicKeysModel.swift */,
44684471
C91E91AD29FFC8A1003E7883 /* ExtendedPublicKeysViewController.swift */,
4472+
B3A1F2C4D5E6078901234567 /* ExtendedPublicKeySheet.swift */,
44694473
);
44704474
path = ExtendedKeys;
44714475
sourceTree = "<group>";
@@ -8628,6 +8632,7 @@
86288632
2A74EFFB2305464C00C475EB /* DWRecoverTextView.m in Sources */,
86298633
4709C315287EA11900B4BD48 /* TransactionMetadata.swift in Sources */,
86308634
C91E91AE29FFC8A1003E7883 /* ExtendedPublicKeysViewController.swift in Sources */,
8635+
B3A1F2C4D5E6078901234568 /* ExtendedPublicKeySheet.swift in Sources */,
86318636
47AE8BFE28C1306000490F5E /* AtmListViewController.swift in Sources */,
86328637
754D02082D155564005CA466 /* CoinJoinTransaction.swift in Sources */,
86338638
47A514602848F75B005A8E3E /* TxDetailViewController.swift in Sources */,
@@ -9307,6 +9312,7 @@
93079312
C943B5012A40A54600AF23C5 /* DWDPGenericItemView.m in Sources */,
93089313
C9D2C6D52A320AA000D15901 /* TransactionMetadata.swift in Sources */,
93099314
C9D2C6D62A320AA000D15901 /* ExtendedPublicKeysViewController.swift in Sources */,
9315+
B3A1F2C4D5E6078901234569 /* ExtendedPublicKeySheet.swift in Sources */,
93109316
C9D2C6D82A320AA000D15901 /* AtmListViewController.swift in Sources */,
93119317
C9D2C6D92A320AA000D15901 /* TxDetailViewController.swift in Sources */,
93129318
C9D2C6DA2A320AA000D15901 /* NSAttributedString+Builder.swift in Sources */,

DashWallet/Resources/AppAssets.xcassets/Navigation bar/toolbar-close.imageset/Contents.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,9 @@
1919
"info" : {
2020
"author" : "xcode",
2121
"version" : 1
22+
},
23+
"properties" : {
24+
"localizable" : true,
25+
"template-rendering-intent" : "template"
2226
}
2327
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
//
2+
// ExtendedPublicKeySheet.swift
3+
// DashWallet
4+
//
5+
// Copyright © 2026 Dash Core Group. All rights reserved.
6+
//
7+
// Licensed under the MIT License (the "License");
8+
// you may not use this file except in compliance with the License.
9+
// You may obtain a copy of the License at
10+
//
11+
// https://opensource.org/licenses/MIT
12+
//
13+
// Unless required by applicable law or agreed to in writing, software
14+
// distributed under the License is distributed on an "AS IS" BASIS,
15+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
// See the License for the specific language governing permissions and
17+
// limitations under the License.
18+
//
19+
20+
import SwiftUI
21+
import UIKit
22+
23+
// MARK: - ExtendedPublicKeySheetViewModel
24+
25+
class ExtendedPublicKeySheetViewModel: ObservableObject {
26+
@Published var keyValue: String = ""
27+
@Published var qrImage: UIImage? = nil
28+
29+
init() {
30+
loadKey()
31+
}
32+
33+
private func loadKey() {
34+
let model = ExtendedPublicKeysModel()
35+
guard let firstPath = model.derivationPaths.first else { return }
36+
keyValue = firstPath.item.value
37+
qrImage = generateQRCode(from: keyValue)
38+
}
39+
40+
private func generateQRCode(from string: String) -> UIImage? {
41+
guard !string.isEmpty,
42+
let filter = CIFilter(name: "CIQRCodeGenerator"),
43+
let data = string.data(using: .utf8) else { return nil }
44+
45+
filter.setValue(data, forKey: "inputMessage")
46+
filter.setValue("M", forKey: "inputCorrectionLevel")
47+
48+
guard let outputImage = filter.outputImage else { return nil }
49+
50+
let scale = 180.0 / outputImage.extent.width
51+
let scaledImage = outputImage.transformed(by: CGAffineTransform(scaleX: scale, y: scale))
52+
let context = CIContext()
53+
guard let cgImage = context.createCGImage(scaledImage, from: scaledImage.extent) else { return nil }
54+
return UIImage(cgImage: cgImage)
55+
}
56+
}
57+
58+
// MARK: - ShareSheet
59+
60+
private struct ShareSheet: UIViewControllerRepresentable {
61+
let items: [Any]
62+
63+
func makeUIViewController(context: Context) -> UIActivityViewController {
64+
UIActivityViewController(activityItems: items, applicationActivities: nil)
65+
}
66+
67+
func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {}
68+
}
69+
70+
// MARK: - ExtendedPublicKeySheet
71+
72+
struct ExtendedPublicKeySheet: View {
73+
@StateObject private var viewModel = ExtendedPublicKeySheetViewModel()
74+
@Environment(\.dismiss) private var dismiss
75+
@Environment(\.colorScheme) private var colorScheme
76+
77+
@State private var isCopied = false
78+
@State private var showShareSheet = false
79+
80+
var body: some View {
81+
VStack(spacing: 0) {
82+
// Grabber
83+
VStack {
84+
RoundedRectangle(cornerRadius: 5, style: .continuous)
85+
.fill(Color.gray300.opacity(0.5))
86+
.frame(width: 36, height: 5)
87+
}
88+
.frame(maxWidth: .infinity, minHeight: 18)
89+
90+
// Close button
91+
NavBarClose {
92+
dismiss()
93+
}
94+
95+
// QR code
96+
Group {
97+
if let qrImage = viewModel.qrImage {
98+
Image(uiImage: qrImage)
99+
.interpolation(.none)
100+
.resizable()
101+
.frame(width: 180, height: 180)
102+
} else {
103+
RoundedRectangle(cornerRadius: 8, style: .continuous)
104+
.fill(Color.gray300.opacity(0.2))
105+
.frame(width: 180, height: 180)
106+
}
107+
}
108+
.padding(.vertical, 30)
109+
110+
// Title + tappable key text
111+
VStack(alignment: .leading, spacing: 6) {
112+
Text(NSLocalizedString("Extended public key (BIP 44)", comment: ""))
113+
.font(.system(size: 28, weight: .bold))
114+
.foregroundColor(.primaryText)
115+
.frame(maxWidth: .infinity, alignment: .leading)
116+
117+
Button(action: {
118+
guard !viewModel.keyValue.isEmpty else { return }
119+
UIPasteboard.general.string = viewModel.keyValue
120+
isCopied = true
121+
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
122+
isCopied = false
123+
}
124+
}) {
125+
Text(viewModel.keyValue.isEmpty
126+
? NSLocalizedString("Not available", comment: "")
127+
: viewModel.keyValue)
128+
.font(.system(size: 15))
129+
.foregroundColor(.secondaryText)
130+
.multilineTextAlignment(.leading)
131+
.frame(maxWidth: .infinity, alignment: .leading)
132+
}
133+
.buttonStyle(PlainButtonStyle())
134+
.overlay(
135+
Group {
136+
if isCopied {
137+
Text(NSLocalizedString("Copied", comment: ""))
138+
.font(.caption)
139+
.padding(.horizontal, 12)
140+
.padding(.vertical, 6)
141+
.background(copiedBackground)
142+
.foregroundColor(.whiteText)
143+
.clipShape(RoundedRectangle(cornerRadius: 8, style: .continuous))
144+
}
145+
}
146+
)
147+
}
148+
.padding(.horizontal, 60)
149+
.padding(.top, 20)
150+
.padding(.bottom, 32)
151+
152+
Spacer()
153+
154+
// Share key button
155+
DashButton(
156+
text: NSLocalizedString("Share key", comment: ""),
157+
style: .tintedBlue,
158+
action: { showShareSheet = true }
159+
)
160+
.padding(.horizontal, 60)
161+
.padding(.bottom, 20)
162+
}
163+
.background(Color.secondaryBackground)
164+
.sheet(isPresented: $showShareSheet) {
165+
if !viewModel.keyValue.isEmpty {
166+
ShareSheet(items: [viewModel.keyValue])
167+
}
168+
}
169+
}
170+
171+
@ViewBuilder
172+
private var copiedBackground: some View {
173+
if colorScheme == .dark {
174+
ZStack {
175+
BackgroundBlurView()
176+
Color.whiteAlpha15
177+
}
178+
} else {
179+
Color.black.opacity(0.8)
180+
}
181+
}
182+
}
183+
184+
// MARK: - Preview
185+
186+
#Preview {
187+
ExtendedPublicKeySheet()
188+
}

DashWallet/Sources/UI/Menu/Tools/Masternode Keys/DerivationPathKeys/DerivationPathKeysView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ struct DerivationPathKeysContentView: View {
128128

129129
// MARK: - BackgroundBlurView
130130

131-
private struct BackgroundBlurView: UIViewRepresentable {
131+
struct BackgroundBlurView: UIViewRepresentable {
132132
func makeUIView(context: Context) -> UIVisualEffectView {
133133
UIVisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterialDark))
134134
}

DashWallet/Sources/UI/Menu/Tools/ToolsMenuScreen.swift

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ struct ToolsMenuScreen: View {
2929
@State private var showCSVExportActivity = false
3030
@State private var showZenLedgerSheet = false
3131
@State private var showImportPrivateKeySheet = false
32+
@State private var showExtendedPublicKeySheet = false
3233
@State private var scannerDelegate: QRScannerDelegate? = nil
3334

3435
init(vc: UINavigationController, onImportPrivateKey: @escaping () -> ()) {
@@ -160,6 +161,18 @@ struct ToolsMenuScreen: View {
160161
ImportPrivateKeySheet(onScanPrivateKey: presentScanner)
161162
}
162163
}
164+
.sheet(isPresented: $showExtendedPublicKeySheet) {
165+
if #available(iOS 16.4, *) {
166+
ExtendedPublicKeySheet()
167+
.presentationDetents([.height(640)])
168+
.presentationCornerRadius(32)
169+
} else if #available(iOS 16.0, *) {
170+
ExtendedPublicKeySheet()
171+
.presentationDetents([.height(640)])
172+
} else {
173+
ExtendedPublicKeySheet()
174+
}
175+
}
163176
}
164177

165178
private func handleNavigation(_ destination: ToolsMenuNavigationDestination?) {
@@ -189,9 +202,7 @@ struct ToolsMenuScreen: View {
189202
}
190203

191204
private func showExtendedPublicKeys() {
192-
let controller = ExtendedPublicKeysViewController()
193-
controller.hidesBottomBarWhenPushed = true
194-
vc.pushViewController(controller, animated: true)
205+
showExtendedPublicKeySheet = true
195206
}
196207

197208
private func showMasternodeKeys() {

DashWallet/Sources/UI/Menu/Tools/ToolsMenuViewModel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class ToolsMenuViewModel: ObservableObject {
4848
}
4949
),
5050
MenuItemModel(
51-
title: NSLocalizedString("Extended Public Keys", comment: ""),
51+
title: NSLocalizedString("Extended public key (BIP44)", comment: ""),
5252
icon: .custom("image.extend.public.key", maxHeight: 22),
5353
action: { [weak self] in
5454
self?.navigationDestination = .extendedPublicKeys

DashWallet/Sources/UI/SwiftUI Components/Button.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ struct DashButton: View {
3333
case outlined
3434
case filled
3535
case filledBlue
36+
case tintedBlue
3637
}
3738

3839
enum Size {
@@ -113,6 +114,8 @@ struct DashButton: View {
113114
return overridenBackgroundColor ?? Color.dashBlue
114115
case .filledBlue:
115116
return Color.blue
117+
case .tintedBlue:
118+
return Color.blueAlpha5
116119
default:
117120
return Color.clear
118121
}
@@ -133,6 +136,8 @@ struct DashButton: View {
133136
return Color.white
134137
case .filledBlue:
135138
return Color.white
139+
case .tintedBlue:
140+
return Color.blue
136141
default:
137142
return Color.primaryText
138143
}

DashWallet/Sources/UI/SwiftUI Components/Color+DWStyle.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ extension Color {
6464
Color("SystemYellowColor")
6565
}
6666

67+
// Blue
68+
69+
static var blueAlpha5: Color {
70+
Color("BlueAlpha5")
71+
}
72+
6773
// Black
6874

6975
static var blackAlpha5: Color {

0 commit comments

Comments
 (0)