mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
407 lines
16 KiB
Swift
407 lines
16 KiB
Swift
import Foundation
|
|
import Display
|
|
import SwiftSignalKit
|
|
import Postbox
|
|
import TelegramCore
|
|
|
|
private struct SettingsItemArguments {
|
|
let account: Account
|
|
let accountManager: AccountManager
|
|
|
|
let pushController: (ViewController) -> Void
|
|
let presentController: (ViewController) -> Void
|
|
let updateEditingName: (ItemListAvatarAndNameInfoItemName) -> Void
|
|
let saveEditingState: () -> Void
|
|
let logout: () -> Void
|
|
}
|
|
|
|
private enum SettingsSection: Int32 {
|
|
case info
|
|
case generalSettings
|
|
case accountSettings
|
|
case help
|
|
case logOut
|
|
}
|
|
|
|
private enum SettingsEntry: ItemListNodeEntry {
|
|
case userInfo(Peer?, CachedPeerData?, ItemListAvatarAndNameInfoItemState)
|
|
case setProfilePhoto
|
|
|
|
case notificationsAndSounds
|
|
case privacyAndSecurity
|
|
case dataAndStorage
|
|
case stickers
|
|
case phoneNumber(String)
|
|
case username(String)
|
|
case askAQuestion
|
|
case faq
|
|
case debug
|
|
case logOut
|
|
|
|
var section: ItemListSectionId {
|
|
switch self {
|
|
case .userInfo, .setProfilePhoto:
|
|
return SettingsSection.info.rawValue
|
|
case .notificationsAndSounds, .privacyAndSecurity, .dataAndStorage, .stickers:
|
|
return SettingsSection.generalSettings.rawValue
|
|
case .phoneNumber, .username:
|
|
return SettingsSection.accountSettings.rawValue
|
|
case .askAQuestion, .faq, .debug:
|
|
return SettingsSection.help.rawValue
|
|
case .logOut:
|
|
return SettingsSection.logOut.rawValue
|
|
}
|
|
}
|
|
|
|
var stableId: Int32 {
|
|
switch self {
|
|
case .userInfo:
|
|
return 0
|
|
case .setProfilePhoto:
|
|
return 1
|
|
case .notificationsAndSounds:
|
|
return 2
|
|
case .privacyAndSecurity:
|
|
return 3
|
|
case .dataAndStorage:
|
|
return 4
|
|
case .stickers:
|
|
return 5
|
|
case .phoneNumber:
|
|
return 6
|
|
case .username:
|
|
return 7
|
|
case .askAQuestion:
|
|
return 8
|
|
case .faq:
|
|
return 9
|
|
case .debug:
|
|
return 10
|
|
case .logOut:
|
|
return 11
|
|
}
|
|
}
|
|
|
|
static func ==(lhs: SettingsEntry, rhs: SettingsEntry) -> Bool {
|
|
switch lhs {
|
|
case let .userInfo(lhsPeer, lhsCachedData, lhsEditingState):
|
|
if case let .userInfo(rhsPeer, rhsCachedData, rhsEditingState) = rhs {
|
|
if let lhsPeer = lhsPeer, let rhsPeer = rhsPeer {
|
|
if !lhsPeer.isEqual(rhsPeer) {
|
|
return false
|
|
}
|
|
} else if (lhsPeer != nil) != (rhsPeer != nil) {
|
|
return false
|
|
}
|
|
if let lhsCachedData = lhsCachedData, let rhsCachedData = rhsCachedData {
|
|
if !lhsCachedData.isEqual(to: rhsCachedData) {
|
|
return false
|
|
}
|
|
} else if (lhsCachedData != nil) != (rhsCachedData != nil) {
|
|
return false
|
|
}
|
|
if lhsEditingState != rhsEditingState {
|
|
return false
|
|
}
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case .setProfilePhoto:
|
|
if case .setProfilePhoto = rhs {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case .notificationsAndSounds:
|
|
if case .notificationsAndSounds = rhs {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case .privacyAndSecurity:
|
|
if case .privacyAndSecurity = rhs {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case .dataAndStorage:
|
|
if case .dataAndStorage = rhs {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case .stickers:
|
|
if case .stickers = rhs {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .phoneNumber(number):
|
|
if case .phoneNumber(number) = rhs {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .username(address):
|
|
if case .username(address) = rhs {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case .askAQuestion:
|
|
if case .askAQuestion = rhs {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case .faq:
|
|
if case .faq = rhs {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case .debug:
|
|
if case .debug = rhs {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case .logOut:
|
|
if case .logOut = rhs {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
static func <(lhs: SettingsEntry, rhs: SettingsEntry) -> Bool {
|
|
return lhs.stableId < rhs.stableId
|
|
}
|
|
|
|
func item(_ arguments: SettingsItemArguments) -> ListViewItem {
|
|
switch self {
|
|
case let .userInfo(peer, cachedData, state):
|
|
return ItemListAvatarAndNameInfoItem(account: arguments.account, peer: peer, presence: TelegramUserPresence(status: .present(until: Int32.max)), cachedData: cachedData, state: state, sectionId: ItemListSectionId(self.section), style: .blocks, editingNameUpdated: { editingName in
|
|
arguments.updateEditingName(editingName)
|
|
})
|
|
case .setProfilePhoto:
|
|
return ItemListActionItem(title: "Set Profile Photo", kind: .generic, alignment: .natural, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.presentController(standardTextAlertController(title: "Verification Failed", text: "Your Apple ID or password is incorrect.", actions: [
|
|
TextAlertAction(type: .genericAction, title: "Cancel", action: {
|
|
|
|
}),
|
|
TextAlertAction(type: .defaultAction, title: "OK", action: {
|
|
|
|
})
|
|
]))
|
|
})
|
|
case .notificationsAndSounds:
|
|
return ItemListDisclosureItem(title: "Notifications and Sounds", label: "", sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.pushController(notificationsAndSoundsController(account: arguments.account))
|
|
})
|
|
case .privacyAndSecurity:
|
|
return ItemListDisclosureItem(title: "Privacy and Security", label: "", sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.pushController(privacyAndSecurityController(account: arguments.account))
|
|
})
|
|
case .dataAndStorage:
|
|
return ItemListDisclosureItem(title: "Data and Storage", label: "", sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
|
|
})
|
|
case .stickers:
|
|
return ItemListDisclosureItem(title: "Stickers", label: "", sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
|
|
})
|
|
case let .phoneNumber(number):
|
|
return ItemListDisclosureItem(title: "Phone Number", label: number, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
|
|
})
|
|
case let .username(address):
|
|
return ItemListDisclosureItem(title: "Username", label: address, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.presentController(usernameSetupController(account: arguments.account))
|
|
})
|
|
case .askAQuestion:
|
|
return ItemListDisclosureItem(title: "Ask a Question", label: "", sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
|
|
})
|
|
case .faq:
|
|
return ItemListDisclosureItem(title: "Telegram FAQ", label: "", sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
|
|
})
|
|
case .debug:
|
|
return ItemListDisclosureItem(title: "Debug", label: "", sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.pushController(debugController(account: arguments.account, accountManager: arguments.accountManager))
|
|
})
|
|
case .logOut:
|
|
return ItemListActionItem(title: "Log Out", kind: .destructive, alignment: .center, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.logout()
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct SettingsEditingState: Equatable {
|
|
let editingName: ItemListAvatarAndNameInfoItemName
|
|
|
|
static func ==(lhs: SettingsEditingState, rhs: SettingsEditingState) -> Bool {
|
|
if lhs.editingName != rhs.editingName {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
}
|
|
|
|
private struct SettingsState: Equatable {
|
|
let editingState: SettingsEditingState?
|
|
let updatingName: ItemListAvatarAndNameInfoItemName?
|
|
|
|
func withUpdatedEditingState(_ editingState: SettingsEditingState?) -> SettingsState {
|
|
return SettingsState(editingState: editingState, updatingName: self.updatingName)
|
|
}
|
|
|
|
func withUpdatedUpdatingName(_ updatingName: ItemListAvatarAndNameInfoItemName?) -> SettingsState {
|
|
return SettingsState(editingState: self.editingState, updatingName: updatingName)
|
|
}
|
|
|
|
static func ==(lhs: SettingsState, rhs: SettingsState) -> Bool {
|
|
if lhs.editingState != rhs.editingState {
|
|
return false
|
|
}
|
|
if lhs.updatingName != rhs.updatingName {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
private func settingsEntries(state: SettingsState, view: PeerView) -> [SettingsEntry] {
|
|
var entries: [SettingsEntry] = []
|
|
|
|
if let peer = peerViewMainPeer(view) as? TelegramUser {
|
|
let userInfoState = ItemListAvatarAndNameInfoItemState(editingName: state.editingState?.editingName, updatingName: state.updatingName)
|
|
entries.append(.userInfo(peer, view.cachedData, userInfoState))
|
|
entries.append(.setProfilePhoto)
|
|
|
|
entries.append(.notificationsAndSounds)
|
|
entries.append(.privacyAndSecurity)
|
|
entries.append(.dataAndStorage)
|
|
entries.append(.stickers)
|
|
|
|
if let phone = peer.phone {
|
|
entries.append(.phoneNumber(formatPhoneNumber(phone)))
|
|
}
|
|
entries.append(.username(peer.addressName == nil ? "" : ("@" + peer.addressName!)))
|
|
|
|
entries.append(.askAQuestion)
|
|
entries.append(.faq)
|
|
entries.append(.debug)
|
|
|
|
if let _ = state.editingState {
|
|
entries.append(.logOut)
|
|
}
|
|
}
|
|
|
|
return entries
|
|
}
|
|
|
|
public func settingsController(account: Account, accountManager: AccountManager) -> ViewController {
|
|
let statePromise = ValuePromise(SettingsState(editingState: nil, updatingName: nil), ignoreRepeated: true)
|
|
let stateValue = Atomic(value: SettingsState(editingState: nil, updatingName: nil))
|
|
let updateState: ((SettingsState) -> SettingsState) -> Void = { f in
|
|
statePromise.set(stateValue.modify { f($0) })
|
|
}
|
|
|
|
var pushControllerImpl: ((ViewController) -> Void)?
|
|
var presentControllerImpl: ((ViewController) -> Void)?
|
|
|
|
let actionsDisposable = DisposableSet()
|
|
|
|
let updatePeerNameDisposable = MetaDisposable()
|
|
actionsDisposable.add(updatePeerNameDisposable)
|
|
|
|
let arguments = SettingsItemArguments(account: account, accountManager: accountManager, pushController: { controller in
|
|
pushControllerImpl?(controller)
|
|
}, presentController: { controller in
|
|
presentControllerImpl?(controller)
|
|
}, updateEditingName: { editingName in
|
|
updateState { state in
|
|
if let _ = state.editingState {
|
|
return state.withUpdatedEditingState(SettingsEditingState(editingName: editingName))
|
|
} else {
|
|
return state
|
|
}
|
|
}
|
|
}, saveEditingState: {
|
|
var updateName: ItemListAvatarAndNameInfoItemName?
|
|
updateState { state in
|
|
if let editingState = state.editingState {
|
|
updateName = editingState.editingName
|
|
return state.withUpdatedEditingState(nil).withUpdatedUpdatingName(editingState.editingName)
|
|
} else {
|
|
return state
|
|
}
|
|
}
|
|
if let updateName = updateName, case let .personName(firstName, lastName) = updateName {
|
|
updatePeerNameDisposable.set((updateAccountPeerName(account: account, firstName: firstName, lastName: lastName) |> afterDisposed {
|
|
Queue.mainQueue().async {
|
|
updateState { state in
|
|
return state.withUpdatedUpdatingName(nil)
|
|
}
|
|
}
|
|
}).start())
|
|
}
|
|
}, logout: {
|
|
let alertController = standardTextAlertController(title: NSLocalizedString("Settings.LogoutConfirmationTitle", comment: ""), text: NSLocalizedString("Settings.LogoutConfirmationText", comment: ""), actions: [
|
|
TextAlertAction(type: .genericAction, title: "Cancel", action: {
|
|
}),
|
|
TextAlertAction(type: .defaultAction, title: "OK", action: {
|
|
let _ = logoutFromAccount(id: account.id, accountManager: accountManager).start()
|
|
})
|
|
])
|
|
presentControllerImpl?(alertController)
|
|
})
|
|
|
|
let peerView = account.viewTracker.peerView(account.peerId)
|
|
|
|
let signal = combineLatest(statePromise.get(), peerView)
|
|
|> map { state, view -> (ItemListControllerState, (ItemListNodeState<SettingsEntry>, SettingsEntry.ItemGenerationArguments)) in
|
|
let peer = peerViewMainPeer(view)
|
|
let rightNavigationButton: ItemListNavigationButton
|
|
if let _ = state.editingState {
|
|
rightNavigationButton = ItemListNavigationButton(title: "Done", style: .bold, enabled: true, action: {
|
|
arguments.saveEditingState()
|
|
})
|
|
} else {
|
|
rightNavigationButton = ItemListNavigationButton(title: "Edit", style: .regular, enabled: true, action: {
|
|
if let peer = peer as? TelegramUser {
|
|
updateState { state in
|
|
return state.withUpdatedEditingState(SettingsEditingState(editingName: ItemListAvatarAndNameInfoItemName(peer.indexName)))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
let controllerState = ItemListControllerState(title: "Settings", leftNavigationButton: nil, rightNavigationButton: rightNavigationButton)
|
|
let listState = ItemListNodeState(entries: settingsEntries(state: state, view: view), style: .blocks)
|
|
|
|
return (controllerState, (listState, arguments))
|
|
} |> afterDisposed {
|
|
actionsDisposable.dispose()
|
|
}
|
|
|
|
let controller = ItemListController(signal)
|
|
controller.navigationItem.backBarButtonItem = UIBarButtonItem(title: "Back", style: .plain, target: nil, action: nil)
|
|
controller.tabBarItem.title = "Settings"
|
|
controller.tabBarItem.image = UIImage(bundleImageName: "Chat List/Tabs/IconSettings")?.precomposed()
|
|
controller.tabBarItem.selectedImage = UIImage(bundleImageName: "Chat List/Tabs/IconSettingsSelected")?.precomposed()
|
|
pushControllerImpl = { [weak controller] value in
|
|
(controller?.navigationController as? NavigationController)?.pushViewController(value)
|
|
}
|
|
presentControllerImpl = { [weak controller] value in
|
|
controller?.present(value, in: .window, with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
}
|
|
return controller
|
|
}
|