// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ui/authentication/history_sync/history_sync_popup_coordinator.h"
#import <UIKit/UIKit.h>
#import "base/memory/raw_ptr.h"
#import "base/metrics/user_metrics.h"
#import "components/signin/public/base/signin_metrics.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/signin/model/authentication_service.h"
#import "ios/chrome/browser/signin/model/authentication_service_factory.h"
#import "ios/chrome/browser/sync/model/sync_service_factory.h"
#import "ios/chrome/browser/ui/authentication/authentication_ui_util.h"
#import "ios/chrome/browser/ui/authentication/history_sync/history_sync_coordinator.h"
@interface HistorySyncPopupCoordinator () <
HistorySyncCoordinatorDelegate,
UIAdaptivePresentationControllerDelegate>
@end
@implementation HistorySyncPopupCoordinator {
// Authentication service.
raw_ptr<AuthenticationService> _authenticationService;
// Coordinator to display the tangible sync view.
HistorySyncCoordinator* _historySyncCoordinator;
// Navigation controller created for the popup.
UINavigationController* _navigationController;
// Whether the account email should be shown in the footer.
// Should be `NO` if the user has seen the account info by signing in just
// before seeing the history opt-in screen.
BOOL _showUserEmail;
// `YES` if the user should be signed-out if history sync is declined. It
// should be done for entry points dedicated to history sync instead of
// sign-in.
BOOL _signOutIfDeclined;
// Whether the History Sync screen is a optional step, that can be skipped
// if declined too often.
BOOL _isOptional;
// Access point associated with the history opt-in screen.
signin_metrics::AccessPoint _accessPoint;
}
- (instancetype)initWithBaseViewController:(UIViewController*)viewController
browser:(Browser*)browser
showUserEmail:(BOOL)showUserEmail
signOutIfDeclined:(BOOL)signOutIfDeclined
isOptional:(BOOL)isOptional
accessPoint:
(signin_metrics::AccessPoint)accessPoint {
self = [super initWithBaseViewController:viewController browser:browser];
if (self) {
_showUserEmail = showUserEmail;
_signOutIfDeclined = signOutIfDeclined;
_isOptional = isOptional;
_accessPoint = accessPoint;
}
return self;
}
- (void)start {
[super start];
ChromeBrowserState* browserState = self.browser->GetBrowserState();
CHECK_EQ(browserState, browserState->GetOriginalChromeBrowserState());
_authenticationService =
AuthenticationServiceFactory::GetForBrowserState(browserState);
syncer::SyncService* syncService =
SyncServiceFactory::GetForBrowserState(browserState);
// Check if History Sync Opt-In should be skipped.
HistorySyncSkipReason skipReason = [HistorySyncCoordinator
getHistorySyncOptInSkipReason:syncService
authenticationService:_authenticationService
prefService:browserState->GetPrefs()
isHistorySyncOptional:_isOptional];
if (skipReason != HistorySyncSkipReason::kNone) {
[HistorySyncCoordinator recordHistorySyncSkipMetric:skipReason
accessPoint:_accessPoint];
[self.delegate historySyncPopupCoordinator:self
didFinishWithResult:SigninCoordinatorResultDisabled];
return;
}
_navigationController =
[[UINavigationController alloc] initWithNavigationBarClass:nil
toolbarClass:nil];
_navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
_navigationController.presentationController.delegate = self;
_historySyncCoordinator = [[HistorySyncCoordinator alloc]
initWithBaseNavigationController:_navigationController
browser:self.browser
delegate:self
firstRun:NO
showUserEmail:_showUserEmail
isOptional:_isOptional
accessPoint:_accessPoint];
[_historySyncCoordinator start];
[_navigationController setNavigationBarHidden:YES animated:NO];
[self.baseViewController presentViewController:_navigationController
animated:YES
completion:nil];
}
- (void)dealloc {
// TODO(crbug.com/40272467)
DUMP_WILL_BE_CHECK(!_historySyncCoordinator);
}
- (void)stop {
[_historySyncCoordinator stop];
_historySyncCoordinator = nil;
_navigationController.presentationController.delegate = nil;
[_navigationController dismissViewControllerAnimated:NO completion:nil];
_navigationController = nil;
[super stop];
}
#pragma mark - InterruptibleChromeCoordinator
- (void)interruptWithAction:(SigninCoordinatorInterrupt)action
completion:(ProceduralBlock)completion {
__weak __typeof(self) weakSelf = self;
ProceduralBlock dismissCompletion = ^() {
[weakSelf viewWasDismissedWithResult:SigninCoordinatorResultInterrupted];
if (completion) {
completion();
}
};
switch (action) {
case SigninCoordinatorInterrupt::DismissWithAnimation:
[_navigationController dismissViewControllerAnimated:YES
completion:dismissCompletion];
break;
case SigninCoordinatorInterrupt::DismissWithoutAnimation:
[_navigationController dismissViewControllerAnimated:NO
completion:dismissCompletion];
break;
case SigninCoordinatorInterrupt::UIShutdownNoDismiss:
// The view should be ignored and leave it being presented.
_navigationController.presentationController.delegate = nil;
_navigationController = nil;
// This coordinator is now done, and its owner can now stop it.
[self.delegate
historySyncPopupCoordinator:self
didFinishWithResult:SigninCoordinatorResultInterrupted];
if (completion) {
completion();
}
break;
}
}
#pragma mark - Private
- (void)viewWasDismissedWithResult:(SigninCoordinatorResult)result {
_navigationController.presentationController.delegate = nil;
_navigationController = nil;
if (result != SigninCoordinatorResultSuccess && _signOutIfDeclined) {
_authenticationService->SignOut(
signin_metrics::ProfileSignout::
kUserDeclinedHistorySyncAfterDedicatedSignIn,
/*force_clear_browsing_data=*/false, nil);
}
[self.delegate historySyncPopupCoordinator:self didFinishWithResult:result];
}
#pragma mark - HistorySyncCoordinatorDelegate
- (void)closeHistorySyncCoordinator:
(HistorySyncCoordinator*)historySyncCoordinator
declinedByUser:(BOOL)declined {
CHECK(_navigationController);
[_historySyncCoordinator stop];
_historySyncCoordinator = nil;
SigninCoordinatorResult result = declined
? SigninCoordinatorResultCanceledByUser
: SigninCoordinatorResultSuccess;
__weak __typeof(self) weakSelf = self;
[_navigationController
dismissViewControllerAnimated:YES
completion:^() {
[weakSelf viewWasDismissedWithResult:result];
}];
}
#pragma mark - UIAdaptivePresentationControllerDelegate
- (void)presentationControllerDidDismiss:
(UIPresentationController*)presentationController {
// This should be triggered only if user dismisses the screen manually.
base::RecordAction(base::UserMetricsAction("Signin_HistorySync_SwipedDown"));
[_historySyncCoordinator stop];
_historySyncCoordinator = nil;
_navigationController.presentationController.delegate = nil;
_navigationController = nil;
[self viewWasDismissedWithResult:SigninCoordinatorResultCanceledByUser];
}
#pragma mark - NSObject
- (NSString*)description {
return [NSString
stringWithFormat:
@"<%@: %p, authenticationService: %p, historySyncCoordinator: %@, "
@"presented: %@, accessPoint: %d>",
self.class.description, self, _authenticationService.get(),
_historySyncCoordinator,
ViewControllerPresentationStatusDescription(_navigationController),
static_cast<int>(_accessPoint)];
}
@end