Skip to content

Conversation

Syn-McJ
Copy link
Member

@Syn-McJ Syn-McJ commented Feb 14, 2023

Issue being fixed or feature implemented

  • If the user had created an account using the blockchain API, they still might want to create an online account for an additional recovery option.

What was done?

  • UI for Email input screen
  • Signing Email using the private key of the reserved address
  • Tracking Email status for the new online account

How Has This Been Tested?

QA

Breaking Changes

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

@Syn-McJ Syn-McJ requested a review from tikhop February 14, 2023 05:33
@Syn-McJ Syn-McJ self-assigned this Feb 14, 2023
Comment on lines +293 to +312
func signAndSendEmail(email: String) async throws -> Bool {
guard !crowdNode.accountAddress.isEmpty else { return false }
emailForAccount = email

let wallet = DWEnvironment.sharedInstance().currentWallet
let result = await wallet.seed(withPrompt: NSLocalizedString("Sign the message", comment: "CrowdNode"), forAmount: 1)

if !result.1 {
let key = wallet.privateKey(forAddress: crowdNode.accountAddress, fromSeed: result.0!)
let signResult = await key?.signMessageDigest(email.magicDigest())

if signResult?.0 == true {
let signature = (signResult!.1 as NSData).base64String()
try await crowdNode.registerEmailForAccount(email: email, signature: signature)
return true
}
}

return false
}
Copy link
Member Author

@Syn-McJ Syn-McJ Feb 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We sign the email that we send with the private key of our account address - that's how CrowdNode links those two together.

Comment on lines +106 to +122
if let email = email, let url = navigationAction.request.url?.absoluteString {
let fullSuffix = "\(kSignupSuffix)&loginHint=\(email)"

if (url.hasPrefix(loginPrefix) && !url.hasSuffix(fullSuffix) && !url.hasSuffix(kCallbackSuffix)) {
let redirectUrl = "\(url)\(fullSuffix)"
let urlRequest = URLRequest(url: URL(string: redirectUrl)!)
webView.load(urlRequest)
previousUrl = url

return (WKNavigationActionPolicy.cancel, preferences)
} else if (previousUrl.hasPrefix(loginPrefix) && url.hasPrefix(accountPrefix)) {
// Successful signup
viewModel.finishSignUpToOnlineAccount()
}

previousUrl = url
}
Copy link
Member Author

@Syn-McJ Syn-McJ Feb 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to manually track webview transition to the profile screen to detect successful signup.

Comment on lines +105 to +111
let backButton = UIButton(type: .custom)
backButton.frame = .init(x: 0, y: 0, width: 30, height: 30)
backButton.setImage(UIImage(systemName: "arrow.backward"), for: .normal)
backButton.tintColor = .white
backButton.imageEdgeInsets = .init(top: 0, left: -10, bottom: 0, right: 0)
backButton.addTarget(self, action: #selector(backButtonAction), for: .touchUpInside)
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backButton)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the only way to fix the back button and make it white is to recreate it here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can also change the tintColor of the 'original' back button:

navigationItem.leftBarButtonItem!.customView!.tintColor = .white

This solution is better, but there is a problem that we can only do this at viewDidAppear which doesn't look good. Let's keep your implementation for now and I will improve BaseNavigationController and add extra property into NavigationBarDisplayable protocol, like backButtonTintColor and we will be able to change the back button color per view controller without adding a custom button.

Comment on lines 91 to 96
@objc func onEditingBegin() {
let path = UIBezierPath(roundedRect: self.layer.bounds.inset(by: UIEdgeInsets(top: -2, left: -2, bottom: -2, right: -2)), cornerRadius: 14)
outerBorder.path = path.cgPath
self.layer.addSublayer(outerBorder)
self.layer.borderColor = UIColor.dw_dashBlue().cgColor
}
Copy link
Member Author

@Syn-McJ Syn-McJ Feb 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured I'll keep the external border as a sublayer - that way we can implement the focused, unfocused, and error states according to the design and have it all contained in a single control for reusability sake.
https://www.figma.com/file/azdJACb5WmivxYVhB5q46F/Design-system%E3%83%BBAndroid?node-id=3014%3A14441&t=K4J99XOQ6jVwlIWC-0

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Syn-McJ Sound good — this approach works fine as well. The only thing is that we should not use onEditingBegin to calculate the rect for the path, this could be done within layoutSubviews or drawRect methods:

override func layoutSubviews() {
    super.layoutSubviews()

    let path = UIBezierPath(roundedRect: bounds.inset(by: UIEdgeInsets(top: -2, left: -2, bottom: -2, right: -2)), cornerRadius: 14)
    outerBorder.path = path.cgPath
}
    

To change the visibility of outerBorder layer, I suggest just to toggle isHidden flag, when onEditingBegin or onEditingEnd is being invoked, instead of adding/removing for the view stack.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed


private func waitForApiAddressConfirmation(primaryAddress: String, apiAddress: String) async -> DSTransaction {
private func waitForApiAddressConfirmation(primaryAddress: String, apiAddress: String) async -> (tx: DSTransaction, didWait: Bool) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Syn-McJ I'm not sure I understand this change, why do we need to return a flag from async method that shows whether it was invoked synchronously or not in order to check availability of confirmation. I think, it's better to keep it as it was and has some additional flag that tells whether we need to invoke notifyIfNeeded.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tikhop the problem is that after the wallet reset / the network is changed, we don't have a good way to determine if we need to show this notification or not. Any other flag that we keep will be erased.

The boolean here isn't about how the method was invoked, but whether we had to wait for the confirmation or not. If we waited - means the confirmation was still in process and we need to inform the user that it's finished. If not - means the confirmation was already done and the user has already received the notification.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Syn-McJ I would keep it simple and use the flag instead. If user resets the wallet, the flag is also reseted then it's fine to show notification once again.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Showing the notification again is the issue that was raised during QA and that we're trying to fix :)
Specifically, the user would see "Your account is confirmed" every time the network is changed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can probably do this with a flag but in a slightly different way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, you have one flag for all networks and if I try to create on mainnet and then switch on testnet you need to reset the flag as well, otherwise you can't display a notification if we created an account once on a different network. We can keep it as it is, but I think a better solution is to have a different flag per network since you can have two accounts with different networks.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't sound like "keep it simple" to me :)
Anyway, I'll do the flag solution and trigger it when the user initiates the confirmation. Then we'll only show the notification if the flag is set.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@@ -62,6 +62,8 @@ public class CrowdNodeObjcWrapper: NSObject {

private let kValidStatus = "valid"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Syn-McJ I see that we are passing the status as a string, but since we have a predefined statuses it would be better to have a string enum.

Copy link
Member Author

@Syn-McJ Syn-McJ Feb 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OnlineAccountState and these statuses are different - we have the first to keep the state of the app, while these are just constants to check which response had CrowdNode API returned.

I don't think we should mix them since that could be confusing.

Copy link
Contributor

@tikhop tikhop left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good except a few things.

@Syn-McJ Syn-McJ requested a review from tikhop February 14, 2023 13:25
@@ -758,11 +800,40 @@ extension CrowdNode {
notifyIfNeeded(message: NSLocalizedString("Your CrowdNode address has been validated, but verification is required.", comment: "CrowdNode"))
} else if status.lowercased() == kConfirmedStatus {
changeOnlineState(to: .done)
notifyIfNeeded(message: NSLocalizedString("Your CrowdNode address has been confirmed.", comment: "CrowdNode"))

if prefs.shouldShowConfirmedNotification {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I forgot about this case, so there was probably a bug with the previous solution.

Copy link
Contributor

@tikhop tikhop left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

@Syn-McJ Syn-McJ merged commit f2093ea into dashpay:feature/crowdnode Feb 15, 2023
@Syn-McJ Syn-McJ deleted the feature/crowdnode-new-online-account branch August 3, 2023 13:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants