mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 14:45:21 +00:00
Refactor SettingsUI and related modules
This commit is contained in:
@@ -0,0 +1,759 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import TelegramCallsUI
|
||||
import ItemListUI
|
||||
import AccountContext
|
||||
import TelegramNotices
|
||||
import LocalAuth
|
||||
|
||||
private final class PrivacyAndSecurityControllerArguments {
|
||||
let account: Account
|
||||
let openBlockedUsers: () -> Void
|
||||
let openLastSeenPrivacy: () -> Void
|
||||
let openGroupsPrivacy: () -> Void
|
||||
let openVoiceCallPrivacy: () -> Void
|
||||
let openProfilePhotoPrivacy: () -> Void
|
||||
let openForwardPrivacy: () -> Void
|
||||
let openPhoneNumberPrivacy: () -> Void
|
||||
let openPasscode: () -> Void
|
||||
let openTwoStepVerification: (TwoStepVerificationAccessConfiguration?) -> Void
|
||||
let openActiveSessions: () -> Void
|
||||
let setupAccountAutoremove: () -> Void
|
||||
let openDataSettings: () -> Void
|
||||
|
||||
init(account: Account, openBlockedUsers: @escaping () -> Void, openLastSeenPrivacy: @escaping () -> Void, openGroupsPrivacy: @escaping () -> Void, openVoiceCallPrivacy: @escaping () -> Void, openProfilePhotoPrivacy: @escaping () -> Void, openForwardPrivacy: @escaping () -> Void, openPhoneNumberPrivacy: @escaping () -> Void, openPasscode: @escaping () -> Void, openTwoStepVerification: @escaping (TwoStepVerificationAccessConfiguration?) -> Void, openActiveSessions: @escaping () -> Void, setupAccountAutoremove: @escaping () -> Void, openDataSettings: @escaping () -> Void) {
|
||||
self.account = account
|
||||
self.openBlockedUsers = openBlockedUsers
|
||||
self.openLastSeenPrivacy = openLastSeenPrivacy
|
||||
self.openGroupsPrivacy = openGroupsPrivacy
|
||||
self.openVoiceCallPrivacy = openVoiceCallPrivacy
|
||||
self.openProfilePhotoPrivacy = openProfilePhotoPrivacy
|
||||
self.openForwardPrivacy = openForwardPrivacy
|
||||
self.openPhoneNumberPrivacy = openPhoneNumberPrivacy
|
||||
self.openPasscode = openPasscode
|
||||
self.openTwoStepVerification = openTwoStepVerification
|
||||
self.openActiveSessions = openActiveSessions
|
||||
self.setupAccountAutoremove = setupAccountAutoremove
|
||||
self.openDataSettings = openDataSettings
|
||||
}
|
||||
}
|
||||
|
||||
private enum PrivacyAndSecuritySection: Int32 {
|
||||
case general
|
||||
case privacy
|
||||
case account
|
||||
case dataSettings
|
||||
}
|
||||
|
||||
public enum PrivacyAndSecurityEntryTag: ItemListItemTag {
|
||||
case accountTimeout
|
||||
|
||||
public func isEqual(to other: ItemListItemTag) -> Bool {
|
||||
if let other = other as? PrivacyAndSecurityEntryTag, self == other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
|
||||
case privacyHeader(PresentationTheme, String)
|
||||
case blockedPeers(PresentationTheme, String, String)
|
||||
case phoneNumberPrivacy(PresentationTheme, String, String)
|
||||
case lastSeenPrivacy(PresentationTheme, String, String)
|
||||
case profilePhotoPrivacy(PresentationTheme, String, String)
|
||||
case voiceCallPrivacy(PresentationTheme, String, String)
|
||||
case forwardPrivacy(PresentationTheme, String, String)
|
||||
case groupPrivacy(PresentationTheme, String, String)
|
||||
case selectivePrivacyInfo(PresentationTheme, String)
|
||||
case passcode(PresentationTheme, String, Bool, String)
|
||||
case twoStepVerification(PresentationTheme, String, String, TwoStepVerificationAccessConfiguration?)
|
||||
case activeSessions(PresentationTheme, String, String)
|
||||
case accountHeader(PresentationTheme, String)
|
||||
case accountTimeout(PresentationTheme, String, String)
|
||||
case accountInfo(PresentationTheme, String)
|
||||
case dataSettings(PresentationTheme, String)
|
||||
case dataSettingsInfo(PresentationTheme, String)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
case .blockedPeers, .activeSessions, .passcode, .twoStepVerification:
|
||||
return PrivacyAndSecuritySection.general.rawValue
|
||||
case .privacyHeader, .phoneNumberPrivacy, .lastSeenPrivacy, .profilePhotoPrivacy, .forwardPrivacy, .groupPrivacy, .selectivePrivacyInfo, .voiceCallPrivacy:
|
||||
return PrivacyAndSecuritySection.privacy.rawValue
|
||||
case .accountHeader, .accountTimeout, .accountInfo:
|
||||
return PrivacyAndSecuritySection.account.rawValue
|
||||
case .dataSettings, .dataSettingsInfo:
|
||||
return PrivacyAndSecuritySection.dataSettings.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
var stableId: Int32 {
|
||||
switch self {
|
||||
case .blockedPeers:
|
||||
return 1
|
||||
case .activeSessions:
|
||||
return 2
|
||||
case .passcode:
|
||||
return 3
|
||||
case .twoStepVerification:
|
||||
return 4
|
||||
case .privacyHeader:
|
||||
return 5
|
||||
case .phoneNumberPrivacy:
|
||||
return 6
|
||||
case .lastSeenPrivacy:
|
||||
return 7
|
||||
case .profilePhotoPrivacy:
|
||||
return 8
|
||||
case .voiceCallPrivacy:
|
||||
return 9
|
||||
case .forwardPrivacy:
|
||||
return 10
|
||||
case .groupPrivacy:
|
||||
return 11
|
||||
case .selectivePrivacyInfo:
|
||||
return 12
|
||||
case .accountHeader:
|
||||
return 13
|
||||
case .accountTimeout:
|
||||
return 14
|
||||
case .accountInfo:
|
||||
return 15
|
||||
case .dataSettings:
|
||||
return 16
|
||||
case .dataSettingsInfo:
|
||||
return 17
|
||||
}
|
||||
}
|
||||
|
||||
static func ==(lhs: PrivacyAndSecurityEntry, rhs: PrivacyAndSecurityEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .privacyHeader(lhsTheme, lhsText):
|
||||
if case let .privacyHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .blockedPeers(lhsTheme, lhsText, lhsValue):
|
||||
if case let .blockedPeers(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .phoneNumberPrivacy(lhsTheme, lhsText, lhsValue):
|
||||
if case let .phoneNumberPrivacy(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .lastSeenPrivacy(lhsTheme, lhsText, lhsValue):
|
||||
if case let .lastSeenPrivacy(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .profilePhotoPrivacy(lhsTheme, lhsText, lhsValue):
|
||||
if case let .profilePhotoPrivacy(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .forwardPrivacy(lhsTheme, lhsText, lhsValue):
|
||||
if case let .forwardPrivacy(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .groupPrivacy(lhsTheme, lhsText, lhsValue):
|
||||
if case let .groupPrivacy(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .selectivePrivacyInfo(lhsTheme, lhsText):
|
||||
if case let .selectivePrivacyInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .voiceCallPrivacy(lhsTheme, lhsText, lhsValue):
|
||||
if case let .voiceCallPrivacy(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .passcode(lhsTheme, lhsText, lhsHasFaceId, lhsValue):
|
||||
if case let .passcode(rhsTheme, rhsText, rhsHasFaceId, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsHasFaceId == rhsHasFaceId, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .twoStepVerification(lhsTheme, lhsText, lhsValue, lhsData):
|
||||
if case let .twoStepVerification(rhsTheme, rhsText, rhsValue, rhsData) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsData == rhsData {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .activeSessions(lhsTheme, lhsText, lhsValue):
|
||||
if case let .activeSessions(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .accountHeader(lhsTheme, lhsText):
|
||||
if case let .accountHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .accountTimeout(lhsTheme, lhsText, lhsValue):
|
||||
if case let .accountTimeout(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .accountInfo(lhsTheme, lhsText):
|
||||
if case let .accountInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .dataSettings(lhsTheme, lhsText):
|
||||
if case let .dataSettings(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .dataSettingsInfo(lhsTheme, lhsText):
|
||||
if case let .dataSettingsInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func <(lhs: PrivacyAndSecurityEntry, rhs: PrivacyAndSecurityEntry) -> Bool {
|
||||
return lhs.stableId < rhs.stableId
|
||||
}
|
||||
|
||||
func item(_ arguments: PrivacyAndSecurityControllerArguments) -> ListViewItem {
|
||||
switch self {
|
||||
case let .privacyHeader(theme, text):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||
case let .blockedPeers(theme, text, value):
|
||||
return ItemListDisclosureItem(theme: theme, icon: UIImage(bundleImageName: "Settings/MenuIcons/Blocked")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.openBlockedUsers()
|
||||
})
|
||||
case let .phoneNumberPrivacy(theme, text, value):
|
||||
return ItemListDisclosureItem(theme: theme, title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.openPhoneNumberPrivacy()
|
||||
})
|
||||
case let .lastSeenPrivacy(theme, text, value):
|
||||
return ItemListDisclosureItem(theme: theme, title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.openLastSeenPrivacy()
|
||||
})
|
||||
case let .profilePhotoPrivacy(theme, text, value):
|
||||
return ItemListDisclosureItem(theme: theme, title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.openProfilePhotoPrivacy()
|
||||
})
|
||||
case let .forwardPrivacy(theme, text, value):
|
||||
return ItemListDisclosureItem(theme: theme, title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.openForwardPrivacy()
|
||||
})
|
||||
case let .groupPrivacy(theme, text, value):
|
||||
return ItemListDisclosureItem(theme: theme, title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.openGroupsPrivacy()
|
||||
})
|
||||
case let .selectivePrivacyInfo(theme, text):
|
||||
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
||||
case let .voiceCallPrivacy(theme, text, value):
|
||||
return ItemListDisclosureItem(theme: theme, title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.openVoiceCallPrivacy()
|
||||
})
|
||||
case let .passcode(theme, text, hasFaceId, value):
|
||||
return ItemListDisclosureItem(theme: theme, icon: UIImage(bundleImageName: hasFaceId ? "Settings/MenuIcons/FaceId" : "Settings/MenuIcons/TouchId")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.openPasscode()
|
||||
})
|
||||
case let .twoStepVerification(theme, text, value, data):
|
||||
return ItemListDisclosureItem(theme: theme, icon: UIImage(bundleImageName: "Settings/MenuIcons/TwoStepAuth")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.openTwoStepVerification(data)
|
||||
})
|
||||
case let .activeSessions(theme, text, value):
|
||||
return ItemListDisclosureItem(theme: theme, icon: UIImage(bundleImageName: "Settings/MenuIcons/Sessions")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.openActiveSessions()
|
||||
})
|
||||
case let .accountHeader(theme, text):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||
case let .accountTimeout(theme, text, value):
|
||||
return ItemListDisclosureItem(theme: theme, title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.setupAccountAutoremove()
|
||||
}, tag: PrivacyAndSecurityEntryTag.accountTimeout)
|
||||
case let .accountInfo(theme, text):
|
||||
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
||||
case let .dataSettings(theme, text):
|
||||
return ItemListDisclosureItem(theme: theme, title: text, label: "", sectionId: self.section, style: .blocks, action: {
|
||||
arguments.openDataSettings()
|
||||
})
|
||||
case let .dataSettingsInfo(theme, text):
|
||||
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct PrivacyAndSecurityControllerState: Equatable {
|
||||
var updatingAccountTimeoutValue: Int32? = nil
|
||||
}
|
||||
|
||||
private func countForSelectivePeers(_ peers: [PeerId: SelectivePrivacyPeer]) -> Int {
|
||||
var result = 0
|
||||
for (_, peer) in peers {
|
||||
result += peer.userCount
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private func stringForSelectiveSettings(strings: PresentationStrings, settings: SelectivePrivacySettings) -> String {
|
||||
switch settings {
|
||||
case let .disableEveryone(enableFor):
|
||||
if enableFor.isEmpty {
|
||||
return strings.PrivacySettings_LastSeenNobody
|
||||
} else {
|
||||
return strings.PrivacySettings_LastSeenNobodyPlus("\(countForSelectivePeers(enableFor))").0
|
||||
}
|
||||
case let .enableEveryone(disableFor):
|
||||
if disableFor.isEmpty {
|
||||
return strings.PrivacySettings_LastSeenEverybody
|
||||
} else {
|
||||
return strings.PrivacySettings_LastSeenEverybodyMinus("\(countForSelectivePeers(disableFor))").0
|
||||
}
|
||||
case let .enableContacts(enableFor, disableFor):
|
||||
if !enableFor.isEmpty && !disableFor.isEmpty {
|
||||
return strings.PrivacySettings_LastSeenContactsMinusPlus("\(countForSelectivePeers(enableFor))", "\(countForSelectivePeers(disableFor))").0
|
||||
} else if !enableFor.isEmpty {
|
||||
return strings.PrivacySettings_LastSeenContactsPlus("\(countForSelectivePeers(enableFor))").0
|
||||
} else if !disableFor.isEmpty {
|
||||
return strings.PrivacySettings_LastSeenContactsMinus("\(countForSelectivePeers(disableFor))").0
|
||||
} else {
|
||||
return strings.PrivacySettings_LastSeenContacts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func privacyAndSecurityControllerEntries(presentationData: PresentationData, state: PrivacyAndSecurityControllerState, privacySettings: AccountPrivacySettings?, accessChallengeData: PostboxAccessChallengeData, blockedPeerCount: Int?, activeSessionsCount: Int, twoStepAuthData: TwoStepVerificationAccessConfiguration?) -> [PrivacyAndSecurityEntry] {
|
||||
var entries: [PrivacyAndSecurityEntry] = []
|
||||
|
||||
entries.append(.blockedPeers(presentationData.theme, presentationData.strings.Settings_BlockedUsers, blockedPeerCount == nil ? "" : (blockedPeerCount == 0 ? presentationData.strings.PrivacySettings_BlockedPeersEmpty : "\(blockedPeerCount!)")))
|
||||
entries.append(.activeSessions(presentationData.theme, presentationData.strings.PrivacySettings_AuthSessions, activeSessionsCount == 0 ? "" : "\(activeSessionsCount)"))
|
||||
|
||||
let passcodeValue: String
|
||||
switch accessChallengeData {
|
||||
case .none:
|
||||
passcodeValue = presentationData.strings.PrivacySettings_PasscodeOff
|
||||
default:
|
||||
passcodeValue = presentationData.strings.PrivacySettings_PasscodeOn
|
||||
}
|
||||
|
||||
if let biometricAuthentication = LocalAuth.biometricAuthentication {
|
||||
switch biometricAuthentication {
|
||||
case .touchId:
|
||||
entries.append(.passcode(presentationData.theme, presentationData.strings.PrivacySettings_PasscodeAndTouchId, false, passcodeValue))
|
||||
case .faceId:
|
||||
entries.append(.passcode(presentationData.theme, presentationData.strings.PrivacySettings_PasscodeAndFaceId, true, passcodeValue))
|
||||
}
|
||||
} else {
|
||||
entries.append(.passcode(presentationData.theme, presentationData.strings.PrivacySettings_Passcode, false, passcodeValue))
|
||||
}
|
||||
var twoStepAuthString = ""
|
||||
if let twoStepAuthData = twoStepAuthData {
|
||||
switch twoStepAuthData {
|
||||
case .set:
|
||||
twoStepAuthString = presentationData.strings.PrivacySettings_PasscodeOn
|
||||
case .notSet:
|
||||
twoStepAuthString = presentationData.strings.PrivacySettings_PasscodeOff
|
||||
}
|
||||
}
|
||||
entries.append(.twoStepVerification(presentationData.theme, presentationData.strings.PrivacySettings_TwoStepAuth, twoStepAuthString, twoStepAuthData))
|
||||
|
||||
entries.append(.privacyHeader(presentationData.theme, presentationData.strings.PrivacySettings_PrivacyTitle))
|
||||
if let privacySettings = privacySettings {
|
||||
entries.append(.phoneNumberPrivacy(presentationData.theme, presentationData.strings.PrivacySettings_PhoneNumber, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.phoneNumber)))
|
||||
entries.append(.lastSeenPrivacy(presentationData.theme, presentationData.strings.PrivacySettings_LastSeen, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.presence)))
|
||||
entries.append(.profilePhotoPrivacy(presentationData.theme, presentationData.strings.Privacy_ProfilePhoto, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.profilePhoto)))
|
||||
entries.append(.voiceCallPrivacy(presentationData.theme, presentationData.strings.Privacy_Calls, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.voiceCalls)))
|
||||
entries.append(.forwardPrivacy(presentationData.theme, presentationData.strings.Privacy_Forwards, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.forwards)))
|
||||
entries.append(.groupPrivacy(presentationData.theme, presentationData.strings.Privacy_GroupsAndChannels, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.groupInvitations)))
|
||||
|
||||
entries.append(.selectivePrivacyInfo(presentationData.theme, presentationData.strings.PrivacyLastSeenSettings_GroupsAndChannelsHelp))
|
||||
} else {
|
||||
entries.append(.lastSeenPrivacy(presentationData.theme, presentationData.strings.PrivacySettings_LastSeen, presentationData.strings.Channel_NotificationLoading))
|
||||
entries.append(.profilePhotoPrivacy(presentationData.theme, presentationData.strings.Privacy_ProfilePhoto, presentationData.strings.Channel_NotificationLoading))
|
||||
entries.append(.voiceCallPrivacy(presentationData.theme, presentationData.strings.Privacy_Calls, presentationData.strings.Channel_NotificationLoading))
|
||||
entries.append(.forwardPrivacy(presentationData.theme, presentationData.strings.Privacy_Forwards, presentationData.strings.Channel_NotificationLoading))
|
||||
entries.append(.groupPrivacy(presentationData.theme, presentationData.strings.Privacy_GroupsAndChannels, presentationData.strings.Channel_NotificationLoading))
|
||||
entries.append(.selectivePrivacyInfo(presentationData.theme, presentationData.strings.PrivacyLastSeenSettings_GroupsAndChannelsHelp))
|
||||
}
|
||||
|
||||
entries.append(.accountHeader(presentationData.theme, presentationData.strings.PrivacySettings_DeleteAccountTitle.uppercased()))
|
||||
if let privacySettings = privacySettings {
|
||||
let value: Int32
|
||||
if let updatingAccountTimeoutValue = state.updatingAccountTimeoutValue {
|
||||
value = updatingAccountTimeoutValue
|
||||
} else {
|
||||
value = privacySettings.accountRemovalTimeout
|
||||
}
|
||||
entries.append(.accountTimeout(presentationData.theme, presentationData.strings.PrivacySettings_DeleteAccountIfAwayFor, timeIntervalString(strings: presentationData.strings, value: value)))
|
||||
} else {
|
||||
entries.append(.accountTimeout(presentationData.theme, presentationData.strings.PrivacySettings_DeleteAccountIfAwayFor, presentationData.strings.Channel_NotificationLoading))
|
||||
}
|
||||
entries.append(.accountInfo(presentationData.theme, presentationData.strings.PrivacySettings_DeleteAccountHelp))
|
||||
|
||||
entries.append(.dataSettings(presentationData.theme, presentationData.strings.PrivacySettings_DataSettings))
|
||||
entries.append(.dataSettingsInfo(presentationData.theme, presentationData.strings.PrivacySettings_DataSettingsHelp))
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
public func privacyAndSecurityController(context: AccountContext, initialSettings: AccountPrivacySettings? = nil, updatedSettings: ((AccountPrivacySettings?) -> Void)? = nil, focusOnItemTag: PrivacyAndSecurityEntryTag? = nil) -> ViewController {
|
||||
let statePromise = ValuePromise(PrivacyAndSecurityControllerState(), ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: PrivacyAndSecurityControllerState())
|
||||
let updateState: ((PrivacyAndSecurityControllerState) -> PrivacyAndSecurityControllerState) -> Void = { f in
|
||||
statePromise.set(stateValue.modify { f($0) })
|
||||
}
|
||||
|
||||
var pushControllerImpl: ((ViewController, Bool) -> Void)?
|
||||
var replaceTopControllerImpl: ((ViewController) -> Void)?
|
||||
var presentControllerImpl: ((ViewController) -> Void)?
|
||||
|
||||
let actionsDisposable = DisposableSet()
|
||||
|
||||
let currentInfoDisposable = MetaDisposable()
|
||||
actionsDisposable.add(currentInfoDisposable)
|
||||
|
||||
let updateAccountTimeoutDisposable = MetaDisposable()
|
||||
actionsDisposable.add(updateAccountTimeoutDisposable)
|
||||
|
||||
let privacySettingsPromise = Promise<AccountPrivacySettings?>()
|
||||
privacySettingsPromise.set(.single(initialSettings) |> then(requestAccountPrivacySettings(account: context.account) |> map(Optional.init)))
|
||||
|
||||
let blockedPeersContext = BlockedPeersContext(account: context.account)
|
||||
let activeSessionsContext = ActiveSessionsContext(account: context.account)
|
||||
|
||||
let updateTwoStepAuthDisposable = MetaDisposable()
|
||||
actionsDisposable.add(updateTwoStepAuthDisposable)
|
||||
|
||||
let twoStepAuthDataValue = Promise<TwoStepVerificationAccessConfiguration?>(nil)
|
||||
let updateHasTwoStepAuth: () -> Void = {
|
||||
let signal = twoStepVerificationConfiguration(account: context.account)
|
||||
|> map { value -> TwoStepVerificationAccessConfiguration? in
|
||||
return TwoStepVerificationAccessConfiguration(configuration: value, password: nil)
|
||||
}
|
||||
|> deliverOnMainQueue
|
||||
updateTwoStepAuthDisposable.set(
|
||||
signal.start(next: { value in
|
||||
twoStepAuthDataValue.set(.single(value))
|
||||
})
|
||||
)
|
||||
}
|
||||
updateHasTwoStepAuth()
|
||||
|
||||
let arguments = PrivacyAndSecurityControllerArguments(account: context.account, openBlockedUsers: {
|
||||
pushControllerImpl?(blockedPeersController(context: context, blockedPeersContext: blockedPeersContext), true)
|
||||
}, openLastSeenPrivacy: {
|
||||
let signal = privacySettingsPromise.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue
|
||||
currentInfoDisposable.set(signal.start(next: { [weak currentInfoDisposable] info in
|
||||
if let info = info {
|
||||
pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .presence, current: info.presence, updated: { updated, _ in
|
||||
if let currentInfoDisposable = currentInfoDisposable {
|
||||
let applySetting: Signal<Void, NoError> = privacySettingsPromise.get()
|
||||
|> filter { $0 != nil }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue
|
||||
|> mapToSignal { value -> Signal<Void, NoError> in
|
||||
if let value = value {
|
||||
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: updated, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, accountRemovalTimeout: value.accountRemovalTimeout)))
|
||||
}
|
||||
return .complete()
|
||||
}
|
||||
currentInfoDisposable.set(applySetting.start())
|
||||
}
|
||||
}), true)
|
||||
}
|
||||
}))
|
||||
}, openGroupsPrivacy: {
|
||||
let signal = privacySettingsPromise.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue
|
||||
currentInfoDisposable.set(signal.start(next: { [weak currentInfoDisposable] info in
|
||||
if let info = info {
|
||||
pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .groupInvitations, current: info.groupInvitations, updated: { updated, _ in
|
||||
if let currentInfoDisposable = currentInfoDisposable {
|
||||
let applySetting: Signal<Void, NoError> = privacySettingsPromise.get()
|
||||
|> filter { $0 != nil }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue
|
||||
|> mapToSignal { value -> Signal<Void, NoError> in
|
||||
if let value = value {
|
||||
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: updated, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, accountRemovalTimeout: value.accountRemovalTimeout)))
|
||||
}
|
||||
return .complete()
|
||||
}
|
||||
currentInfoDisposable.set(applySetting.start())
|
||||
}
|
||||
}), true)
|
||||
}
|
||||
}))
|
||||
}, openVoiceCallPrivacy: {
|
||||
let privacySignal = privacySettingsPromise.get()
|
||||
|> take(1)
|
||||
|
||||
let callsSignal = combineLatest(context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.voiceCallSettings]), context.account.postbox.preferencesView(keys: [PreferencesKeys.voipConfiguration]))
|
||||
|> take(1)
|
||||
|> map { sharedData, view -> (VoiceCallSettings, VoipConfiguration) in
|
||||
let voiceCallSettings: VoiceCallSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.voiceCallSettings] as? VoiceCallSettings ?? .defaultSettings
|
||||
let voipConfiguration = view.values[PreferencesKeys.voipConfiguration] as? VoipConfiguration ?? .defaultValue
|
||||
|
||||
return (voiceCallSettings, voipConfiguration)
|
||||
}
|
||||
|
||||
currentInfoDisposable.set((combineLatest(privacySignal, callsSignal)
|
||||
|> deliverOnMainQueue).start(next: { [weak currentInfoDisposable] info, callSettings in
|
||||
if let info = info {
|
||||
pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .voiceCalls, current: info.voiceCalls, callSettings: (info.voiceCallsP2P, callSettings.0), voipConfiguration: callSettings.1, callIntegrationAvailable: CallKitIntegration.isAvailable, updated: { updated, updatedCallSettings in
|
||||
if let currentInfoDisposable = currentInfoDisposable, let (updatedCallsPrivacy, updatedCallSettings) = updatedCallSettings {
|
||||
let _ = updateVoiceCallSettingsSettingsInteractively(accountManager: context.sharedContext.accountManager, { _ in
|
||||
return updatedCallSettings
|
||||
}).start()
|
||||
|
||||
let applySetting: Signal<Void, NoError> = privacySettingsPromise.get()
|
||||
|> filter { $0 != nil }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue
|
||||
|> mapToSignal { value -> Signal<Void, NoError> in
|
||||
if let value = value {
|
||||
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: updated, voiceCallsP2P: updatedCallsPrivacy, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, accountRemovalTimeout: value.accountRemovalTimeout)))
|
||||
}
|
||||
return .complete()
|
||||
}
|
||||
currentInfoDisposable.set(applySetting.start())
|
||||
}
|
||||
}), true)
|
||||
}
|
||||
}))
|
||||
}, openProfilePhotoPrivacy: {
|
||||
let signal = privacySettingsPromise.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue
|
||||
currentInfoDisposable.set(signal.start(next: { [weak currentInfoDisposable] info in
|
||||
if let info = info {
|
||||
pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .profilePhoto, current: info.profilePhoto, updated: { updated, _ in
|
||||
if let currentInfoDisposable = currentInfoDisposable {
|
||||
let applySetting: Signal<Void, NoError> = privacySettingsPromise.get()
|
||||
|> filter { $0 != nil }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue
|
||||
|> mapToSignal { value -> Signal<Void, NoError> in
|
||||
if let value = value {
|
||||
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: updated, forwards: value.forwards, phoneNumber: value.phoneNumber, accountRemovalTimeout: value.accountRemovalTimeout)))
|
||||
}
|
||||
return .complete()
|
||||
}
|
||||
currentInfoDisposable.set(applySetting.start())
|
||||
}
|
||||
}), true)
|
||||
}
|
||||
}))
|
||||
}, openForwardPrivacy: {
|
||||
let signal = privacySettingsPromise.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue
|
||||
currentInfoDisposable.set(signal.start(next: { [weak currentInfoDisposable] info in
|
||||
if let info = info {
|
||||
pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .forwards, current: info.forwards, updated: { updated, _ in
|
||||
if let currentInfoDisposable = currentInfoDisposable {
|
||||
let applySetting: Signal<Void, NoError> = privacySettingsPromise.get()
|
||||
|> filter { $0 != nil }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue
|
||||
|> mapToSignal { value -> Signal<Void, NoError> in
|
||||
if let value = value {
|
||||
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: updated, phoneNumber: value.phoneNumber, accountRemovalTimeout: value.accountRemovalTimeout)))
|
||||
}
|
||||
return .complete()
|
||||
}
|
||||
currentInfoDisposable.set(applySetting.start())
|
||||
}
|
||||
}), true)
|
||||
}
|
||||
}))
|
||||
}, openPhoneNumberPrivacy: {
|
||||
let signal = privacySettingsPromise.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue
|
||||
currentInfoDisposable.set(signal.start(next: { [weak currentInfoDisposable] info in
|
||||
if let info = info {
|
||||
pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .phoneNumber, current: info.phoneNumber, updated: { updated, _ in
|
||||
if let currentInfoDisposable = currentInfoDisposable {
|
||||
let applySetting: Signal<Void, NoError> = privacySettingsPromise.get()
|
||||
|> filter { $0 != nil }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue
|
||||
|> mapToSignal { value -> Signal<Void, NoError> in
|
||||
if let value = value {
|
||||
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: updated, accountRemovalTimeout: value.accountRemovalTimeout)))
|
||||
}
|
||||
return .complete()
|
||||
}
|
||||
currentInfoDisposable.set(applySetting.start())
|
||||
}
|
||||
}), true)
|
||||
}
|
||||
}))
|
||||
}, openPasscode: {
|
||||
let _ = passcodeOptionsAccessController(context: context, pushController: { controller in
|
||||
replaceTopControllerImpl?(controller)
|
||||
}, completion: { _ in
|
||||
replaceTopControllerImpl?(passcodeOptionsController(context: context))
|
||||
}).start(next: { controller in
|
||||
if let controller = controller {
|
||||
pushControllerImpl?(controller, true)
|
||||
}
|
||||
})
|
||||
}, openTwoStepVerification: { data in
|
||||
var intro = false
|
||||
if let data = data {
|
||||
switch data {
|
||||
case .set:
|
||||
break
|
||||
case let .notSet(pendingEmail):
|
||||
intro = pendingEmail == nil
|
||||
}
|
||||
}
|
||||
if intro {
|
||||
var dismissInto: (() -> Void)?
|
||||
let controller = PrivacyIntroController(context: context, mode: .twoStepVerification, arguments: PrivacyIntroControllerPresentationArguments(fadeIn: false, animateIn: true), proceedAction: {
|
||||
pushControllerImpl?(twoStepVerificationUnlockSettingsController(context: context, mode: .access(intro: intro, data: data.flatMap({ Signal<TwoStepVerificationUnlockSettingsControllerData, NoError>.single(.access(configuration: $0)) })), openSetupPasswordImmediately: true), false)
|
||||
dismissInto?()
|
||||
})
|
||||
dismissInto = { [weak controller] in
|
||||
controller?.dismiss()
|
||||
}
|
||||
presentControllerImpl?(controller)
|
||||
} else {
|
||||
pushControllerImpl?(twoStepVerificationUnlockSettingsController(context: context, mode: .access(intro: intro, data: data.flatMap({ Signal<TwoStepVerificationUnlockSettingsControllerData, NoError>.single(.access(configuration: $0)) }))), true)
|
||||
}
|
||||
}, openActiveSessions: {
|
||||
pushControllerImpl?(recentSessionsController(context: context, activeSessionsContext: activeSessionsContext), true)
|
||||
}, setupAccountAutoremove: {
|
||||
let signal = privacySettingsPromise.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue
|
||||
updateAccountTimeoutDisposable.set(signal.start(next: { [weak updateAccountTimeoutDisposable] privacySettingsValue in
|
||||
if let _ = privacySettingsValue {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = ActionSheetController(presentationTheme: presentationData.theme)
|
||||
let dismissAction: () -> Void = { [weak controller] in
|
||||
controller?.dismissAnimated()
|
||||
}
|
||||
let timeoutAction: (Int32) -> Void = { timeout in
|
||||
if let updateAccountTimeoutDisposable = updateAccountTimeoutDisposable {
|
||||
updateState { state in
|
||||
var state = state
|
||||
state.updatingAccountTimeoutValue = timeout
|
||||
return state
|
||||
}
|
||||
let applyTimeout: Signal<Void, NoError> = privacySettingsPromise.get()
|
||||
|> filter { $0 != nil }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue
|
||||
|> mapToSignal { value -> Signal<Void, NoError> in
|
||||
if let value = value {
|
||||
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, accountRemovalTimeout: timeout)))
|
||||
}
|
||||
return .complete()
|
||||
}
|
||||
updateAccountTimeoutDisposable.set((updateAccountRemovalTimeout(account: context.account, timeout: timeout)
|
||||
|> then(applyTimeout)
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
updateState { state in
|
||||
var state = state
|
||||
state.updatingAccountTimeoutValue = nil
|
||||
return state
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
let timeoutValues: [Int32] = [
|
||||
1 * 30 * 24 * 60 * 60,
|
||||
3 * 30 * 24 * 60 * 60,
|
||||
6 * 30 * 24 * 60 * 60,
|
||||
12 * 30 * 24 * 60 * 60
|
||||
]
|
||||
let timeoutItems: [ActionSheetItem] = timeoutValues.map { value in
|
||||
return ActionSheetButtonItem(title: timeIntervalString(strings: presentationData.strings, value: value), action: {
|
||||
dismissAction()
|
||||
timeoutAction(value)
|
||||
})
|
||||
}
|
||||
controller.setItemGroups([
|
||||
ActionSheetItemGroup(items: timeoutItems),
|
||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||
])
|
||||
presentControllerImpl?(controller)
|
||||
}
|
||||
}))
|
||||
}, openDataSettings: {
|
||||
pushControllerImpl?(dataPrivacyController(context: context), true)
|
||||
})
|
||||
|
||||
actionsDisposable.add(managedUpdatedRecentPeers(accountPeerId: context.account.peerId, postbox: context.account.postbox, network: context.account.network).start())
|
||||
|
||||
actionsDisposable.add((privacySettingsPromise.get()
|
||||
|> deliverOnMainQueue).start(next: { settings in
|
||||
updatedSettings?(settings)
|
||||
}))
|
||||
|
||||
let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, statePromise.get(), privacySettingsPromise.get(), context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.secretChatLinkPreviewsKey()), context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.contactSynchronizationSettings]), recentPeers(account: context.account), blockedPeersContext.state, activeSessionsContext.state, context.sharedContext.accountManager.accessChallengeData(), twoStepAuthDataValue.get())
|
||||
|> map { presentationData, state, privacySettings, noticeView, sharedData, recentPeers, blockedPeersState, activeSessionsState, accessChallengeData, twoStepAuthData -> (ItemListControllerState, (ItemListNodeState<PrivacyAndSecurityEntry>, PrivacyAndSecurityEntry.ItemGenerationArguments)) in
|
||||
var rightNavigationButton: ItemListNavigationButton?
|
||||
if privacySettings == nil || state.updatingAccountTimeoutValue != nil {
|
||||
rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {})
|
||||
}
|
||||
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.PrivacySettings_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
|
||||
let listState = ItemListNodeState(entries: privacyAndSecurityControllerEntries(presentationData: presentationData, state: state, privacySettings: privacySettings, accessChallengeData: accessChallengeData.data, blockedPeerCount: blockedPeersState.totalCount, activeSessionsCount: activeSessionsState.sessions.count, twoStepAuthData: twoStepAuthData), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
|> afterDisposed {
|
||||
actionsDisposable.dispose()
|
||||
}
|
||||
|
||||
let controller = ItemListController(context: context, state: signal)
|
||||
pushControllerImpl = { [weak controller] c, animated in
|
||||
(controller?.navigationController as? NavigationController)?.pushViewController(c, animated: animated)
|
||||
}
|
||||
replaceTopControllerImpl = { [weak controller] c in
|
||||
(controller?.navigationController as? NavigationController)?.replaceTopController(c, animated: true)
|
||||
}
|
||||
presentControllerImpl = { [weak controller] c in
|
||||
controller?.present(c, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}
|
||||
|
||||
controller.didAppear = { _ in
|
||||
updateHasTwoStepAuth()
|
||||
}
|
||||
|
||||
return controller
|
||||
}
|
||||
Reference in New Issue
Block a user