-
Notifications
You must be signed in to change notification settings - Fork 19
feat(crowdnode): Linking New Online account [NMI-917] #506
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(crowdnode): Linking New Online account [NMI-917] #506
Conversation
…e-new-online-account
…e-new-online-account
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 | ||
} |
There was a problem hiding this comment.
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.
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 | ||
} |
There was a problem hiding this comment.
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.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
@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 | ||
} |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this 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.
@@ -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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great!
Issue being fixed or feature implemented
What was done?
How Has This Been Tested?
QA
Breaking Changes
Checklist:
For repository code-owners and collaborators only