mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00

Fixed music playback panel glitches Added ability to display more than 100 message search results Fixed instagram videos playback
330 lines
14 KiB
Swift
330 lines
14 KiB
Swift
import Foundation
|
|
import Display
|
|
import SwiftSignalKit
|
|
import Postbox
|
|
import TelegramCore
|
|
|
|
private final class ChangePhoneNumberCodeControllerArguments {
|
|
let updateEntryText: (String) -> Void
|
|
let next: () -> Void
|
|
|
|
init(updateEntryText: @escaping (String) -> Void, next: @escaping () -> Void) {
|
|
self.updateEntryText = updateEntryText
|
|
self.next = next
|
|
}
|
|
}
|
|
|
|
private enum ChangePhoneNumberCodeSection: Int32 {
|
|
case code
|
|
}
|
|
|
|
private enum ChangePhoneNumberCodeTag: ItemListItemTag {
|
|
case input
|
|
|
|
func isEqual(to other: ItemListItemTag) -> Bool {
|
|
if let other = other as? ChangePhoneNumberCodeTag {
|
|
switch self {
|
|
case .input:
|
|
if case .input = other {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
private enum ChangePhoneNumberCodeEntry: ItemListNodeEntry {
|
|
case codeEntry(PresentationTheme, String)
|
|
case codeInfo(PresentationTheme, String)
|
|
|
|
var section: ItemListSectionId {
|
|
return ChangePhoneNumberCodeSection.code.rawValue
|
|
}
|
|
|
|
var stableId: Int32 {
|
|
switch self {
|
|
case .codeEntry:
|
|
return 1
|
|
case .codeInfo:
|
|
return 2
|
|
}
|
|
}
|
|
|
|
static func ==(lhs: ChangePhoneNumberCodeEntry, rhs: ChangePhoneNumberCodeEntry) -> Bool {
|
|
switch lhs {
|
|
case let .codeEntry(lhsTheme, lhsText):
|
|
if case let .codeEntry(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .codeInfo(lhsTheme, lhsText):
|
|
if case let .codeInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
static func <(lhs: ChangePhoneNumberCodeEntry, rhs: ChangePhoneNumberCodeEntry) -> Bool {
|
|
return lhs.stableId < rhs.stableId
|
|
}
|
|
|
|
func item(_ arguments: ChangePhoneNumberCodeControllerArguments) -> ListViewItem {
|
|
switch self {
|
|
case let .codeEntry(theme, text):
|
|
return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(string: "Code", textColor: .black), text: text, placeholder: "", type: .number, spacing: 10.0, tag: ChangePhoneNumberCodeTag.input, sectionId: self.section, textUpdated: { updatedText in
|
|
arguments.updateEntryText(updatedText)
|
|
}, action: {
|
|
arguments.next()
|
|
})
|
|
case let .codeInfo(theme, text):
|
|
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct ChangePhoneNumberCodeControllerState: Equatable {
|
|
let codeText: String
|
|
let checking: Bool
|
|
|
|
init(codeText: String, checking: Bool) {
|
|
self.codeText = codeText
|
|
self.checking = checking
|
|
}
|
|
|
|
static func ==(lhs: ChangePhoneNumberCodeControllerState, rhs: ChangePhoneNumberCodeControllerState) -> Bool {
|
|
if lhs.codeText != rhs.codeText {
|
|
return false
|
|
}
|
|
if lhs.checking != rhs.checking {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func withUpdatedCodeText(_ codeText: String) -> ChangePhoneNumberCodeControllerState {
|
|
return ChangePhoneNumberCodeControllerState(codeText: codeText, checking: self.checking)
|
|
}
|
|
|
|
func withUpdatedChecking(_ checking: Bool) -> ChangePhoneNumberCodeControllerState {
|
|
return ChangePhoneNumberCodeControllerState(codeText: self.codeText, checking: checking)
|
|
}
|
|
|
|
func withUpdatedNextMethodTimeout(_ nextMethodTimeout: Int32?) -> ChangePhoneNumberCodeControllerState {
|
|
return ChangePhoneNumberCodeControllerState(codeText: self.codeText, checking: self.checking)
|
|
}
|
|
|
|
func withUpdatedCodeData(_ codeData: ChangeAccountPhoneNumberData) -> ChangePhoneNumberCodeControllerState {
|
|
return ChangePhoneNumberCodeControllerState(codeText: self.codeText, checking: self.checking)
|
|
}
|
|
}
|
|
|
|
private func changePhoneNumberCodeControllerEntries(presentationData: PresentationData, state: ChangePhoneNumberCodeControllerState, codeData: ChangeAccountPhoneNumberData, timeout: Int32?, strings: PresentationStrings, theme: AuthorizationTheme) -> [ChangePhoneNumberCodeEntry] {
|
|
var entries: [ChangePhoneNumberCodeEntry] = []
|
|
|
|
entries.append(.codeEntry(presentationData.theme, state.codeText))
|
|
var text = authorizationCurrentOptionText(codeData.type, strings: presentationData.strings, primaryColor: presentationData.theme.list.itemPrimaryTextColor, accentColor: presentationData.theme.list.itemAccentColor).string
|
|
if let nextType = codeData.nextType {
|
|
text += "\n\n" + authorizationNextOptionText(currentType: codeData.type, nextType: nextType, timeout: timeout, strings: presentationData.strings, primaryColor: .black, accentColor: .black).0.string
|
|
}
|
|
entries.append(.codeInfo(presentationData.theme, text))
|
|
|
|
return entries
|
|
}
|
|
|
|
private func timeoutSignal(codeData: ChangeAccountPhoneNumberData) -> Signal<Int32?, NoError> {
|
|
if let _ = codeData.nextType, let timeout = codeData.timeout {
|
|
return Signal { subscriber in
|
|
let value = Atomic<Int32>(value: timeout)
|
|
subscriber.putNext(timeout)
|
|
|
|
let timer = SwiftSignalKit.Timer(timeout: 1.0, repeat: true, completion: {
|
|
subscriber.putNext(value.modify { value in
|
|
return max(0, value - 1)
|
|
})
|
|
}, queue: Queue.mainQueue())
|
|
timer.start()
|
|
|
|
return ActionDisposable {
|
|
timer.invalidate()
|
|
}
|
|
}
|
|
} else {
|
|
return .single(nil)
|
|
}
|
|
}
|
|
|
|
protocol ChangePhoneNumberCodeController: class {
|
|
func applyCode(_ code: Int)
|
|
}
|
|
|
|
private final class ChangePhoneNumberCodeControllerImpl: ItemListController<ChangePhoneNumberCodeEntry>, ChangePhoneNumberCodeController {
|
|
private let applyCodeImpl: (Int) -> Void
|
|
|
|
init(account: Account, state: Signal<(ItemListControllerState, (ItemListNodeState<ChangePhoneNumberCodeEntry>, ChangePhoneNumberCodeEntry.ItemGenerationArguments)), NoError>, applyCodeImpl: @escaping (Int) -> Void) {
|
|
self.applyCodeImpl = applyCodeImpl
|
|
|
|
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
|
super.init(theme: presentationData.theme, strings: presentationData.strings, updatedPresentationData: account.telegramApplicationContext.presentationData |> map { ($0.theme, $0.strings) }, state: state, tabBarItem: nil)
|
|
}
|
|
|
|
required init(coder aDecoder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
func applyCode(_ code: Int) {
|
|
self.applyCodeImpl(code)
|
|
}
|
|
}
|
|
|
|
func changePhoneNumberCodeController(account: Account, phoneNumber: String, codeData: ChangeAccountPhoneNumberData) -> ViewController {
|
|
let initialState = ChangePhoneNumberCodeControllerState(codeText: "", checking: false)
|
|
|
|
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
|
let stateValue = Atomic(value: initialState)
|
|
let updateState: ((ChangePhoneNumberCodeControllerState) -> ChangePhoneNumberCodeControllerState) -> Void = { f in
|
|
statePromise.set(stateValue.modify { f($0) })
|
|
}
|
|
|
|
var dismissImpl: (() -> Void)?
|
|
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments) -> Void)?
|
|
|
|
let actionsDisposable = DisposableSet()
|
|
|
|
let changePhoneDisposable = MetaDisposable()
|
|
actionsDisposable.add(changePhoneDisposable)
|
|
|
|
let nextTypeDisposable = MetaDisposable()
|
|
actionsDisposable.add(nextTypeDisposable)
|
|
|
|
let currentDataPromise = Promise<ChangeAccountPhoneNumberData>()
|
|
currentDataPromise.set(.single(codeData))
|
|
|
|
let timeout = Promise<Int32?>()
|
|
timeout.set(timeoutSignal(codeData: codeData))
|
|
|
|
let resendCode = currentDataPromise.get()
|
|
|> mapToSignal { [weak currentDataPromise] data -> Signal<Void, NoError> in
|
|
if let _ = data.nextType {
|
|
return timeout.get()
|
|
|> filter { $0 == 0 }
|
|
|> take(1)
|
|
|> mapToSignal { _ -> Signal<Void, NoError> in
|
|
return Signal { subscriber in
|
|
return requestNextChangeAccountPhoneNumberVerification(account: account, phoneNumber: phoneNumber, phoneCodeHash: data.hash).start(next: { next in
|
|
currentDataPromise?.set(.single(next))
|
|
}, error: { error in
|
|
|
|
})
|
|
}
|
|
}
|
|
} else {
|
|
return .complete()
|
|
}
|
|
}
|
|
nextTypeDisposable.set(resendCode.start())
|
|
|
|
let checkCode: () -> Void = {
|
|
var code: String?
|
|
updateState { state in
|
|
if state.checking || state.codeText.isEmpty {
|
|
return state
|
|
} else {
|
|
code = state.codeText
|
|
return state.withUpdatedChecking(true)
|
|
}
|
|
}
|
|
if let code = code {
|
|
changePhoneDisposable.set((requestChangeAccountPhoneNumber(account: account, phoneNumber: phoneNumber, phoneCodeHash: codeData.hash, phoneCode: code) |> deliverOnMainQueue).start(error: { error in
|
|
updateState {
|
|
return $0.withUpdatedChecking(false)
|
|
}
|
|
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
|
let alertText: String
|
|
switch error {
|
|
case .generic:
|
|
alertText = presentationData.strings.Login_UnknownError
|
|
case .invalidCode:
|
|
alertText = presentationData.strings.Login_InvalidCodeError
|
|
case .codeExpired:
|
|
alertText = presentationData.strings.Login_CodeExpiredError
|
|
case .limitExceeded:
|
|
alertText = presentationData.strings.Login_CodeFloodError
|
|
}
|
|
presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: alertText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
}, completed: {
|
|
updateState {
|
|
return $0.withUpdatedChecking(false)
|
|
}
|
|
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
|
presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: "You have changed your phone number to \(formatPhoneNumber(phoneNumber)).", actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
dismissImpl?()
|
|
}))
|
|
}
|
|
}
|
|
|
|
let arguments = ChangePhoneNumberCodeControllerArguments(updateEntryText: { updatedText in
|
|
var initiateCheck = false
|
|
updateState { state in
|
|
if state.codeText.count < 5 && updatedText.count == 5 {
|
|
initiateCheck = true
|
|
}
|
|
return state.withUpdatedCodeText(updatedText)
|
|
}
|
|
if initiateCheck {
|
|
checkCode()
|
|
}
|
|
}, next: {
|
|
checkCode()
|
|
})
|
|
|
|
let signal = combineLatest((account.applicationContext as! TelegramApplicationContext).presentationData, statePromise.get() |> deliverOnMainQueue, currentDataPromise.get() |> deliverOnMainQueue, timeout.get() |> deliverOnMainQueue)
|
|
|> deliverOnMainQueue
|
|
|> map { presentationData, state, data, timeout -> (ItemListControllerState, (ItemListNodeState<ChangePhoneNumberCodeEntry>, ChangePhoneNumberCodeEntry.ItemGenerationArguments)) in
|
|
var rightNavigationButton: ItemListNavigationButton?
|
|
if state.checking {
|
|
rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {})
|
|
} else {
|
|
var nextEnabled = true
|
|
if state.codeText.isEmpty {
|
|
nextEnabled = false
|
|
}
|
|
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Next), style: .bold, enabled: nextEnabled, action: {
|
|
checkCode()
|
|
})
|
|
}
|
|
|
|
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(formatPhoneNumber(phoneNumber)), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
|
let listState = ItemListNodeState(entries: changePhoneNumberCodeControllerEntries(presentationData: presentationData, state: state, codeData: data, timeout: timeout, strings: presentationData.strings, theme: defaultLightAuthorizationTheme), style: .blocks, focusItemTag: ChangePhoneNumberCodeTag.input, emptyStateItem: nil, animateChanges: false)
|
|
|
|
return (controllerState, (listState, arguments))
|
|
} |> afterDisposed {
|
|
actionsDisposable.dispose()
|
|
}
|
|
|
|
let controller = ChangePhoneNumberCodeControllerImpl(account: account, state: signal, applyCodeImpl: { code in
|
|
updateState { state in
|
|
return state.withUpdatedCodeText("\(code)")
|
|
}
|
|
checkCode()
|
|
})
|
|
|
|
presentControllerImpl = { [weak controller] c, p in
|
|
if let controller = controller {
|
|
controller.present(c, in: .window(.root), with: p)
|
|
}
|
|
}
|
|
dismissImpl = { [weak controller] in
|
|
(controller?.navigationController as? NavigationController)?.popToRoot(animated: true)
|
|
}
|
|
|
|
return controller
|
|
}
|