mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 23:32:41 +00:00
Send the Signup analytics event (#6118)
* Implement the Signup event from AuthenticationViewController. Use AuthenticationFlow instead of MXKAuthenticationType. Create new AuthenticationType that aligns with AnalyticsEvent naming. Add additional cases from AnalyticsEvents.
This commit is contained in:
parent
f81bd5d656
commit
54a4feb0ea
21 changed files with 232 additions and 54 deletions
|
@ -183,7 +183,7 @@ EXTERNAL SOURCES:
|
|||
|
||||
CHECKOUT OPTIONS:
|
||||
AnalyticsEvents:
|
||||
:commit: f37a2f243270bffdcddfa5cbaeb4379e0db581c2
|
||||
:commit: b275ccb194a219a61b3100159d51cadbf7c9020c
|
||||
:git: https://github.com/matrix-org/matrix-analytics-events.git
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
|
|
|
@ -226,6 +226,13 @@ extension Analytics {
|
|||
client.updateUserProperties(userProperties)
|
||||
}
|
||||
|
||||
/// Track the registration of a new user.
|
||||
/// - Parameter authenticationType: The type of authentication that was used.
|
||||
func trackSignup(authenticationType: AnalyticsEvent.Signup.AuthenticationType) {
|
||||
let event = AnalyticsEvent.Signup(authenticationType: authenticationType)
|
||||
capture(event: event)
|
||||
}
|
||||
|
||||
/// Track the presentation of a screen
|
||||
/// - Parameters:
|
||||
/// - screen: The screen that was shown.
|
||||
|
|
|
@ -26,6 +26,7 @@ import AnalyticsEvents
|
|||
case slashCommand
|
||||
case spaceHierarchy
|
||||
case timeline
|
||||
case permalink
|
||||
|
||||
var trigger: AnalyticsEvent.JoinedRoom.Trigger? {
|
||||
switch self {
|
||||
|
@ -45,6 +46,8 @@ import AnalyticsEvents
|
|||
return .SpaceHierarchy
|
||||
case .timeline:
|
||||
return .Timeline
|
||||
case .permalink:
|
||||
return .MobilePermalink
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,11 @@ import AnalyticsEvents
|
|||
case inCall
|
||||
case spaceMenu
|
||||
case spaceSettings
|
||||
case roomPreview
|
||||
case permalink
|
||||
case linkShare
|
||||
case exploreRooms
|
||||
case spaceMembers
|
||||
|
||||
var trigger: AnalyticsEvent.ViewRoom.Trigger? {
|
||||
switch self {
|
||||
|
@ -84,6 +89,16 @@ import AnalyticsEvents
|
|||
return .MobileSpaceMenu
|
||||
case .spaceSettings:
|
||||
return .MobileSpaceSettings
|
||||
case .roomPreview:
|
||||
return .MobileRoomPreview
|
||||
case .permalink:
|
||||
return .MobilePermalink
|
||||
case .linkShare:
|
||||
return .MobileLinkShare
|
||||
case .exploreRooms:
|
||||
return .MobileExploreRooms
|
||||
case .spaceMembers:
|
||||
return .MobileSpaceMembers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import Foundation
|
|||
enum AuthenticationCoordinatorResult {
|
||||
/// The user has authenticated but key verification is yet to happen. The session value is
|
||||
/// for a fresh session that still needs to load, sync etc before being ready.
|
||||
case didLogin(session: MXSession, authenticationType: MXKAuthenticationType)
|
||||
case didLogin(session: MXSession, authenticationFlow: AuthenticationFlow, authenticationType: AuthenticationType)
|
||||
/// All of the required authentication steps including key verification is complete.
|
||||
case didComplete
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ protocol AuthenticationCoordinatorProtocol: Coordinator, Presentable {
|
|||
var customServerFieldsVisible: Bool { get set }
|
||||
|
||||
/// Update the screen to display registration or login.
|
||||
func update(authenticationType: MXKAuthenticationType)
|
||||
func update(authenticationFlow: AuthenticationFlow)
|
||||
|
||||
/// Force a registration process based on a predefined set of parameters from a server provisioning link.
|
||||
/// For more information see `AuthenticationViewController.externalRegistrationParameters`.
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#import "MatrixKit.h"
|
||||
|
||||
@protocol AuthenticationViewControllerDelegate;
|
||||
@class SSOIdentityProvider;
|
||||
|
||||
|
||||
@interface AuthenticationViewController : MXKAuthenticationViewController <MXKAuthenticationViewControllerDelegate>
|
||||
|
@ -60,8 +61,16 @@
|
|||
|
||||
@protocol AuthenticationViewControllerDelegate <NSObject>
|
||||
|
||||
- (void)authenticationViewController:(AuthenticationViewController *)authenticationViewController
|
||||
/**
|
||||
Notifies the delegate that authentication has succeeded.
|
||||
@param authenticationViewController The view controller that handled the authentication.
|
||||
@param session The session for the authenticated account.
|
||||
@param password Optional password used for authentication (to be handed to the verification flow).
|
||||
@param identityProvider Optional SSO identity provider used for authentication.
|
||||
*/
|
||||
- (void)authenticationViewController:(AuthenticationViewController * _Nonnull)authenticationViewController
|
||||
didLoginWithSession:(MXSession *)session
|
||||
andPassword:(NSString *)password;
|
||||
andPassword:(NSString * _Nullable)password
|
||||
orSSOIdentityProvider:(SSOIdentityProvider * _Nullable)identityProvider;
|
||||
|
||||
@end;
|
||||
|
|
|
@ -76,6 +76,10 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0;
|
|||
|
||||
// Current SSO transaction id used to identify and validate the SSO authentication callback
|
||||
@property (nonatomic, strong) NSString *ssoCallbackTxnId;
|
||||
/**
|
||||
The SSO provider that was used to successfully complete login, otherwise `nil`.
|
||||
*/
|
||||
@property (nonatomic, readwrite, nullable) SSOIdentityProvider *ssoIdentityProvider;
|
||||
|
||||
@property (nonatomic, getter = isFirstViewAppearing) BOOL firstViewAppearing;
|
||||
|
||||
|
@ -1351,7 +1355,10 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0;
|
|||
}
|
||||
|
||||
// Ask the coordinator to show the loading spinner whilst waiting.
|
||||
[self.authVCDelegate authenticationViewController:self didLoginWithSession:session andPassword:self.authInputsView.password];
|
||||
[self.authVCDelegate authenticationViewController:self
|
||||
didLoginWithSession:session
|
||||
andPassword:self.authInputsView.password
|
||||
orSSOIdentityProvider:self.ssoIdentityProvider];
|
||||
}
|
||||
|
||||
#pragma mark - MXKAuthInputsViewDelegate
|
||||
|
@ -1590,14 +1597,14 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0;
|
|||
|
||||
#pragma mark - SocialLoginListViewDelegate
|
||||
|
||||
- (void)socialLoginListView:(SocialLoginListView *)socialLoginListView didTapSocialButtonWithIdentifier:(NSString *)identifier
|
||||
- (void)socialLoginListView:(SocialLoginListView *)socialLoginListView didTapSocialButtonWithProvider:(SSOIdentityProvider *)identityProvider
|
||||
{
|
||||
[self presentSSOAuthenticationForIdentityProviderIdentifier:identifier];
|
||||
[self presentSSOAuthenticationForIdentityProvider:identityProvider];
|
||||
}
|
||||
|
||||
#pragma mark - SSOIdentityProviderAuthenticationPresenter
|
||||
|
||||
- (void)presentSSOAuthenticationForIdentityProviderIdentifier:(NSString*)identityProviderIdentifier
|
||||
- (void)presentSSOAuthenticationForIdentityProvider:(SSOIdentityProvider*)identityProvider
|
||||
{
|
||||
NSString *homeServerStringURL = self.homeServerTextField.text;
|
||||
|
||||
|
@ -1615,7 +1622,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0;
|
|||
// Generate a unique identifier that will identify the success callback URL
|
||||
NSString *transactionId = [MXTools generateTransactionId];
|
||||
|
||||
[presenter presentForIdentityProviderIdentifier:identityProviderIdentifier with: transactionId from:self animated:YES];
|
||||
[presenter presentForIdentityProvider:identityProvider with: transactionId from:self animated:YES];
|
||||
|
||||
self.ssoCallbackTxnId = transactionId;
|
||||
self.ssoAuthenticationPresenter = presenter;
|
||||
|
@ -1623,7 +1630,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0;
|
|||
|
||||
- (void)presentDefaultSSOAuthentication
|
||||
{
|
||||
[self presentSSOAuthenticationForIdentityProviderIdentifier:nil];
|
||||
[self presentSSOAuthenticationForIdentityProvider:nil];
|
||||
}
|
||||
|
||||
- (void)dismissSSOAuthenticationPresenter
|
||||
|
@ -1656,8 +1663,11 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0;
|
|||
[self.errorPresenter presentErrorFromViewController:self forError:error animated:YES handler:nil];
|
||||
}
|
||||
|
||||
- (void)ssoAuthenticationPresenter:(SSOAuthenticationPresenter *)presenter authenticationSucceededWithToken:(NSString *)token
|
||||
- (void)ssoAuthenticationPresenter:(SSOAuthenticationPresenter *)presenter
|
||||
authenticationSucceededWithToken:(NSString *)token
|
||||
usingIdentityProvider:(SSOIdentityProvider * _Nullable)identityProvider
|
||||
{
|
||||
self.ssoIdentityProvider = identityProvider;
|
||||
[self dismissSSOAuthenticationPresenter];
|
||||
[self loginWithToken:token];
|
||||
}
|
||||
|
|
|
@ -81,8 +81,8 @@ final class LegacyAuthenticationCoordinator: NSObject, AuthenticationCoordinator
|
|||
return self.authenticationViewController
|
||||
}
|
||||
|
||||
func update(authenticationType: MXKAuthenticationType) {
|
||||
authenticationViewController.authType = authenticationType
|
||||
func update(authenticationFlow: AuthenticationFlow) {
|
||||
authenticationViewController.authType = authenticationFlow.mxkType
|
||||
}
|
||||
|
||||
func update(externalRegistrationParameters: [AnyHashable: Any]) {
|
||||
|
@ -149,7 +149,16 @@ final class LegacyAuthenticationCoordinator: NSObject, AuthenticationCoordinator
|
|||
|
||||
// MARK: - AuthenticationViewControllerDelegate
|
||||
extension LegacyAuthenticationCoordinator: AuthenticationViewControllerDelegate {
|
||||
func authenticationViewController(_ authenticationViewController: AuthenticationViewController!, didLoginWith session: MXSession!, andPassword password: String!) {
|
||||
func authenticationViewController(_ authenticationViewController: AuthenticationViewController,
|
||||
didLoginWith session: MXSession!,
|
||||
andPassword password: String?,
|
||||
orSSOIdentityProvider identityProvider: SSOIdentityProvider?) {
|
||||
// Sanity check
|
||||
guard let session = session else {
|
||||
MXLog.failure("[LegacyAuthenticationCoordinator] authenticationViewController(_:didLoginWith:) The MXSession should not be nil.")
|
||||
return
|
||||
}
|
||||
|
||||
self.session = session
|
||||
|
||||
if canPresentAdditionalScreens {
|
||||
|
@ -177,8 +186,18 @@ extension LegacyAuthenticationCoordinator: AuthenticationViewControllerDelegate
|
|||
verificationListener.start()
|
||||
self.verificationListener = verificationListener
|
||||
|
||||
|
||||
completion?(.didLogin(session: session, authenticationType: authenticationViewController.authType))
|
||||
let authenticationType: AuthenticationType
|
||||
if let identityProvider = identityProvider {
|
||||
authenticationType = .sso(identityProvider)
|
||||
} else if !password.isEmptyOrNil {
|
||||
authenticationType = .password
|
||||
} else {
|
||||
authenticationType = .other
|
||||
}
|
||||
|
||||
completion?(.didLogin(session: session,
|
||||
authenticationFlow: authenticationViewController.authType.flow,
|
||||
authenticationType: authenticationType))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,3 +229,29 @@ extension LegacyAuthenticationCoordinator: UIAdaptivePresentationControllerDeleg
|
|||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fileprivate extension AuthenticationFlow {
|
||||
var mxkType: MXKAuthenticationType {
|
||||
switch self {
|
||||
case .login:
|
||||
return .login
|
||||
case .register:
|
||||
return .register
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension MXKAuthenticationType {
|
||||
var flow: AuthenticationFlow {
|
||||
switch self {
|
||||
case .register:
|
||||
return .register
|
||||
case .login, .forgotPassword:
|
||||
return .login
|
||||
@unknown default:
|
||||
MXLog.failure("[MXKAuthenticationType] Unknown type exposed to Swift.")
|
||||
return .login
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,9 @@ import SafariServices
|
|||
@objc protocol SSOAuthenticationPresenterDelegate {
|
||||
func ssoAuthenticationPresenterDidCancel(_ presenter: SSOAuthenticationPresenter)
|
||||
func ssoAuthenticationPresenter(_ presenter: SSOAuthenticationPresenter, authenticationDidFailWithError error: Error)
|
||||
func ssoAuthenticationPresenter(_ presenter: SSOAuthenticationPresenter, authenticationSucceededWithToken token: String)
|
||||
func ssoAuthenticationPresenter(_ presenter: SSOAuthenticationPresenter,
|
||||
authenticationSucceededWithToken token: String,
|
||||
usingIdentityProvider identityProvider: SSOIdentityProvider?)
|
||||
}
|
||||
|
||||
enum SSOAuthenticationPresenterError: Error {
|
||||
|
@ -46,6 +48,7 @@ final class SSOAuthenticationPresenter: NSObject {
|
|||
|
||||
// MARK: Public
|
||||
|
||||
private(set) var identityProvider: SSOIdentityProvider?
|
||||
weak var delegate: SSOAuthenticationPresenterDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
@ -57,15 +60,16 @@ final class SSOAuthenticationPresenter: NSObject {
|
|||
|
||||
// MARK: - Public
|
||||
|
||||
func present(forIdentityProviderIdentifier identityProviderIdentifier: String?,
|
||||
func present(forIdentityProvider identityProvider: SSOIdentityProvider?,
|
||||
with transactionId: String,
|
||||
from presentingViewController: UIViewController,
|
||||
animated: Bool) {
|
||||
guard let authenticationURL = self.ssoAuthenticationService.authenticationURL(for: identityProviderIdentifier, transactionId: transactionId) else {
|
||||
guard let authenticationURL = self.ssoAuthenticationService.authenticationURL(for: identityProvider?.id, transactionId: transactionId) else {
|
||||
self.delegate?.ssoAuthenticationPresenter(self, authenticationDidFailWithError: SSOAuthenticationPresenterError.failToLoadAuthenticationURL)
|
||||
return
|
||||
}
|
||||
|
||||
self.identityProvider = identityProvider
|
||||
self.presentingViewController = presentingViewController
|
||||
|
||||
// NOTE: By using SFAuthenticationSession the consent alert show product name instead of display name. Fallback to SFSafariViewController instead in order to not disturb users with "Riot" wording at the moment.
|
||||
|
@ -130,7 +134,7 @@ final class SSOAuthenticationPresenter: NSObject {
|
|||
}
|
||||
} else if let successURL = callBackURL {
|
||||
if let loginToken = self.ssoAuthenticationService.loginToken(from: successURL) {
|
||||
self.delegate?.ssoAuthenticationPresenter(self, authenticationSucceededWithToken: loginToken)
|
||||
self.delegate?.ssoAuthenticationPresenter(self, authenticationSucceededWithToken: loginToken, usingIdentityProvider: self.identityProvider)
|
||||
} else {
|
||||
MXLog.debug("SSOAuthenticationPresenter: Login token not found")
|
||||
self.delegate?.ssoAuthenticationPresenter(self, authenticationDidFailWithError: SSOAuthenticationServiceError.tokenNotFound)
|
||||
|
|
|
@ -42,7 +42,10 @@ final class SocialLoginButton: UIButton, Themable {
|
|||
// MARK: Public
|
||||
|
||||
var identifier: String? {
|
||||
return self.viewData?.identifier
|
||||
viewData?.identityProvider.id
|
||||
}
|
||||
var identityProvider: SSOIdentityProvider? {
|
||||
viewData?.identityProvider
|
||||
}
|
||||
|
||||
// MARK: Setup
|
||||
|
|
|
@ -71,7 +71,7 @@ class SocialLoginButtonFactory {
|
|||
|
||||
let title = self.buildButtonTitle(with: identityProvider.name, mode: mode)
|
||||
|
||||
let viewData = SocialLoginButtonViewData(identifier: identityProvider.identifier,
|
||||
let viewData = SocialLoginButtonViewData(identityProvider: identityProvider.ssoIdentityProvider,
|
||||
title: title,
|
||||
defaultStyle: defaultStyle,
|
||||
themeStyles: styles)
|
||||
|
|
|
@ -19,8 +19,8 @@ import Foundation
|
|||
/// SocialLoginButton view data
|
||||
struct SocialLoginButtonViewData {
|
||||
|
||||
/// Identify provider identifier
|
||||
let identifier: String
|
||||
/// Identity provider identifier
|
||||
let identityProvider: SSOIdentityProvider
|
||||
|
||||
/// Button title
|
||||
let title: String
|
||||
|
|
|
@ -18,7 +18,7 @@ import UIKit
|
|||
import Reusable
|
||||
|
||||
@objc protocol SocialLoginListViewDelegate: AnyObject {
|
||||
func socialLoginListView(_ socialLoginListView: SocialLoginListView, didTapSocialButtonWithIdentifier identifier: String)
|
||||
func socialLoginListView(_ socialLoginListView: SocialLoginListView, didTapSocialButtonWithProvider identityProvider: SSOIdentityProvider)
|
||||
}
|
||||
|
||||
/// SocialLoginListView displays a list of social login buttons according to a given array of SSO Identity Providers.
|
||||
|
@ -139,10 +139,10 @@ final class SocialLoginListView: UIView, NibLoadable {
|
|||
// MARK: - Action
|
||||
|
||||
@objc private func socialButtonAction(_ socialLoginButton: SocialLoginButton) {
|
||||
guard let identifier = socialLoginButton.identifier else {
|
||||
guard let provider = socialLoginButton.identityProvider else {
|
||||
return
|
||||
}
|
||||
self.delegate?.socialLoginListView(self, didTapSocialButtonWithIdentifier: identifier)
|
||||
self.delegate?.socialLoginListView(self, didTapSocialButtonWithProvider: provider)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc
|
|||
func start() {
|
||||
Task {
|
||||
do {
|
||||
let flow: AuthenticationFlow = initialScreen == .login ? .login : .registration
|
||||
let flow: AuthenticationFlow = initialScreen == .login ? .login : .register
|
||||
try await authenticationService.startFlow(flow, for: authenticationService.state.homeserver.address)
|
||||
} catch {
|
||||
MXLog.error("[AuthenticationCoordinator] start: Failed to start")
|
||||
|
@ -211,7 +211,7 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc
|
|||
func handleRegistrationResult(_ result: RegistrationResult) {
|
||||
switch result {
|
||||
case .success(let mxSession):
|
||||
onSessionCreated(session: mxSession, isAccountCreated: true)
|
||||
onSessionCreated(session: mxSession, flow: .register)
|
||||
case .flowResponse(let flowResult):
|
||||
// TODO
|
||||
break
|
||||
|
@ -219,7 +219,7 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc
|
|||
}
|
||||
|
||||
/// Handles the creation of a new session following on from a successful authentication.
|
||||
func onSessionCreated(session: MXSession, isAccountCreated: Bool) {
|
||||
func onSessionCreated(session: MXSession, flow: AuthenticationFlow) {
|
||||
self.session = session
|
||||
// self.password = password
|
||||
|
||||
|
@ -249,7 +249,8 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc
|
|||
verificationListener.start()
|
||||
self.verificationListener = verificationListener
|
||||
|
||||
completion?(.didLogin(session: session, authenticationType: isAccountCreated ? .register : .login))
|
||||
#warning("Add authentication type to the new flow")
|
||||
completion?(.didLogin(session: session, authenticationFlow: flow, authenticationType: .other))
|
||||
}
|
||||
|
||||
// MARK: - Additional Screens
|
||||
|
@ -331,7 +332,7 @@ extension AuthenticationCoordinator {
|
|||
set { /* no-op */ }
|
||||
}
|
||||
|
||||
func update(authenticationType: MXKAuthenticationType) {
|
||||
func update(authenticationFlow: AuthenticationFlow) {
|
||||
// unused
|
||||
}
|
||||
|
||||
|
|
45
Riot/Modules/Onboarding/AuthenticationType+Analytics.swift
Normal file
45
Riot/Modules/Onboarding/AuthenticationType+Analytics.swift
Normal file
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import AnalyticsEvents
|
||||
|
||||
extension AuthenticationType {
|
||||
var analyticsType: AnalyticsEvent.Signup.AuthenticationType {
|
||||
switch self {
|
||||
case .password:
|
||||
return .Password
|
||||
case .sso(let provider):
|
||||
guard let brandString = provider.brand else { return .SSO }
|
||||
let brand = MXLoginSSOIdentityProviderBrand(brandString)
|
||||
switch brand {
|
||||
case .apple:
|
||||
return .Apple
|
||||
case .facebook:
|
||||
return .Facebook
|
||||
case .github:
|
||||
return .GitHub
|
||||
case .gitlab:
|
||||
return .GitLab
|
||||
case .google:
|
||||
return .Google
|
||||
default:
|
||||
return .SSO
|
||||
}
|
||||
case .other:
|
||||
return .Other
|
||||
}
|
||||
}
|
||||
}
|
|
@ -63,7 +63,10 @@ final class OnboardingCoordinator: NSObject, OnboardingCoordinatorProtocol {
|
|||
// MARK: Screen results
|
||||
private var splashScreenResult: OnboardingSplashScreenViewModelResult?
|
||||
private var useCaseResult: OnboardingUseCaseViewModelResult?
|
||||
private var authenticationType: MXKAuthenticationType?
|
||||
/// The flow being used for authentication.
|
||||
private var authenticationFlow: AuthenticationFlow?
|
||||
/// The type of authentication used to login/register.
|
||||
private var authenticationType: AuthenticationType?
|
||||
private var session: MXSession?
|
||||
/// A place to store the image selected in the avatar screen until it has been saved.
|
||||
private var selectedAvatar: UIImage?
|
||||
|
@ -154,7 +157,7 @@ final class OnboardingCoordinator: NSObject, OnboardingCoordinatorProtocol {
|
|||
splashScreenResult = result
|
||||
|
||||
// Set the auth type early to allow network requests to finish during display of the use case screen.
|
||||
authenticationCoordinator.update(authenticationType: result.mxkAuthenticationType)
|
||||
authenticationCoordinator.update(authenticationFlow: result.flow)
|
||||
|
||||
switch result {
|
||||
case .register:
|
||||
|
@ -219,8 +222,8 @@ final class OnboardingCoordinator: NSObject, OnboardingCoordinatorProtocol {
|
|||
guard let self = self, let coordinator = coordinator else { return }
|
||||
|
||||
switch result {
|
||||
case .didLogin(let session, let authenticationType):
|
||||
self.authenticationCoordinator(coordinator, didLoginWith: session, and: authenticationType)
|
||||
case .didLogin(let session, let authenticationFlow, let authenticationType):
|
||||
self.authenticationCoordinator(coordinator, didLoginWith: session, and: authenticationFlow, using: authenticationType)
|
||||
case .didComplete:
|
||||
self.authenticationCoordinatorDidComplete(coordinator)
|
||||
}
|
||||
|
@ -241,8 +244,8 @@ final class OnboardingCoordinator: NSObject, OnboardingCoordinatorProtocol {
|
|||
guard let self = self, let coordinator = coordinator else { return }
|
||||
|
||||
switch result {
|
||||
case .didLogin(let session, let authenticationType):
|
||||
self.authenticationCoordinator(coordinator, didLoginWith: session, and: authenticationType)
|
||||
case .didLogin(let session, let authenticationFlow, let authenticationType):
|
||||
self.authenticationCoordinator(coordinator, didLoginWith: session, and: authenticationFlow, using: authenticationType)
|
||||
case .didComplete:
|
||||
self.authenticationCoordinatorDidComplete(coordinator)
|
||||
}
|
||||
|
@ -284,13 +287,15 @@ final class OnboardingCoordinator: NSObject, OnboardingCoordinatorProtocol {
|
|||
/// whilst crypto and the rest of the app is launching in the background.
|
||||
private func authenticationCoordinator(_ coordinator: AuthenticationCoordinatorProtocol,
|
||||
didLoginWith session: MXSession,
|
||||
and authenticationType: MXKAuthenticationType) {
|
||||
and authenticationFlow: AuthenticationFlow,
|
||||
using authenticationType: AuthenticationType) {
|
||||
self.session = session
|
||||
self.authenticationFlow = authenticationFlow
|
||||
self.authenticationType = authenticationType
|
||||
|
||||
// Check whether another screen should be shown.
|
||||
if #available(iOS 14.0, *) {
|
||||
if authenticationType == .register,
|
||||
if authenticationFlow == .register,
|
||||
let userId = session.credentials.userId,
|
||||
let userSession = UserSessionsService.shared.userSession(withUserId: userId) {
|
||||
// If personalisation is to be shown, check that the homeserver supports it otherwise show the congratulations screen
|
||||
|
@ -335,7 +340,7 @@ final class OnboardingCoordinator: NSObject, OnboardingCoordinatorProtocol {
|
|||
isShowingLegacyAuthentication = false
|
||||
|
||||
// Handle the chosen use case where applicable
|
||||
if authenticationType == .register,
|
||||
if authenticationFlow == .register,
|
||||
let useCase = useCaseResult?.userSessionPropertyValue,
|
||||
let userSession = UserSessionsService.shared.mainUserSession {
|
||||
// Store the value in the user's session
|
||||
|
@ -559,15 +564,28 @@ final class OnboardingCoordinator: NSObject, OnboardingCoordinatorProtocol {
|
|||
return
|
||||
}
|
||||
|
||||
trackSignup()
|
||||
|
||||
completion?()
|
||||
}
|
||||
|
||||
/// Sends a signup event to the Analytics class if onboarding has completed via the register flow.
|
||||
private func trackSignup() {
|
||||
guard authenticationFlow == .register else { return }
|
||||
guard let authenticationType = authenticationType else {
|
||||
MXLog.warning("[OnboardingCoordinator] sendSignedEvent: Registration finished without collecting an authentication type.")
|
||||
return
|
||||
}
|
||||
|
||||
Analytics.shared.trackSignup(authenticationType: authenticationType.analyticsType)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
extension OnboardingSplashScreenViewModelResult {
|
||||
/// The result converted into the MatrixKit authentication type to use.
|
||||
var mxkAuthenticationType: MXKAuthenticationType {
|
||||
/// The result converted into an authentication flow.
|
||||
var flow: AuthenticationFlow {
|
||||
switch self {
|
||||
case .login:
|
||||
return .login
|
||||
|
|
|
@ -16,10 +16,20 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
/// A value that represents the type of authentication flow being used.
|
||||
/// A value that represents an authentication flow as either login or register.
|
||||
enum AuthenticationFlow {
|
||||
case login
|
||||
case registration
|
||||
case register
|
||||
}
|
||||
|
||||
/// A value that represents the type of authentication used.
|
||||
enum AuthenticationType {
|
||||
/// A username and password.
|
||||
case password
|
||||
/// SSO with the associated provider
|
||||
case sso(SSOIdentityProvider)
|
||||
/// Some other method such as the fall back page.
|
||||
case other
|
||||
}
|
||||
|
||||
/// Errors that can be thrown from `AuthenticationService`.
|
||||
|
@ -65,8 +75,8 @@ enum LoginError: String, Error {
|
|||
}
|
||||
|
||||
/// Represents an SSO Identity Provider as provided in a login flow.
|
||||
struct SSOIdentityProvider: Identifiable {
|
||||
/// The identifier field (id field in JSON) is the Identity Provider identifier used for the SSO Web page redirection `/login/sso/redirect/{idp_id}`.
|
||||
@objc class SSOIdentityProvider: NSObject, Identifiable {
|
||||
/// The id field is the Identity Provider identifier used for the SSO Web page redirection `/login/sso/redirect/{idp_id}`.
|
||||
let id: String
|
||||
/// The name field is a human readable string intended to be printed by the client.
|
||||
let name: String
|
||||
|
@ -74,4 +84,11 @@ struct SSOIdentityProvider: Identifiable {
|
|||
let brand: String?
|
||||
/// The icon field is an optional field that points to an icon representing the identity provider. If present then it must be an HTTPS URL to an image resource.
|
||||
let iconURL: String?
|
||||
|
||||
init(id: String, name: String, brand: String?, iconURL: String?) {
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.brand = brand
|
||||
self.iconURL = iconURL
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ class AuthenticationService: NSObject {
|
|||
let loginWizard = LoginWizard()
|
||||
self.loginWizard = loginWizard
|
||||
|
||||
if flow == .registration {
|
||||
if flow == .register {
|
||||
do {
|
||||
let registrationWizard = RegistrationWizard(client: client)
|
||||
state.homeserver.registrationFlow = try await registrationWizard.registrationFlow()
|
||||
|
@ -124,7 +124,7 @@ class AuthenticationService: NSObject {
|
|||
switch flow {
|
||||
case .login:
|
||||
return client.loginFallbackURL
|
||||
case .registration:
|
||||
case .register:
|
||||
return client.registerFallbackURL
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ final class AuthenticationServerSelectionCoordinator: Coordinator, Presentable {
|
|||
Task {
|
||||
do {
|
||||
#warning("The screen should be configuration for .login too.")
|
||||
try await authenticationService.startFlow(.registration, for: homeserverAddress)
|
||||
try await authenticationService.startFlow(.register, for: homeserverAddress)
|
||||
stopLoading()
|
||||
|
||||
completion?(.updated)
|
||||
|
|
|
@ -39,7 +39,7 @@ class AuthenticationServiceTests: XCTestCase {
|
|||
XCTAssertNil(service.state.homeserver.registrationFlow, "A new service shouldn't provide a registration flow for the homeserver.")
|
||||
|
||||
// When starting a new registration flow.
|
||||
try await service.startFlow(.registration, for: "https://matrix.org")
|
||||
try await service.startFlow(.register, for: "https://matrix.org")
|
||||
|
||||
// Then a registration wizard should be available for use.
|
||||
XCTAssertNotNil(service.registrationWizard, "The registration wizard should exist after starting a registration flow.")
|
||||
|
@ -49,13 +49,13 @@ class AuthenticationServiceTests: XCTestCase {
|
|||
func testReset() async throws {
|
||||
// Given a service that has begun registration.
|
||||
let service = AuthenticationService()
|
||||
try await service.startFlow(.registration, for: "https://matrix.org")
|
||||
try await service.startFlow(.register, for: "https://matrix.org")
|
||||
_ = try await service.registrationWizard?.createAccount(username: UUID().uuidString, password: UUID().uuidString, initialDeviceDisplayName: "Test")
|
||||
XCTAssertNotNil(service.loginWizard, "The login wizard should exist after starting a registration flow.")
|
||||
XCTAssertNotNil(service.registrationWizard, "The registration wizard should exist after starting a registration flow.")
|
||||
XCTAssertNotNil(service.state.homeserver.registrationFlow, "The supported registration flow should be stored after starting a registration flow.")
|
||||
XCTAssertTrue(service.isRegistrationStarted, "The service should show as having started registration.")
|
||||
XCTAssertEqual(service.state.flow, .registration, "The service should show as using a registration flow.")
|
||||
XCTAssertEqual(service.state.flow, .register, "The service should show as using a registration flow.")
|
||||
|
||||
// When resetting the service.
|
||||
service.reset()
|
||||
|
|
1
changelog.d/6074.change
Normal file
1
changelog.d/6074.change
Normal file
|
@ -0,0 +1 @@
|
|||
Authentication: New user accounts are now tracked in analytics if the user opted in.
|
Loading…
Reference in a new issue