chromium/ios/chrome/browser/policy/ui_bundled/idle/idle_timeout_confirmation_mediator.mm

// 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/policy/ui_bundled/idle/idle_timeout_confirmation_mediator.h"

#import "base/timer/timer.h"
#import "ios/chrome/browser/policy/ui_bundled/idle/idle_timeout_cofirmation_presenter.h"

@implementation IdleTimeoutConfirmationMediator {
  // Repeating timer for updating the countdown every 1 second.
  base::RepeatingTimer _updateTimer;
  // Timer for dialog expiry.
  base::OneShotTimer _dialogTimer;
  // Time when the dialog is expected to expire.
  base::TimeTicks _deadline;
  // The time the dialog needs to be shown for. This is needed in case the
  // dialog needs to continue being displayed on a different scene after the
  // scene displaying the dialog is closed and not just minimized.
  base::TimeDelta _dialogDuration;

  // Presenter of the confirmation dialog. The presenter needs to stop showing
  // the dialog when it times out.
  id<IdleTimeoutConfirmationPresenter> _presenter;
}

#pragma mark - ConfirmationAlertActionHandler

- (void)confirmationAlertPrimaryAction {
  // Stop all timers and trigger the presenter to dismiss the dialog.
  [self stop];
  [_presenter stopPresentingAfterUserClickedContinue];
}

#pragma mark - IdleTimeoutConfirmationConsumer

- (void)setConsumer:(id<IdleTimeoutConfirmationConsumer>)consumer {
  if (_consumer == consumer) {
    return;
  }
  _consumer = consumer;

  __weak __typeof(self) weakSelf = self;
  _dialogTimer.Start(FROM_HERE, _dialogDuration, base::BindOnce(^{
                       [weakSelf OnDialogExpired];
                     }));
  _deadline = base::TimeTicks::Now() + _dialogDuration;
  // Update the dialog with the remaining time before it is presented or else it
  // will be empty when the dialog is first presented.
  [self updateConsumerCountdown];
  _updateTimer.Start(FROM_HERE, base::Seconds(1), base::BindRepeating((^{
                       [weakSelf updateConsumerCountdown];
                     })));
}

#pragma mark - Internal

- (instancetype)initWithPresenter:
                    (id<IdleTimeoutConfirmationPresenter>)presenter
                   dialogDuration:(base::TimeDelta)duration {
  if ((self = [super init])) {
    _presenter = presenter;
    _dialogDuration = duration;
  }
  return self;
}

- (void)updateConsumerCountdown {
  base::TimeDelta delay =
      std::max(base::TimeDelta(), _deadline - base::TimeTicks::Now());
  [_consumer setCountdown:delay];
}

- (void)OnDialogExpired {
  // Stop all timers and trigger the presenter to dismiss the dialog after no
  // user action.
  [self stop];
  [_presenter stopPresentingAfterDialogExpired];
}

- (void)stop {
  // No-op if timers are already stopped.
  _updateTimer.Stop();
  _dialogTimer.Stop();
}

@end