Skip to content

Commit

Permalink
Merge pull request #1606 from Automattic/lantean/1605-invalid-link-alert
Browse files Browse the repository at this point in the history
Invalid Authentication Link Alert
  • Loading branch information
jleandroperez committed Jul 8, 2024
2 parents a679f88 + eb62589 commit 2614941
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 4 deletions.
4 changes: 4 additions & 0 deletions Simplenote.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@
B56AA00022AEDD06003F85CB /* PlainTextExtractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56A9FFF22AEDD06003F85CB /* PlainTextExtractor.swift */; };
B56AA00622B03CBB003F85CB /* Extractors.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56AA00522B03CBB003F85CB /* Extractors.swift */; };
B56B357F1AC3565600B9F365 /* UITextView+Simplenote.m in Sources */ = {isa = PBXBuildFile; fileRef = B56B357D1AC3565600B9F365 /* UITextView+Simplenote.m */; };
B56BAF612C2DE317005065C9 /* MagicLinkInvalidView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56BAF602C2DE317005065C9 /* MagicLinkInvalidView.swift */; };
B56C8CA2234CEAF100FE55F4 /* TagListViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B56C8CA1234CEAF100FE55F4 /* TagListViewController.xib */; };
B56E763422BD394C00C5AA47 /* UIImage+Simplenote.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56E763322BD394C00C5AA47 /* UIImage+Simplenote.swift */; };
B56E763622BD565700C5AA47 /* UIView+Simplenote.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56E763522BD565700C5AA47 /* UIView+Simplenote.swift */; };
Expand Down Expand Up @@ -1038,6 +1039,7 @@
B56AA00522B03CBB003F85CB /* Extractors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extractors.swift; sourceTree = "<group>"; };
B56B357C1AC3565600B9F365 /* UITextView+Simplenote.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UITextView+Simplenote.h"; path = "Classes/UITextView+Simplenote.h"; sourceTree = "<group>"; };
B56B357D1AC3565600B9F365 /* UITextView+Simplenote.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UITextView+Simplenote.m"; path = "Classes/UITextView+Simplenote.m"; sourceTree = "<group>"; };
B56BAF602C2DE317005065C9 /* MagicLinkInvalidView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MagicLinkInvalidView.swift; sourceTree = "<group>"; };
B56C8CA1234CEAF100FE55F4 /* TagListViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = TagListViewController.xib; path = Classes/TagListViewController.xib; sourceTree = "<group>"; };
B56E763322BD394C00C5AA47 /* UIImage+Simplenote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "UIImage+Simplenote.swift"; path = "Classes/UIImage+Simplenote.swift"; sourceTree = "<group>"; };
B56E763522BD565700C5AA47 /* UIView+Simplenote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "UIView+Simplenote.swift"; path = "Classes/UIView+Simplenote.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2082,6 +2084,7 @@
B5AB169922FA128000B4EBA5 /* SPSheetController.xib */,
A6CDD9C725F0163D00E0BC4D /* MagicLinkAuthenticator.swift */,
B54A11C32C136225002AC8AA /* MagicLinkRequestedView.swift */,
B56BAF602C2DE317005065C9 /* MagicLinkInvalidView.swift */,
A628BEB425ECD97900121B64 /* SignupVerificationViewController.swift */,
A628BEB525ECD97900121B64 /* SignupVerificationViewController.xib */,
);
Expand Down Expand Up @@ -3455,6 +3458,7 @@
E215C793180B115C00AD36B5 /* SPNavigationController.m in Sources */,
B5DF734422A56E2800602CE7 /* UserDefaults+Simplenote.swift in Sources */,
A6DB2C1525BF5D6E00437B99 /* SPMarkdownPreviewViewController+Extensions.swift in Sources */,
B56BAF612C2DE317005065C9 /* MagicLinkInvalidView.swift in Sources */,
B56A696022F9D53400B90398 /* SPAuthHandler.swift in Sources */,
A6E1E78924B5196C008A44BC /* SPNoteHistoryControllerDelegate.swift in Sources */,
B5B9AB4522EE8AF0001CB0AD /* UIImageName.swift in Sources */,
Expand Down
16 changes: 15 additions & 1 deletion Simplenote/Classes/MagicLinkAuthenticator.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import Foundation


// MARK: - Notifications
//
extension NSNotification.Name {
static let magicLinkAuthWillStart = NSNotification.Name("magicLinkAuthWillStart")
static let magicLinkAuthDidSucceed = NSNotification.Name("magicLinkAuthDidSucceed")
static let magicLinkAuthDidFail = NSNotification.Name("magicLinkAuthDidFail")
}


// MARK: - MagicLinkAuthenticator
//
struct MagicLinkAuthenticator {
Expand Down Expand Up @@ -48,7 +58,8 @@ private extension MagicLinkAuthenticator {
}

NSLog("[MagicLinkAuthenticator] Requesting SyncToken for \(authKey) and \(authCode)")

NotificationCenter.default.post(name: .magicLinkAuthWillStart, object: nil)

Task {
do {
let remote = LoginRemote()
Expand All @@ -57,11 +68,14 @@ private extension MagicLinkAuthenticator {
Task { @MainActor in
NSLog("[MagicLinkAuthenticator] Should auth with token \(confirmation.syncToken)")
authenticator.authenticate(withUsername: confirmation.username, token: confirmation.syncToken)

NotificationCenter.default.post(name: .magicLinkAuthDidSucceed, object: nil)
SPTracker.trackUserConfirmedLoginLink()
}

} catch {
NSLog("[MagicLinkAuthenticator] Magic Link TokenExchange Error: \(error)")
NotificationCenter.default.post(name: .magicLinkAuthDidFail, object: error)
}
}

Expand Down
2 changes: 1 addition & 1 deletion Simplenote/Classes/SPAuthViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,7 @@ extension SPAuthViewController {

// MARK: - AuthenticationMode: Signup / Login
//
struct AuthenticationMode {
struct AuthenticationMode: Equatable {
let title: String
let validationStyle: AuthenticationValidator.Style
let primaryActionSelector: Selector
Expand Down
47 changes: 45 additions & 2 deletions Simplenote/Classes/SPOnboardingViewController.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Foundation
import UIKit
import SafariServices
import SwiftUI

// MARK: - SPOnboardingViewController
//
Expand Down Expand Up @@ -196,8 +197,10 @@ private extension SPOnboardingViewController {

func startListeningToNotifications() {
let name = NSNotification.Name(rawValue: kSignInErrorNotificationName)

NotificationCenter.default.addObserver(self, selector: #selector(handleSignInError), name: name, object: nil)

let nc = NotificationCenter.default
nc.addObserver(self, selector: #selector(handleSignInError), name: name, object: nil)
nc.addObserver(self, selector: #selector(handleMagicLinkAuthDidFail), name: .magicLinkAuthDidFail, object: nil)
}

@objc func handleSignInError(note: Notification) {
Expand All @@ -210,9 +213,49 @@ private extension SPOnboardingViewController {
presentedViewController?.dismiss(animated: true, completion: nil)
present(alertController, animated: true, completion: nil)
}

@objc func handleMagicLinkAuthDidFail() {
DispatchQueue.main.async {
self.presentMagicLinkInvalidView()
}
}
}


// MARK: - Magic Link Helpers
//
private extension SPOnboardingViewController {

/// Presents the Invalid Magic Link UI
///
private func presentMagicLinkInvalidView() {
var rootView = MagicLinkInvalidView()
rootView.onPressRequestNewLink = { [weak self] in
self?.presentAuthenticationInterfaceIfNeeded(mode: .loginWithMagicLink)
}

let hostingController = UIHostingController(rootView: rootView)
hostingController.modalPresentationStyle = .formSheet
hostingController.sheetPresentationController?.detents = [.medium()]

let presenter = presentedViewController ?? self
presenter.present(hostingController, animated: true)
}

/// Dismisses all of the presented ViewControllers, and pushes the Authentication UI with the specified mode.
/// - Note: Whenever the required AuthUI is already onscreen, we'll do nothing
///
func presentAuthenticationInterfaceIfNeeded(mode: AuthenticationMode) {
if let authController = navigationController?.topViewController as? SPAuthViewController, authController.mode == mode {
return
}

navigationController?.popToRootViewController(animated: true)
presentAuthenticationInterface(mode: mode)
}
}


// MARK: - Private Types
//
private struct OnboardingStrings {
Expand Down
92 changes: 92 additions & 0 deletions Simplenote/MagicLinkInvalidView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import Foundation
import SwiftUI
import Gridicons


// MARK: - MagicLinkConfirmationView
//
struct MagicLinkInvalidView: View {
@Environment(\.presentationMode) var presentationMode

var onPressRequestNewLink: (() -> Void)?


var body: some View {
NavigationView {
VStack(alignment: .center, spacing: 10) {
Image(uiImage: MagicLinkImages.cross)
.renderingMode(.template)
.foregroundColor(Color(.simplenoteLightBlueColor))

Text("Invalid Link")
.bold()
.font(.system(size: Metrics.titleFontSize))
.padding()

Text("Your authentication link is no longer valid")
.font(.system(size: Metrics.detailsFontSize))
.multilineTextAlignment(.center)
.padding(.bottom, Metrics.detailsPaddingBottom)

Button(action: pressedRequestNewLink) {
Text("Request a new Link")
.fontWeight(.bold)
.foregroundStyle(.white)
}
.padding()
.background(Color(.simplenoteBlue50Color))
.cornerRadius(Metrics.actionCornerRadius)
.buttonStyle(PlainButtonStyle())

}
.padding()
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
presentationMode.wrappedValue.dismiss()
}) {
Image(uiImage: MagicLinkImages.dismiss)
.renderingMode(.template)
.foregroundColor(Color(.darkGray))
}
}
}
}
.navigationViewStyle(.stack)

/// Force Light Mode (since the Authentication UI is all light!)
.environment(\.colorScheme, .light)
}

func pressedRequestNewLink() {
presentationMode.wrappedValue.dismiss()
onPressRequestNewLink?()
}
}


// MARK: - Constants
//
private enum Metrics {
static let crossIconSize = CGSize(width: 100, height: 100)
static let dismissSize = CGSize(width: 30, height: 30)
static let titleFontSize: CGFloat = 22
static let detailsFontSize: CGFloat = 17
static let detailsPaddingBottom: CGFloat = 30
static let actionCornerRadius: CGFloat = 10
}

private enum MagicLinkImages {
static let cross = Gridicon.iconOfType(.crossCircle, withSize: Metrics.crossIconSize)
static let dismiss = Gridicon.iconOfType(.crossCircle, withSize: Metrics.dismissSize)
}


// MARK: - Preview
//
struct MagicLinkInvalidView_Previews: PreviewProvider {
static var previews: some View {
MagicLinkInvalidView()
}
}

0 comments on commit 2614941

Please sign in to comment.