Skip to content

Commit 1c1d123

Browse files
authored
[trello.com/c/G77vaoVI]: login right after pasting using paste button (#709)
* [trello.com/c/G77vaoVI]: login right after pasting using paste button * [trello.com/c/G77vaoVI]: allow editing after pasting wrong passphrase * [trello.com/c/G77vaoVI]: added auto-login after pasting using cmd+v or native ios textfield popup
1 parent 8a5618d commit 1c1d123

File tree

5 files changed

+150
-11
lines changed

5 files changed

+150
-11
lines changed

Adamant.xcodeproj/project.pbxproj

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -467,8 +467,9 @@
467467
AA33BEB62D303E240083E59C /* APICoreProtocolMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA33BEB52D303DB60083E59C /* APICoreProtocolMock.swift */; };
468468
AA33BEB72D3041A30083E59C /* AddressConverterMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA33BEB42D303CBD0083E59C /* AddressConverterMock.swift */; };
469469
AA33BEB92D3044760083E59C /* BtcApiServiceProtocolMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA33BEB82D30446F0083E59C /* BtcApiServiceProtocolMock.swift */; };
470-
AA8FFFCA2D4E6435001D8576 /* CryptoSwift in Frameworks */ = {isa = PBXBuildFile; productRef = AA8FFFC92D4E6435001D8576 /* CryptoSwift */; };
471-
AA8FFFCC2D50D503001D8576 /* NodeOrigin+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8FFFCB2D50D4F8001D8576 /* NodeOrigin+Extensions.swift */; };
470+
AA7202E32D6B4A5100CCAC20 /* PasteInterceptingPasswordCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA7202E22D6B4A4B00CCAC20 /* PasteInterceptingPasswordCell.swift */; };
471+
AA7202E52D6B4ABB00CCAC20 /* PasteInterceptingTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA7202E42D6B4AAF00CCAC20 /* PasteInterceptingTextField.swift */; };
472+
AA7202E72D6B4B3500CCAC20 /* PasteInterceptingPasswordRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA7202E62D6B4B2E00CCAC20 /* PasteInterceptingPasswordRow.swift */; };
472473
AA8FFFB02D497175001D8576 /* AdmWalletServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8FFFAF2D49716D001D8576 /* AdmWalletServiceTests.swift */; };
473474
AA8FFFB22D49726F001D8576 /* SecuredStoreMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8FFFB12D49726B001D8576 /* SecuredStoreMock.swift */; };
474475
AA8FFFB42D497510001D8576 /* AccountServiceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8FFFB32D497509001D8576 /* AccountServiceMock.swift */; };
@@ -478,6 +479,8 @@
478479
AA8FFFBC2D49796A001D8576 /* ChatsProviderMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8FFFBB2D497966001D8576 /* ChatsProviderMock.swift */; };
479480
AA8FFFC02D4AC011001D8576 /* AdamantCoreMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8FFFBF2D4AC00B001D8576 /* AdamantCoreMock.swift */; };
480481
AA8FFFC42D4C1174001D8576 /* NativeAdamantCoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8FFFC32D4C1168001D8576 /* NativeAdamantCoreTests.swift */; };
482+
AA8FFFCA2D4E6435001D8576 /* CryptoSwift in Frameworks */ = {isa = PBXBuildFile; productRef = AA8FFFC92D4E6435001D8576 /* CryptoSwift */; };
483+
AA8FFFCC2D50D503001D8576 /* NodeOrigin+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8FFFCB2D50D4F8001D8576 /* NodeOrigin+Extensions.swift */; };
481484
AA8FFFE32D513681001D8576 /* CustomFieldRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8FFFE22D513670001D8576 /* CustomFieldRow.swift */; };
482485
AA8FFFE52D513787001D8576 /* EdgeInsetTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8FFFE42D513780001D8576 /* EdgeInsetTextField.swift */; };
483486
AAB01CAD2D3AE44B007D6BF4 /* BitcoinKitTransactionFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB01CAC2D3AE449007D6BF4 /* BitcoinKitTransactionFactory.swift */; };
@@ -1146,7 +1149,9 @@
11461149
AA33BEB42D303CBD0083E59C /* AddressConverterMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressConverterMock.swift; sourceTree = "<group>"; };
11471150
AA33BEB52D303DB60083E59C /* APICoreProtocolMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APICoreProtocolMock.swift; sourceTree = "<group>"; };
11481151
AA33BEB82D30446F0083E59C /* BtcApiServiceProtocolMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BtcApiServiceProtocolMock.swift; sourceTree = "<group>"; };
1149-
AA8FFFCB2D50D4F8001D8576 /* NodeOrigin+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NodeOrigin+Extensions.swift"; sourceTree = "<group>"; };
1152+
AA7202E22D6B4A4B00CCAC20 /* PasteInterceptingPasswordCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteInterceptingPasswordCell.swift; sourceTree = "<group>"; };
1153+
AA7202E42D6B4AAF00CCAC20 /* PasteInterceptingTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteInterceptingTextField.swift; sourceTree = "<group>"; };
1154+
AA7202E62D6B4B2E00CCAC20 /* PasteInterceptingPasswordRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteInterceptingPasswordRow.swift; sourceTree = "<group>"; };
11501155
AA8FFFAF2D49716D001D8576 /* AdmWalletServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdmWalletServiceTests.swift; sourceTree = "<group>"; };
11511156
AA8FFFB12D49726B001D8576 /* SecuredStoreMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecuredStoreMock.swift; sourceTree = "<group>"; };
11521157
AA8FFFB32D497509001D8576 /* AccountServiceMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountServiceMock.swift; sourceTree = "<group>"; };
@@ -1156,6 +1161,7 @@
11561161
AA8FFFBB2D497966001D8576 /* ChatsProviderMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatsProviderMock.swift; sourceTree = "<group>"; };
11571162
AA8FFFBF2D4AC00B001D8576 /* AdamantCoreMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantCoreMock.swift; sourceTree = "<group>"; };
11581163
AA8FFFC32D4C1168001D8576 /* NativeAdamantCoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeAdamantCoreTests.swift; sourceTree = "<group>"; };
1164+
AA8FFFCB2D50D4F8001D8576 /* NodeOrigin+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NodeOrigin+Extensions.swift"; sourceTree = "<group>"; };
11591165
AA8FFFE22D513670001D8576 /* CustomFieldRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomFieldRow.swift; sourceTree = "<group>"; };
11601166
AA8FFFE42D513780001D8576 /* EdgeInsetTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EdgeInsetTextField.swift; sourceTree = "<group>"; };
11611167
AAB01CAC2D3AE449007D6BF4 /* BitcoinKitTransactionFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BitcoinKitTransactionFactory.swift; sourceTree = "<group>"; };
@@ -2601,6 +2607,9 @@
26012607
E913C9101FFFAA4B001A83F7 /* Helpers */ = {
26022608
isa = PBXGroup;
26032609
children = (
2610+
AA7202E62D6B4B2E00CCAC20 /* PasteInterceptingPasswordRow.swift */,
2611+
AA7202E42D6B4AAF00CCAC20 /* PasteInterceptingTextField.swift */,
2612+
AA7202E22D6B4A4B00CCAC20 /* PasteInterceptingPasswordCell.swift */,
26042613
AA8FFFE42D513780001D8576 /* EdgeInsetTextField.swift */,
26052614
AA8FFFE22D513670001D8576 /* CustomFieldRow.swift */,
26062615
93775E452A674FA9009061AC /* Markdown+Adamant.swift */,
@@ -3573,6 +3582,7 @@
35733582
3A4068342ACD7C18007E87BD /* CoinTransaction+TransactionDetails.swift in Sources */,
35743583
E921597B206503000000CA5C /* ButtonsStripeView.swift in Sources */,
35753584
93FC16A12B01DE120062B507 /* ERC20ApiService.swift in Sources */,
3585+
AA7202E72D6B4B3500CCAC20 /* PasteInterceptingPasswordRow.swift in Sources */,
35763586
3A299C692B838AA600B54C61 /* ChatMediaCell.swift in Sources */,
35773587
93294B9A2AAD624100911109 /* WalletFactoryCompose.swift in Sources */,
35783588
41C1698E29E7F36900FEB3CB /* AdamantRichTransactionReplyService.swift in Sources */,
@@ -3764,6 +3774,7 @@
37643774
E9942B87203D9E5100C163AF /* EurekaQRRow.swift in Sources */,
37653775
3ACD307E2BBD86B700ABF671 /* FilesStorageProprietiesService.swift in Sources */,
37663776
3AA50DF32AEBE67C00C58FC8 /* PartnerQRFactory.swift in Sources */,
3777+
AA7202E32D6B4A5100CCAC20 /* PasteInterceptingPasswordCell.swift in Sources */,
37673778
E9AA8C02212C5BF500F9249F /* AdmWalletService+Send.swift in Sources */,
37683779
E90847332196FEA80095825D /* TransferTransaction+CoreDataProperties.swift in Sources */,
37693780
9366588D2B0AB6BD00BDB2D3 /* CoinsNodesListState.swift in Sources */,
@@ -3886,6 +3897,7 @@
38863897
A5E0422B282AB18B0076CD13 /* BtcUnspentTransactionResponse.swift in Sources */,
38873898
E972206B201F44CA004F2AAD /* TransfersProvider.swift in Sources */,
38883899
3A20D93B2AE7F316005475A6 /* AdamantTransactionDetails.swift in Sources */,
3900+
AA7202E52D6B4ABB00CCAC20 /* PasteInterceptingTextField.swift in Sources */,
38893901
93294B962AAD320B00911109 /* ScreensFactory.swift in Sources */,
38903902
3A9015A72A614A62002A2464 /* AdamantEmojiService.swift in Sources */,
38913903
93ADE0722ACA66AF008ED641 /* VibrationSelectionView.swift in Sources */,
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//
2+
// PasteInterceptingPasswordCell.swift
3+
// Adamant
4+
//
5+
// Created by Christian Benua on 23.02.2025.
6+
// Copyright © 2025 Adamant. All rights reserved.
7+
//
8+
9+
import UIKit
10+
import Eureka
11+
12+
final class PasteInterceptingPasswordCell: CustomFieldCell<String, PasteInterceptingTextField>, CellType {
13+
14+
required init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
15+
super.init(style: style, reuseIdentifier: reuseIdentifier)
16+
}
17+
18+
required init?(coder aDecoder: NSCoder) {
19+
super.init(coder: aDecoder)
20+
}
21+
22+
override func setup() {
23+
super.setup()
24+
textField.autocorrectionType = .no
25+
textField.autocapitalizationType = .none
26+
textField.keyboardType = .asciiCapable
27+
textField.isSecureTextEntry = true
28+
textField.textContentType = .password
29+
if let textLabel = textLabel {
30+
textField.setContentHuggingPriority(textLabel.contentHuggingPriority(for: .horizontal) - 1, for: .horizontal)
31+
}
32+
}
33+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//
2+
// PasteInterceptingPasswordRow.swift
3+
// Adamant
4+
//
5+
// Created by Christian Benua on 23.02.2025.
6+
// Copyright © 2025 Adamant. All rights reserved.
7+
//
8+
9+
import Eureka
10+
11+
final class PasteInterceptingPasswordRow: FieldRow<PasteInterceptingPasswordCell>, RowType {
12+
required init(tag: String?) {
13+
super.init(tag: tag)
14+
}
15+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//
2+
// PasteInterceptingTextField.swift
3+
// Adamant
4+
//
5+
// Created by Christian Benua on 23.02.2025.
6+
// Copyright © 2025 Adamant. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
final class PasteInterceptingTextField: UITextField, UITextFieldDelegate {
12+
13+
private var isAfterPaste: Bool = false
14+
var pasteInterceptor: ((String?) -> Void)?
15+
16+
override init(frame: CGRect) {
17+
super.init(frame: frame)
18+
setup()
19+
}
20+
21+
private func setup() {
22+
NotificationCenter.default.addObserver(
23+
self,
24+
selector: #selector(handlePasteNotification),
25+
name: UITextField.textDidChangeNotification,
26+
object: self
27+
)
28+
}
29+
30+
deinit {
31+
NotificationCenter.default.removeObserver(self)
32+
}
33+
34+
required init?(coder: NSCoder) {
35+
fatalError("init(coder:) has not been implemented")
36+
}
37+
38+
override func paste(_ sender: Any?) {
39+
isAfterPaste = true
40+
super.paste(sender)
41+
}
42+
43+
@objc func handlePasteNotification() {
44+
if isAfterPaste {
45+
pasteInterceptor?(text)
46+
}
47+
isAfterPaste = false
48+
}
49+
50+
func textField(
51+
_ textField: UITextField,
52+
shouldChangeCharactersIn range: NSRange,
53+
replacementString string: String
54+
) -> Bool {
55+
if isAfterPaste {
56+
pasteInterceptor?(textField.text)
57+
}
58+
59+
isAfterPaste = false
60+
return true
61+
}
62+
}

Adamant/Modules/Login/LoginViewController.swift

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -226,11 +226,22 @@ final class LoginViewController: FormViewController {
226226
}
227227

228228
// Passphrase row
229-
<<< PasswordRow {
229+
<<< PasteInterceptingPasswordRow {
230230
$0.tag = Rows.passphrase.tag
231231
$0.placeholder = Rows.passphrase.localized
232232
$0.placeholderColor = UIColor.adamant.secondary
233-
$0.cell.textField.enablePasteButtonAndPasswordToggle()
233+
$0.cell._textField.pasteInterceptor = { [weak self] text in
234+
if let text {
235+
self?.loginWith(passphrase: text)
236+
}
237+
}
238+
$0.cell.textField.enablePasteButtonAndPasswordToggle { [weak self] in
239+
// assing text to textfield like this to make loginButton update its enabled/disabled state
240+
let row = self?.form.rowBy(tag: Rows.passphrase.tag) as? PasteInterceptingPasswordRow
241+
row?.value = $0
242+
row?.cell.textField.text = $0
243+
self?.loginWith(passphrase: $0)
244+
}
234245
$0.keyboardReturnType = KeyboardReturnTypeConfiguration(nextKeyboardType: .go, defaultKeyboardType: .go)
235246
}
236247

@@ -550,9 +561,9 @@ extension LoginViewController {
550561
// MARK: UITextField + extensions
551562

552563
private extension UITextField {
553-
func enablePasteButtonAndPasswordToggle() {
564+
func enablePasteButtonAndPasswordToggle(_ pasteButtonHandler: @escaping (String) -> Void) {
554565
let passwordToggleButton = makePasswordButton()
555-
let pasteButton = makePasteButton()
566+
let pasteButton = makePasteButton(pasteButtonHandler)
556567

557568
let containerView = UIView()
558569
let buttonStack = UIStackView(arrangedSubviews: [pasteButton, passwordToggleButton])
@@ -579,17 +590,23 @@ private extension UITextField {
579590
rightViewMode = .always
580591
}
581592

582-
func makePasteButton() -> UIButton {
593+
func makePasteButton(_ handler: @escaping (String) -> Void) -> UIButton {
583594
let button = UIButton(type: .custom)
584595
button.imageEdgeInsets = UITextField.buttonImageEdgeInsets
585596
button.setImage(.asset(named: "clipboard"), for: .normal)
586-
button.addTarget(self, action: #selector(pasteFromPasteboard(_:)), for: .touchUpInside)
597+
button.addAction(
598+
UIAction { [weak self] _ in
599+
self?.pasteFromPasteboard(handler)
600+
},
601+
for: .touchUpInside
602+
)
587603
return button
588604
}
589605

590-
@objc func pasteFromPasteboard(_ sender: UIButton) {
606+
@objc func pasteFromPasteboard(_ handler: @escaping (String) -> Void) {
591607
if let pasteboardText = UIPasteboard.general.string {
592-
self.text = pasteboardText
608+
let newText = String(pasteboardText.prefix(150))
609+
handler(newText)
593610
}
594611
}
595612
}

0 commit comments

Comments
 (0)