Signal-iOS/Signal/Registration/UserInterface/RegistrationPhoneNumberViewState.swift
2024-03-25 13:26:31 -05:00

134 lines
4.1 KiB
Swift

//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import SignalServiceKit
public enum RegistrationPhoneNumberViewState: Equatable {
case registration(RegistrationMode)
case changingNumber(ChangingNumberMode)
public enum RegistrationMode: Equatable {
case initialRegistration(InitialRegistration)
case reregistration(Reregistration)
}
public enum ChangingNumberMode: Equatable {
case initialEntry(ChangeNumberInitialEntry)
case confirmation(ChangeNumberConfirmation)
}
public struct InitialRegistration: Equatable {
/// previouslyEnteredE164 is if the user entered a number, quit, and came back.
/// Will be used to pre-populate the entry field.
let previouslyEnteredE164: E164?
let validationError: ValidationError?
let canExitRegistration: Bool
}
public struct Reregistration: Equatable {
let e164: E164
let validationError: ValidationError?
let canExitRegistration: Bool
}
public struct ChangeNumberInitialEntry: Equatable {
let oldE164: E164
let newE164: E164?
let hasConfirmed: Bool
let invalidNumberError: ValidationError.InvalidNumber?
}
public struct ChangeNumberConfirmation: Equatable {
let oldE164: E164
let newE164: E164
let rateLimitedError: ValidationError.RateLimited?
}
public enum ValidationError: Equatable {
case invalidNumber(InvalidNumber)
case rateLimited(RateLimited)
public struct InvalidNumber: Equatable {
let invalidE164: E164
}
public struct RateLimited: Equatable {
let expiration: Date
let e164: E164
}
}
}
extension RegistrationPhoneNumberViewState.ValidationError {
func canSubmit(e164: E164, dateProvider: DateProvider) -> Bool {
switch self {
case let .invalidNumber(error):
return error.canSubmit(e164: e164)
case let .rateLimited(error):
return error.canSubmit(e164: e164, dateProvider: dateProvider)
}
}
func warningLabelText(e164: E164, dateProvider: DateProvider) -> String? {
switch self {
case .invalidNumber(let error):
return error.warningLabelText(e164: e164)
case let .rateLimited(error):
return error.warningLabelText(e164: e164, dateProvider: dateProvider)
}
}
}
extension RegistrationPhoneNumberViewState.ValidationError.InvalidNumber {
func canSubmit(e164: E164) -> Bool {
return e164 != invalidE164
}
func warningLabelText(e164: E164) -> String? {
guard self.invalidE164 == e164 else {
return nil
}
return OWSLocalizedString(
"ONBOARDING_PHONE_NUMBER_VALIDATION_WARNING",
comment: "Label indicating that the phone number is invalid in the 'onboarding phone number' view."
)
}
}
extension RegistrationPhoneNumberViewState.ValidationError.RateLimited {
func canSubmit(e164: E164, dateProvider: DateProvider) -> Bool {
return dateProvider() >= expiration || e164 != self.e164
}
func warningLabelText(e164: E164, dateProvider: DateProvider) -> String? {
guard e164 == self.e164 else {
return nil
}
let now = dateProvider()
if now >= expiration {
return nil
}
let rateLimitFormat = OWSLocalizedString(
"ONBOARDING_PHONE_NUMBER_RATE_LIMIT_WARNING_FORMAT",
comment: "Label indicating that registration has been ratelimited. Embeds {{remaining time string}}."
)
let retryAfterFormatter: DateFormatter = {
let result = DateFormatter()
result.dateFormat = "m:ss"
result.timeZone = TimeZone(identifier: "UTC")!
return result
}()
let timeRemaining = max(expiration.timeIntervalSince(now), 0)
let durationString = retryAfterFormatter.string(from: Date(timeIntervalSinceReferenceDate: timeRemaining))
return String(format: rateLimitFormat, durationString)
}
}