mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
1192 lines
59 KiB
Swift
1192 lines
59 KiB
Swift
import Foundation
|
|
import Display
|
|
import SwiftSignalKit
|
|
import Postbox
|
|
import TelegramCore
|
|
import LegacyComponents
|
|
import MtProtoKitDynamic
|
|
|
|
private final class SettingsItemIcons {
|
|
static let proxy = UIImage(bundleImageName: "Settings/MenuIcons/Proxy")?.precomposed()
|
|
static let savedMessages = UIImage(bundleImageName: "Settings/MenuIcons/SavedMessages")?.precomposed()
|
|
static let recentCalls = UIImage(bundleImageName: "Settings/MenuIcons/RecentCalls")?.precomposed()
|
|
static let stickers = UIImage(bundleImageName: "Settings/MenuIcons/Stickers")?.precomposed()
|
|
|
|
static let notifications = UIImage(bundleImageName: "Settings/MenuIcons/Notifications")?.precomposed()
|
|
static let security = UIImage(bundleImageName: "Settings/MenuIcons/Security")?.precomposed()
|
|
static let dataAndStorage = UIImage(bundleImageName: "Settings/MenuIcons/DataAndStorage")?.precomposed()
|
|
static let appearance = UIImage(bundleImageName: "Settings/MenuIcons/Appearance")?.precomposed()
|
|
static let language = UIImage(bundleImageName: "Settings/MenuIcons/Language")?.precomposed()
|
|
|
|
static let passport = UIImage(bundleImageName: "Settings/MenuIcons/Passport")?.precomposed()
|
|
static let watch = UIImage(bundleImageName: "Settings/MenuIcons/Watch")?.precomposed()
|
|
|
|
static let support = UIImage(bundleImageName: "Settings/MenuIcons/Support")?.precomposed()
|
|
static let faq = UIImage(bundleImageName: "Settings/MenuIcons/Faq")?.precomposed()
|
|
}
|
|
|
|
private struct SettingsItemArguments {
|
|
let accountManager: AccountManager
|
|
let avatarAndNameInfoContext: ItemListAvatarAndNameInfoItemContext
|
|
|
|
let avatarTapAction: () -> Void
|
|
|
|
let changeProfilePhoto: () -> Void
|
|
let openUsername: () -> Void
|
|
let openProxy: () -> Void
|
|
let openSavedMessages: () -> Void
|
|
let openRecentCalls: () -> Void
|
|
let openPrivacyAndSecurity: () -> Void
|
|
let openDataAndStorage: () -> Void
|
|
let openStickerPacks: ([ArchivedStickerPackItem]?) -> Void
|
|
let openNotificationsAndSounds: (NotificationExceptionsList?) -> Void
|
|
let openThemes: () -> Void
|
|
let pushController: (ViewController) -> Void
|
|
let openLanguage: () -> Void
|
|
let openPassport: () -> Void
|
|
let openWatch: () -> Void
|
|
let openSupport: () -> Void
|
|
let openFaq: () -> Void
|
|
let openEditing: () -> Void
|
|
let displayCopyContextMenu: () -> Void
|
|
let switchToAccount: (AccountRecordId) -> Void
|
|
let addAccount: () -> Void
|
|
}
|
|
|
|
private enum SettingsSection: Int32 {
|
|
case info
|
|
case accounts
|
|
case proxy
|
|
case media
|
|
case generalSettings
|
|
case advanced
|
|
case help
|
|
}
|
|
|
|
private enum SettingsEntry: ItemListNodeEntry {
|
|
case userInfo(Account, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer?, CachedPeerData?, ItemListAvatarAndNameInfoItemState, ItemListAvatarAndNameInfoItemUpdatingAvatar?)
|
|
case setProfilePhoto(PresentationTheme, String)
|
|
case setUsername(PresentationTheme, String)
|
|
|
|
case account(Int, Account, PresentationTheme, PresentationStrings, Peer, Int32)
|
|
case addAccount(PresentationTheme, String)
|
|
|
|
case proxy(PresentationTheme, UIImage?, String, String)
|
|
|
|
case savedMessages(PresentationTheme, UIImage?, String)
|
|
case recentCalls(PresentationTheme, UIImage?, String)
|
|
case stickers(PresentationTheme, UIImage?, String, String, [ArchivedStickerPackItem]?)
|
|
|
|
case notificationsAndSounds(PresentationTheme, UIImage?, String, NotificationExceptionsList?, Bool)
|
|
case privacyAndSecurity(PresentationTheme, UIImage?, String)
|
|
case dataAndStorage(PresentationTheme, UIImage?, String)
|
|
case themes(PresentationTheme, UIImage?, String)
|
|
case language(PresentationTheme, UIImage?, String, String)
|
|
case passport(PresentationTheme, UIImage?, String, String)
|
|
case watch(PresentationTheme, UIImage?, String, String)
|
|
|
|
case askAQuestion(PresentationTheme, UIImage?, String)
|
|
case faq(PresentationTheme, UIImage?, String)
|
|
|
|
var section: ItemListSectionId {
|
|
switch self {
|
|
case .userInfo, .setProfilePhoto, .setUsername:
|
|
return SettingsSection.info.rawValue
|
|
case .account, .addAccount:
|
|
return SettingsSection.accounts.rawValue
|
|
case .proxy:
|
|
return SettingsSection.proxy.rawValue
|
|
case .savedMessages, .recentCalls, .stickers:
|
|
return SettingsSection.media.rawValue
|
|
case .notificationsAndSounds, .privacyAndSecurity, .dataAndStorage, .themes, .language:
|
|
return SettingsSection.generalSettings.rawValue
|
|
case .passport, .watch :
|
|
return SettingsSection.advanced.rawValue
|
|
case .askAQuestion, .faq:
|
|
return SettingsSection.help.rawValue
|
|
}
|
|
}
|
|
|
|
var stableId: Int32 {
|
|
switch self {
|
|
case .userInfo:
|
|
return 0
|
|
case .setProfilePhoto:
|
|
return 1
|
|
case .setUsername:
|
|
return 2
|
|
case let .account(account):
|
|
return 3 + Int32(account.0)
|
|
case .addAccount:
|
|
return 1002
|
|
case .proxy:
|
|
return 1003
|
|
case .savedMessages:
|
|
return 1004
|
|
case .recentCalls:
|
|
return 1005
|
|
case .stickers:
|
|
return 1006
|
|
case .notificationsAndSounds:
|
|
return 1007
|
|
case .privacyAndSecurity:
|
|
return 1008
|
|
case .dataAndStorage:
|
|
return 1009
|
|
case .themes:
|
|
return 1010
|
|
case .language:
|
|
return 1011
|
|
case .passport:
|
|
return 1012
|
|
case .watch:
|
|
return 1013
|
|
case .askAQuestion:
|
|
return 1014
|
|
case .faq:
|
|
return 1015
|
|
}
|
|
}
|
|
|
|
static func ==(lhs: SettingsEntry, rhs: SettingsEntry) -> Bool {
|
|
switch lhs {
|
|
case let .userInfo(lhsAccount, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsCachedData, lhsEditingState, lhsUpdatingImage):
|
|
if case let .userInfo(rhsAccount, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsCachedData, rhsEditingState, rhsUpdatingImage) = rhs {
|
|
if lhsAccount !== rhsAccount {
|
|
return false
|
|
}
|
|
if lhsTheme !== rhsTheme {
|
|
return false
|
|
}
|
|
if lhsStrings !== rhsStrings {
|
|
return false
|
|
}
|
|
if lhsDateTimeFormat != rhsDateTimeFormat {
|
|
return false
|
|
}
|
|
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
|
|
}
|
|
if lhsUpdatingImage != rhsUpdatingImage {
|
|
return false
|
|
}
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .account(lhsIndex, lhsAccount, lhsTheme, lhsStrings, lhsPeer, lhsBadgeCount):
|
|
if case let .account(rhsIndex, rhsAccount, rhsTheme, rhsStrings, rhsPeer, rhsBadgeCount) = rhs, lhsIndex == rhsIndex, lhsAccount === rhsAccount, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsPeer.isEqual(rhsPeer), lhsBadgeCount == rhsBadgeCount {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .addAccount(lhsTheme, lhsText):
|
|
if case let .addAccount(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .setProfilePhoto(lhsTheme, lhsText):
|
|
if case let .setProfilePhoto(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .setUsername(lhsTheme, lhsText):
|
|
if case let .setUsername(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .proxy(lhsTheme, lhsImage, lhsText, lhsValue):
|
|
if case let .proxy(rhsTheme, rhsImage, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsImage === rhsImage, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .savedMessages(lhsTheme, lhsImage, lhsText):
|
|
if case let .savedMessages(rhsTheme, rhsImage, rhsText) = rhs, lhsTheme === rhsTheme, lhsImage === rhsImage, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .recentCalls(lhsTheme, lhsImage, lhsText):
|
|
if case let .recentCalls(rhsTheme, rhsImage, rhsText) = rhs, lhsTheme === rhsTheme, lhsImage === rhsImage, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .stickers(lhsTheme, lhsImage, lhsText, lhsValue, _):
|
|
if case let .stickers(rhsTheme, rhsImage, rhsText, rhsValue, _) = rhs, lhsTheme === rhsTheme, lhsImage === rhsImage, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .notificationsAndSounds(lhsTheme, lhsImage, lhsText, lhsExceptionsList, lhsWarning):
|
|
if case let .notificationsAndSounds(rhsTheme, rhsImage, rhsText, rhsExceptionsList, rhsWarning) = rhs, lhsTheme === rhsTheme, lhsImage === rhsImage, lhsText == rhsText, lhsExceptionsList == rhsExceptionsList, lhsWarning == rhsWarning {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .privacyAndSecurity(lhsTheme, lhsImage, lhsText):
|
|
if case let .privacyAndSecurity(rhsTheme, rhsImage, rhsText) = rhs, lhsTheme === rhsTheme, lhsImage === rhsImage, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .dataAndStorage(lhsTheme, lhsImage, lhsText):
|
|
if case let .dataAndStorage(rhsTheme, rhsImage, rhsText) = rhs, lhsTheme === rhsTheme, lhsImage === rhsImage, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .themes(lhsTheme, lhsImage, lhsText):
|
|
if case let .themes(rhsTheme, rhsImage, rhsText) = rhs, lhsTheme === rhsTheme, lhsImage === rhsImage, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .language(lhsTheme, lhsImage, lhsText, lhsValue):
|
|
if case let .language(rhsTheme, rhsImage, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsImage === rhsImage, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .passport(lhsTheme, lhsImage, lhsText, lhsValue):
|
|
if case let .passport(rhsTheme, rhsImage, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsImage === rhsImage, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .watch(lhsTheme, lhsImage, lhsText, lhsValue):
|
|
if case let .watch(rhsTheme, rhsImage, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsImage === rhsImage, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .askAQuestion(lhsTheme, lhsImage, lhsText):
|
|
if case let .askAQuestion(rhsTheme, rhsImage, rhsText) = rhs, lhsTheme === rhsTheme, lhsImage === rhsImage, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .faq(lhsTheme, lhsImage, lhsText):
|
|
if case let .faq(rhsTheme, rhsImage, rhsText) = rhs, lhsTheme === rhsTheme, lhsImage === rhsImage, lhsText == rhsText {
|
|
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(account, theme, strings, dateTimeFormat, peer, cachedData, state, updatingImage):
|
|
return ItemListAvatarAndNameInfoItem(account: account, theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, mode: .settings, peer: peer, presence: TelegramUserPresence(status: .present(until: Int32.max), lastActivity: 0), cachedData: cachedData, state: state, sectionId: ItemListSectionId(self.section), style: .blocks(withTopInset: false), editingNameUpdated: { _ in
|
|
}, avatarTapped: {
|
|
arguments.avatarTapAction()
|
|
}, context: arguments.avatarAndNameInfoContext, updatingImage: updatingImage, action: {
|
|
arguments.openEditing()
|
|
}, longTapAction: {
|
|
arguments.displayCopyContextMenu()
|
|
})
|
|
case let .setProfilePhoto(theme, text):
|
|
return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.changeProfilePhoto()
|
|
})
|
|
case let .setUsername(theme, text):
|
|
return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.openUsername()
|
|
})
|
|
case let .account(_, account, theme, strings, peer, badgeCount):
|
|
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: PresentationDateTimeFormat(timeFormat: .regular, dateFormat: .dayFirst, dateSeparator: "."), nameDisplayOrder: .firstLast, account: account, peer: peer, aliasHandling: .standard, presence: nil, text: .none, label: .badge("\(badgeCount)"), editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), revealOptions: nil, switchValue: nil, enabled: true, sectionId: self.section, action: {
|
|
arguments.switchToAccount(account.id)
|
|
}, setPeerIdWithRevealedOptions: { lhs, rhs in
|
|
}, removePeer: { _ in
|
|
})
|
|
case let .addAccount(theme, text):
|
|
return ItemListPeerActionItem(theme: theme, icon: PresentationResourcesItemList.plusIconImage(theme), title: text, alwaysPlain: false, sectionId: self.section, editing: false, action: {
|
|
arguments.addAccount()
|
|
})
|
|
case let .proxy(theme, image, text, value):
|
|
return ItemListDisclosureItem(theme: theme, icon: image, title: text, label: value, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.openProxy()
|
|
})
|
|
case let .savedMessages(theme, image, text):
|
|
return ItemListDisclosureItem(theme: theme, icon: image, title: text, label: "", sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.openSavedMessages()
|
|
})
|
|
case let .recentCalls(theme, image, text):
|
|
return ItemListDisclosureItem(theme: theme, icon: image, title: text, label: "", sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.openRecentCalls()
|
|
})
|
|
case let .stickers(theme, image, text, value, archivedPacks):
|
|
return ItemListDisclosureItem(theme: theme, icon: image, title: text, label: value, labelStyle: .badge(theme.list.itemAccentColor), sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.openStickerPacks(archivedPacks)
|
|
})
|
|
case let .notificationsAndSounds(theme, image, text, exceptionsList, warning):
|
|
return ItemListDisclosureItem(theme: theme, icon: image, title: text, label: warning ? "!" : "", labelStyle: warning ? .badge(theme.list.itemDestructiveColor) : .text, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.openNotificationsAndSounds(exceptionsList)
|
|
})
|
|
case let .privacyAndSecurity(theme, image, text):
|
|
return ItemListDisclosureItem(theme: theme, icon: image, title: text, label: "", sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.openPrivacyAndSecurity()
|
|
})
|
|
case let .dataAndStorage(theme, image, text):
|
|
return ItemListDisclosureItem(theme: theme, icon: image, title: text, label: "", sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.openDataAndStorage()
|
|
})
|
|
case let .themes(theme, image, text):
|
|
return ItemListDisclosureItem(theme: theme, icon: image, title: text, label: "", sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.openThemes()
|
|
})
|
|
case let .language(theme, image, text, value):
|
|
return ItemListDisclosureItem(theme: theme, icon: image, title: text, label: value, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.openLanguage()
|
|
})
|
|
case let .passport(theme, image, text, value):
|
|
return ItemListDisclosureItem(theme: theme, icon: image, title: text, label: value, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.openPassport()
|
|
})
|
|
case let .watch(theme, image, text, value):
|
|
return ItemListDisclosureItem(theme: theme, icon: image, title: text, label: value, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.openWatch()
|
|
})
|
|
case let .askAQuestion(theme, image, text):
|
|
return ItemListDisclosureItem(theme: theme, icon: image, title: text, label: "", sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.openSupport()
|
|
})
|
|
case let .faq(theme, image, text):
|
|
return ItemListDisclosureItem(theme: theme, icon: image, title: text, label: "", sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
|
arguments.openFaq()
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct SettingsState: Equatable {
|
|
let updatingAvatar: ItemListAvatarAndNameInfoItemUpdatingAvatar?
|
|
|
|
init(updatingAvatar: ItemListAvatarAndNameInfoItemUpdatingAvatar? = nil) {
|
|
self.updatingAvatar = updatingAvatar
|
|
}
|
|
|
|
func withUpdatedUpdatingAvatar(_ updatingAvatar: ItemListAvatarAndNameInfoItemUpdatingAvatar?) -> SettingsState {
|
|
return SettingsState(updatingAvatar: updatingAvatar)
|
|
}
|
|
|
|
static func ==(lhs: SettingsState, rhs: SettingsState) -> Bool {
|
|
if lhs.updatingAvatar != rhs.updatingAvatar {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
private func settingsEntries(account: Account, presentationData: PresentationData, state: SettingsState, view: PeerView, proxySettings: ProxySettings, notifyExceptions: NotificationExceptionsList?, notificationsAuthorizationStatus: AccessType, notificationsWarningSuppressed: Bool, unreadTrendingStickerPacks: Int, archivedPacks: [ArchivedStickerPackItem]?, hasPassport: Bool, hasWatchApp: Bool, accountsAndPeers: [(Account, Peer, Int32)]) -> [SettingsEntry] {
|
|
var entries: [SettingsEntry] = []
|
|
|
|
if let peer = peerViewMainPeer(view) as? TelegramUser {
|
|
let userInfoState = ItemListAvatarAndNameInfoItemState(editingName: nil, updatingName: nil)
|
|
entries.append(.userInfo(account, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, view.cachedData, userInfoState, state.updatingAvatar))
|
|
if peer.photo.isEmpty {
|
|
entries.append(.setProfilePhoto(presentationData.theme, presentationData.strings.Settings_SetProfilePhoto))
|
|
}
|
|
if peer.addressName == nil {
|
|
entries.append(.setUsername(presentationData.theme, presentationData.strings.Settings_SetUsername))
|
|
}
|
|
|
|
if !accountsAndPeers.isEmpty {
|
|
var index = 0
|
|
for (peerAccount, peer, badgeCount) in accountsAndPeers {
|
|
entries.append(.account(index, peerAccount, presentationData.theme, presentationData.strings, peer, badgeCount))
|
|
index += 1
|
|
}
|
|
entries.append(.addAccount(presentationData.theme, presentationData.strings.Settings_AddAccount))
|
|
}
|
|
|
|
if !proxySettings.servers.isEmpty {
|
|
let valueString: String
|
|
if proxySettings.enabled, let activeServer = proxySettings.activeServer {
|
|
switch activeServer.connection {
|
|
case .mtp:
|
|
valueString = presentationData.strings.SocksProxySetup_ProxyTelegram
|
|
case .socks5:
|
|
valueString = presentationData.strings.SocksProxySetup_ProxySocks5
|
|
}
|
|
} else {
|
|
valueString = presentationData.strings.Settings_ProxyDisabled
|
|
}
|
|
entries.append(.proxy(presentationData.theme, SettingsItemIcons.proxy, presentationData.strings.Settings_Proxy, valueString))
|
|
}
|
|
|
|
entries.append(.savedMessages(presentationData.theme, SettingsItemIcons.savedMessages, presentationData.strings.Settings_SavedMessages))
|
|
entries.append(.recentCalls(presentationData.theme, SettingsItemIcons.recentCalls, presentationData.strings.CallSettings_RecentCalls))
|
|
entries.append(.stickers(presentationData.theme, SettingsItemIcons.stickers, presentationData.strings.ChatSettings_Stickers, unreadTrendingStickerPacks == 0 ? "" : "\(unreadTrendingStickerPacks)", archivedPacks))
|
|
|
|
let notificationsWarning = shouldDisplayNotificationsPermissionWarning(status: notificationsAuthorizationStatus, suppressed: notificationsWarningSuppressed)
|
|
entries.append(.notificationsAndSounds(presentationData.theme, SettingsItemIcons.notifications, presentationData.strings.Settings_NotificationsAndSounds, notifyExceptions, notificationsWarning))
|
|
entries.append(.privacyAndSecurity(presentationData.theme, SettingsItemIcons.security, presentationData.strings.Settings_PrivacySettings))
|
|
entries.append(.dataAndStorage(presentationData.theme, SettingsItemIcons.dataAndStorage, presentationData.strings.Settings_ChatSettings))
|
|
entries.append(.themes(presentationData.theme, SettingsItemIcons.appearance, presentationData.strings.Settings_Appearance))
|
|
let languageName = presentationData.strings.primaryComponent.localizedName
|
|
entries.append(.language(presentationData.theme, SettingsItemIcons.language, presentationData.strings.Settings_AppLanguage, languageName.isEmpty ? presentationData.strings.Localization_LanguageName : languageName))
|
|
|
|
if hasPassport {
|
|
entries.append(.passport(presentationData.theme, SettingsItemIcons.passport, presentationData.strings.Settings_Passport, ""))
|
|
}
|
|
if hasWatchApp {
|
|
entries.append(.watch(presentationData.theme, SettingsItemIcons.watch, presentationData.strings.Settings_AppleWatch, ""))
|
|
}
|
|
|
|
entries.append(.askAQuestion(presentationData.theme, SettingsItemIcons.support, presentationData.strings.Settings_Support))
|
|
entries.append(.faq(presentationData.theme, SettingsItemIcons.faq, presentationData.strings.Settings_FAQ))
|
|
}
|
|
|
|
return entries
|
|
}
|
|
|
|
public protocol SettingsController: class {
|
|
func updateAccount(account: Account)
|
|
}
|
|
|
|
private final class SettingsControllerImpl: ItemListController<SettingsEntry>, SettingsController {
|
|
let accountValue: Promise<Account>
|
|
|
|
init(currentAccount: Account, accountValue: Promise<Account>, state: Signal<(ItemListControllerState, (ItemListNodeState<SettingsEntry>, SettingsEntry.ItemGenerationArguments)), NoError>, tabBarItem: Signal<ItemListControllerTabBarItem, NoError>?) {
|
|
self.accountValue = accountValue
|
|
let presentationData = currentAccount.telegramApplicationContext.currentPresentationData.with { $0 }
|
|
|
|
self.accountValue.set(.single(currentAccount))
|
|
|
|
let updatedPresentationData = self.accountValue.get()
|
|
|> mapToSignal { account -> Signal<(theme: PresentationTheme, strings: PresentationStrings), NoError> in
|
|
return account.telegramApplicationContext.presentationData
|
|
|> map { ($0.theme, $0.strings) }
|
|
}
|
|
|
|
super.init(theme: presentationData.theme, strings: presentationData.strings, updatedPresentationData: updatedPresentationData, state: state, tabBarItem: tabBarItem)
|
|
}
|
|
|
|
required init(coder aDecoder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
func updateAccount(account: Account) {
|
|
self.accountValue.set(.single(account))
|
|
}
|
|
}
|
|
|
|
public func settingsController(account currentAccount: Account, accountManager: AccountManager) -> SettingsController & ViewController {
|
|
let statePromise = ValuePromise(SettingsState(), ignoreRepeated: true)
|
|
let stateValue = Atomic(value: SettingsState())
|
|
let updateState: ((SettingsState) -> SettingsState) -> Void = { f in
|
|
statePromise.set(stateValue.modify { f($0) })
|
|
}
|
|
|
|
var pushControllerImpl: ((ViewController) -> Void)?
|
|
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
|
var getNavigationControllerImpl: (() -> NavigationController?)?
|
|
|
|
let actionsDisposable = DisposableSet()
|
|
|
|
let updateAvatarDisposable = MetaDisposable()
|
|
actionsDisposable.add(updateAvatarDisposable)
|
|
|
|
let supportPeerDisposable = MetaDisposable()
|
|
actionsDisposable.add(supportPeerDisposable)
|
|
|
|
let hiddenAvatarRepresentationDisposable = MetaDisposable()
|
|
actionsDisposable.add(hiddenAvatarRepresentationDisposable)
|
|
|
|
let updatePassportDisposable = MetaDisposable()
|
|
actionsDisposable.add(updatePassportDisposable)
|
|
|
|
let openEditingDisposable = MetaDisposable()
|
|
actionsDisposable.add(openEditingDisposable)
|
|
|
|
let currentAvatarMixin = Atomic<TGMediaAvatarMenuMixin?>(value: nil)
|
|
|
|
var avatarGalleryTransitionArguments: ((AvatarGalleryEntry) -> GalleryTransitionArguments?)?
|
|
let avatarAndNameInfoContext = ItemListAvatarAndNameInfoItemContext()
|
|
var updateHiddenAvatarImpl: (() -> Void)?
|
|
var changeProfilePhotoImpl: (() -> Void)?
|
|
var openSavedMessagesImpl: (() -> Void)?
|
|
var displayCopyContextMenuImpl: ((Peer) -> Void)?
|
|
|
|
let archivedPacks = Promise<[ArchivedStickerPackItem]?>()
|
|
|
|
let accountValue = Promise<Account>()
|
|
|
|
let networkArguments = currentAccount.networkArguments
|
|
let auxiliaryMethods = currentAccount.auxiliaryMethods
|
|
let rootPath = rootPathForBasePath(currentAccount.telegramApplicationContext.applicationBindings.containerPath)
|
|
|
|
let accountsAndPeers: Signal<[(Account, Peer, Int32)], NoError> = accountManager.accountRecords()
|
|
|> map { view -> [AccountRecordId] in
|
|
return view.records.compactMap { record -> AccountRecordId? in
|
|
if record.attributes.contains(where: { $0 is LoggedOutAccountAttribute }) {
|
|
return nil
|
|
}
|
|
return record.id
|
|
}
|
|
}
|
|
|> distinctUntilChanged
|
|
|> mapToSignal { recordIds -> Signal<[(Account, Peer, Int32)], NoError> in
|
|
return accountValue.get()
|
|
|> mapToSignal { currentAccount -> Signal<[(Account, Peer, Int32)], NoError> in
|
|
var accounts: [Signal<(Account, Peer, Int32)?, NoError>] = []
|
|
func accountWithPeer(_ account: Signal<Account?, NoError>) -> Signal<(Account, Peer, Int32)?, NoError> {
|
|
return account
|
|
|> mapToSignal { account -> Signal<(Account, Peer, Int32)?, NoError> in
|
|
guard let account = account else {
|
|
return .single(nil)
|
|
}
|
|
return combineLatest(account.postbox.peerView(id: account.peerId), renderedTotalUnreadCount(postbox: account.postbox))
|
|
|> map { view, totalUnreadCount -> (Peer?, Int32) in
|
|
return (view.peers[view.peerId], totalUnreadCount.0)
|
|
}
|
|
|> distinctUntilChanged { lhs, rhs in
|
|
return arePeersEqual(lhs.0, rhs.0) && lhs.1 == rhs.1
|
|
}
|
|
|> map { peer, totalUnreadCount -> (Account, Peer, Int32)? in
|
|
if let peer = peer {
|
|
return (account, peer, totalUnreadCount)
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for id in recordIds {
|
|
if id == currentAccount.id {
|
|
continue
|
|
} else {
|
|
accounts.append(accountWithPeer(accountWithId(networkArguments: networkArguments, id: id, supplementary: true, rootPath: rootPath, beginWithTestingEnvironment: false, auxiliaryMethods: auxiliaryMethods)
|
|
|> map { result -> Account? in
|
|
if case let .authorized(account) = result {
|
|
return account
|
|
} else {
|
|
return nil
|
|
}
|
|
}))
|
|
}
|
|
}
|
|
return combineLatest(accounts)
|
|
|> map { accounts -> [(Account, Peer, Int32)] in
|
|
return accounts.compactMap({ $0 })
|
|
}
|
|
}
|
|
}
|
|
|
|
let openFaq: (Promise<ResolvedUrl>) -> Void = { resolvedUrl in
|
|
let _ = (accountValue.get()
|
|
|> deliverOnMainQueue
|
|
|> take(1)).start(next: { account in
|
|
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
|
let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: nil))
|
|
presentControllerImpl?(controller, nil)
|
|
let _ = (resolvedUrl.get()
|
|
|> take(1)
|
|
|> deliverOnMainQueue).start(next: { [weak controller] resolvedUrl in
|
|
controller?.dismiss()
|
|
|
|
openResolvedUrl(resolvedUrl, account: account, navigationController: getNavigationControllerImpl?(), openPeer: { peer, navigation in
|
|
}, present: { controller, arguments in
|
|
pushControllerImpl?(controller)
|
|
}, dismissInput: {})
|
|
})
|
|
})
|
|
}
|
|
|
|
let resolvedUrl = accountValue.get()
|
|
|> deliverOnMainQueue
|
|
|> mapToSignal { account -> Signal<ResolvedUrl, NoError> in
|
|
var faqUrl = account.telegramApplicationContext.currentPresentationData.with { $0 }.strings.Settings_FAQ_URL
|
|
if faqUrl == "Settings.FAQ_URL" || faqUrl.isEmpty {
|
|
faqUrl = "https://telegram.org/faq#general"
|
|
}
|
|
return resolveInstantViewUrl(account: account, url: faqUrl)
|
|
}
|
|
|
|
var switchToAccountImpl: ((AccountRecordId) -> Void)?
|
|
|
|
let arguments = SettingsItemArguments(accountManager: accountManager, avatarAndNameInfoContext: avatarAndNameInfoContext, avatarTapAction: {
|
|
var updating = false
|
|
updateState {
|
|
updating = $0.updatingAvatar != nil
|
|
return $0
|
|
}
|
|
|
|
if updating {
|
|
return
|
|
}
|
|
|
|
let _ = (accountValue.get()
|
|
|> deliverOnMainQueue
|
|
|> take(1)).start(next: { account in
|
|
let _ = (account.postbox.loadedPeerWithId(account.peerId)
|
|
|> take(1)
|
|
|> deliverOnMainQueue).start(next: { peer in
|
|
if peer.smallProfileImage != nil {
|
|
let galleryController = AvatarGalleryController(account: account, peer: peer, replaceRootController: { controller, ready in
|
|
|
|
})
|
|
hiddenAvatarRepresentationDisposable.set((galleryController.hiddenMedia |> deliverOnMainQueue).start(next: { entry in
|
|
avatarAndNameInfoContext.hiddenAvatarRepresentation = entry?.representations.first?.representation
|
|
updateHiddenAvatarImpl?()
|
|
}))
|
|
presentControllerImpl?(galleryController, AvatarGalleryControllerPresentationArguments(transitionArguments: { entry in
|
|
return avatarGalleryTransitionArguments?(entry)
|
|
}))
|
|
} else {
|
|
changeProfilePhotoImpl?()
|
|
}
|
|
})
|
|
})
|
|
}, changeProfilePhoto: {
|
|
changeProfilePhotoImpl?()
|
|
}, openUsername: {
|
|
let _ = (accountValue.get()
|
|
|> deliverOnMainQueue
|
|
|> take(1)).start(next: { account in
|
|
presentControllerImpl?(usernameSetupController(account: account), nil)
|
|
})
|
|
}, openProxy: {
|
|
let _ = (accountValue.get()
|
|
|> deliverOnMainQueue
|
|
|> take(1)).start(next: { account in
|
|
pushControllerImpl?(proxySettingsController(account: account))
|
|
})
|
|
}, openSavedMessages: {
|
|
openSavedMessagesImpl?()
|
|
}, openRecentCalls: {
|
|
let _ = (accountValue.get()
|
|
|> deliverOnMainQueue
|
|
|> take(1)).start(next: { account in
|
|
pushControllerImpl?(CallListController(account: account, mode: .navigation))
|
|
})
|
|
}, openPrivacyAndSecurity: {
|
|
let _ = (accountValue.get()
|
|
|> deliverOnMainQueue
|
|
|> take(1)).start(next: { account in
|
|
pushControllerImpl?(privacyAndSecurityController(account: account, initialSettings: .single(nil) |> then(requestAccountPrivacySettings(account: account) |> map(Optional.init))))
|
|
})
|
|
}, openDataAndStorage: {
|
|
let _ = (accountValue.get()
|
|
|> deliverOnMainQueue
|
|
|> take(1)).start(next: { account in
|
|
pushControllerImpl?(dataAndStorageController(account: account))
|
|
})
|
|
}, openStickerPacks: { archivedPacksValue in
|
|
let _ = (accountValue.get()
|
|
|> deliverOnMainQueue
|
|
|> take(1)).start(next: { account in
|
|
pushControllerImpl?(installedStickerPacksController(account: account, mode: .general, archivedPacks: archivedPacksValue, updatedPacks: { packs in
|
|
archivedPacks.set(.single(packs))
|
|
}))
|
|
})
|
|
}, openNotificationsAndSounds: { exceptionsList in
|
|
let _ = (accountValue.get()
|
|
|> deliverOnMainQueue
|
|
|> take(1)).start(next: { account in
|
|
pushControllerImpl?(notificationsAndSoundsController(account: account, exceptionsList: exceptionsList))
|
|
})
|
|
}, openThemes: {
|
|
let _ = (accountValue.get()
|
|
|> deliverOnMainQueue
|
|
|> take(1)).start(next: { account in
|
|
pushControllerImpl?(themeSettingsController(account: account))
|
|
})
|
|
}, pushController: { controller in
|
|
pushControllerImpl?(controller)
|
|
}, openLanguage: {
|
|
let _ = (accountValue.get()
|
|
|> deliverOnMainQueue
|
|
|> take(1)).start(next: { account in
|
|
pushControllerImpl?(LocalizationListController(account: account))
|
|
})
|
|
}, openPassport: {
|
|
let _ = (accountValue.get()
|
|
|> deliverOnMainQueue
|
|
|> take(1)).start(next: { account in
|
|
let controller = SecureIdAuthController(account: account, mode: .list)
|
|
presentControllerImpl?(controller, nil)
|
|
})
|
|
}, openWatch: {
|
|
let _ = (accountValue.get()
|
|
|> deliverOnMainQueue
|
|
|> take(1)).start(next: { account in
|
|
let controller = watchSettingsController(account: account)
|
|
pushControllerImpl?(controller)
|
|
})
|
|
}, openSupport: {
|
|
let _ = (accountValue.get()
|
|
|> deliverOnMainQueue
|
|
|> take(1)).start(next: { account in
|
|
let supportPeer = Promise<PeerId?>()
|
|
supportPeer.set(supportPeerId(account: account))
|
|
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
|
|
|
let resolvedUrlPromise = Promise<ResolvedUrl>()
|
|
resolvedUrlPromise.set(resolvedUrl)
|
|
|
|
presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Settings_FAQ_Intro, actions: [
|
|
TextAlertAction(type: .genericAction, title: presentationData.strings.Settings_FAQ_Button, action: {
|
|
openFaq(resolvedUrlPromise)
|
|
}),
|
|
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
|
|
supportPeerDisposable.set((supportPeer.get() |> take(1) |> deliverOnMainQueue).start(next: { peerId in
|
|
if let peerId = peerId {
|
|
pushControllerImpl?(ChatController(account: account, chatLocation: .peer(peerId)))
|
|
}
|
|
}))
|
|
})
|
|
]), nil)
|
|
})
|
|
}, openFaq: {
|
|
let resolvedUrlPromise = Promise<ResolvedUrl>()
|
|
resolvedUrlPromise.set(resolvedUrl)
|
|
|
|
openFaq(resolvedUrlPromise)
|
|
}, openEditing: {
|
|
let _ = (accountValue.get()
|
|
|> deliverOnMainQueue
|
|
|> take(1)).start(next: { account in
|
|
var cancelImpl: (() -> Void)?
|
|
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
|
let progressSignal = Signal<Never, NoError> { subscriber in
|
|
let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: {
|
|
cancelImpl?()
|
|
}))
|
|
presentControllerImpl?(controller, nil)
|
|
return ActionDisposable { [weak controller] in
|
|
Queue.mainQueue().async() {
|
|
controller?.dismiss()
|
|
}
|
|
}
|
|
}
|
|
|> runOn(Queue.mainQueue())
|
|
|> delay(0.15, queue: Queue.mainQueue())
|
|
let progressDisposable = progressSignal.start()
|
|
|
|
let peerKey: PostboxViewKey = .peer(peerId: account.peerId, components: [])
|
|
let cachedDataKey: PostboxViewKey = .cachedPeerData(peerId: account.peerId)
|
|
let signal = (account.postbox.combinedView(keys: [peerKey, cachedDataKey])
|
|
|> mapToSignal { view -> Signal<(TelegramUser, CachedUserData), NoError> in
|
|
guard let cachedDataView = view.views[cachedDataKey] as? CachedPeerDataView, let cachedData = cachedDataView.cachedPeerData as? CachedUserData else {
|
|
return .complete()
|
|
}
|
|
guard let peerView = view.views[peerKey] as? PeerView, let peer = peerView.peers[account.peerId] as? TelegramUser else {
|
|
return .complete()
|
|
}
|
|
return .single((peer, cachedData))
|
|
}
|
|
|> take(1))
|
|
|> afterDisposed {
|
|
Queue.mainQueue().async {
|
|
progressDisposable.dispose()
|
|
}
|
|
}
|
|
cancelImpl = {
|
|
openEditingDisposable.set(nil)
|
|
}
|
|
openEditingDisposable.set((signal
|
|
|> deliverOnMainQueue).start(next: { peer, cachedData in
|
|
pushControllerImpl?(editSettingsController(account: account, currentName: .personName(firstName: peer.firstName ?? "", lastName: peer.lastName ?? ""), currentBioText: cachedData.about ?? "", accountManager: accountManager))
|
|
}))
|
|
})
|
|
}, displayCopyContextMenu: {
|
|
let _ = (accountValue.get()
|
|
|> deliverOnMainQueue
|
|
|> take(1)).start(next: { account in
|
|
let _ = (account.postbox.transaction { transaction -> (Peer?) in
|
|
return transaction.getPeer(account.peerId)
|
|
}
|
|
|> deliverOnMainQueue).start(next: { peer in
|
|
if let peer = peer {
|
|
displayCopyContextMenuImpl?(peer)
|
|
}
|
|
})
|
|
})
|
|
}, switchToAccount: { id in
|
|
switchToAccountImpl?(id)
|
|
}, addAccount: {
|
|
let _ = (accountValue.get()
|
|
|> deliverOnMainQueue
|
|
|> take(1)).start(next: { account in
|
|
let isTestingEnvironment = account.testingEnvironment
|
|
let _ = accountManager.transaction({ transaction -> Void in
|
|
let id = transaction.createRecord([AccountEnvironmentAttribute(environment: isTestingEnvironment ? .test : .production)])
|
|
transaction.setCurrentId(id)
|
|
}).start()
|
|
})
|
|
})
|
|
|
|
changeProfilePhotoImpl = {
|
|
let _ = (accountValue.get()
|
|
|> deliverOnMainQueue
|
|
|> take(1)).start(next: { account in
|
|
let _ = (account.postbox.transaction { transaction -> (Peer?, SearchBotsConfiguration) in
|
|
return (transaction.getPeer(account.peerId), currentSearchBotsConfiguration(transaction: transaction))
|
|
}
|
|
|> deliverOnMainQueue).start(next: { peer, searchBotsConfiguration in
|
|
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
|
|
|
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme)
|
|
legacyController.statusBar.statusBarStyle = .Ignore
|
|
|
|
let emptyController = LegacyEmptyController(context: legacyController.context)!
|
|
let navigationController = makeLegacyNavigationController(rootController: emptyController)
|
|
navigationController.setNavigationBarHidden(true, animated: false)
|
|
navigationController.navigationBar.transform = CGAffineTransform(translationX: -1000.0, y: 0.0)
|
|
|
|
legacyController.bind(controller: navigationController)
|
|
|
|
presentControllerImpl?(legacyController, nil)
|
|
|
|
var hasPhotos = false
|
|
if let peer = peer, !peer.profileImageRepresentations.isEmpty {
|
|
hasPhotos = true
|
|
}
|
|
|
|
let completedImpl: (UIImage) -> Void = { image in
|
|
if let data = UIImageJPEGRepresentation(image, 0.6) {
|
|
let resource = LocalFileMediaResource(fileId: arc4random64())
|
|
account.postbox.mediaBox.storeResourceData(resource.id, data: data)
|
|
let representation = TelegramMediaImageRepresentation(dimensions: CGSize(width: 640.0, height: 640.0), resource: resource)
|
|
updateState {
|
|
$0.withUpdatedUpdatingAvatar(.image(representation, true))
|
|
}
|
|
updateAvatarDisposable.set((updateAccountPhoto(account: account, resource: resource, mapResourceToAvatarSizes: { resource, representations in
|
|
return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
|
|
}) |> deliverOnMainQueue).start(next: { result in
|
|
switch result {
|
|
case .complete:
|
|
updateState {
|
|
$0.withUpdatedUpdatingAvatar(nil)
|
|
}
|
|
case .progress:
|
|
break
|
|
}
|
|
}))
|
|
}
|
|
}
|
|
|
|
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: hasPhotos, hasViewButton: false, personalPhoto: true, saveEditedPhotos: false, saveCapturedMedia: false, signup: false)!
|
|
let _ = currentAvatarMixin.swap(mixin)
|
|
mixin.requestSearchController = { assetsController in
|
|
let controller = WebSearchController(account: account, peer: peer, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: nil, completion: { result in
|
|
assetsController?.dismiss()
|
|
completedImpl(result)
|
|
}))
|
|
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
}
|
|
mixin.didFinishWithImage = { image in
|
|
if let image = image {
|
|
completedImpl(image)
|
|
}
|
|
}
|
|
mixin.didFinishWithDelete = {
|
|
let _ = currentAvatarMixin.swap(nil)
|
|
updateState {
|
|
if let profileImage = peer?.smallProfileImage {
|
|
return $0.withUpdatedUpdatingAvatar(.image(profileImage, false))
|
|
} else {
|
|
return $0.withUpdatedUpdatingAvatar(.none)
|
|
}
|
|
}
|
|
updateAvatarDisposable.set((updateAccountPhoto(account: account, resource: nil, mapResourceToAvatarSizes: { resource, representations in
|
|
return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
|
|
}) |> deliverOnMainQueue).start(next: { result in
|
|
switch result {
|
|
case .complete:
|
|
updateState {
|
|
$0.withUpdatedUpdatingAvatar(nil)
|
|
}
|
|
case .progress:
|
|
break
|
|
}
|
|
}))
|
|
}
|
|
mixin.didDismiss = { [weak legacyController] in
|
|
let _ = currentAvatarMixin.swap(nil)
|
|
legacyController?.dismiss()
|
|
}
|
|
let menuController = mixin.present()
|
|
if let menuController = menuController {
|
|
menuController.customRemoveFromParentViewController = { [weak legacyController] in
|
|
legacyController?.dismiss()
|
|
}
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
let peerView = accountValue.get()
|
|
|> mapToSignal { account -> Signal<PeerView, NoError> in
|
|
return account.viewTracker.peerView(account.peerId)
|
|
}
|
|
|
|
archivedPacks.set(
|
|
.single(nil)
|
|
|> then(
|
|
accountValue.get()
|
|
|> mapToSignal { account -> Signal<[ArchivedStickerPackItem]?, NoError> in
|
|
archivedStickerPacks(account: account)
|
|
|> map(Optional.init)
|
|
}
|
|
)
|
|
)
|
|
|
|
let hasPassport = ValuePromise<Bool>(false)
|
|
let updatePassport: () -> Void = {
|
|
updatePassportDisposable.set((
|
|
accountValue.get()
|
|
|> take(1)
|
|
|> mapToSignal { account -> Signal<Bool, NoError> in
|
|
return twoStepAuthData(account.network)
|
|
|> map { value -> Bool in
|
|
return value.hasSecretValues
|
|
}
|
|
|> `catch` { _ -> Signal<Bool, NoError> in
|
|
return .single(false)
|
|
}
|
|
}
|
|
|> deliverOnMainQueue).start(next: { value in
|
|
hasPassport.set(value)
|
|
}))
|
|
}
|
|
updatePassport()
|
|
|
|
let notificationsAuthorizationStatus = Promise<AccessType>(.allowed)
|
|
if #available(iOSApplicationExtension 10.0, *) {
|
|
notificationsAuthorizationStatus.set(
|
|
.single(.allowed)
|
|
|> then(
|
|
accountValue.get()
|
|
|> mapToSignal { account -> Signal<AccessType, NoError> in
|
|
return DeviceAccess.authorizationStatus(account: account, subject: .notifications)
|
|
}
|
|
)
|
|
)
|
|
}
|
|
|
|
let notificationsWarningSuppressed = Promise<Bool>(true)
|
|
if #available(iOSApplicationExtension 10.0, *) {
|
|
let warningKey = PostboxViewKey.noticeEntry(ApplicationSpecificNotice.notificationsPermissionWarningKey())
|
|
notificationsWarningSuppressed.set(
|
|
.single(true)
|
|
|> then(
|
|
accountValue.get()
|
|
|> mapToSignal { account -> Signal<Bool, NoError> in
|
|
return account.postbox.combinedView(keys: [warningKey])
|
|
|> map { combined -> Bool in
|
|
let timestamp = (combined.views[warningKey] as? NoticeEntryView)?.value.flatMap({ ApplicationSpecificNotice.getTimestampValue($0) })
|
|
if let timestamp = timestamp, timestamp > 0 {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
)
|
|
)
|
|
}
|
|
|
|
let notifyExceptions = Promise<NotificationExceptionsList?>(nil)
|
|
let updateNotifyExceptions: () -> Void = {
|
|
notifyExceptions.set(
|
|
accountValue.get()
|
|
|> take(1)
|
|
|> mapToSignal { account -> Signal<NotificationExceptionsList?, NoError> in
|
|
return notificationExceptionsList(network: account.network)
|
|
|> map(Optional.init)
|
|
}
|
|
)
|
|
}
|
|
|
|
let hasWatchApp = Promise<Bool>(false)
|
|
hasWatchApp.set(
|
|
accountValue.get()
|
|
|> mapToSignal { account -> Signal<Bool, NoError> in
|
|
if let context = account.applicationContext as? TelegramApplicationContext, let watchManager = context.watchManager {
|
|
return watchManager.watchAppInstalled
|
|
} else {
|
|
return .single(false)
|
|
}
|
|
}
|
|
)
|
|
|
|
let updatedPresentationData = accountValue.get()
|
|
|> mapToSignal { account -> Signal<PresentationData, NoError> in
|
|
return account.telegramApplicationContext.presentationData
|
|
}
|
|
|
|
let proxyPreferences = accountValue.get()
|
|
|> mapToSignal { account in
|
|
return account.postbox.preferencesView(keys: [PreferencesKeys.proxySettings])
|
|
}
|
|
|
|
let featuredStickerPacks = accountValue.get()
|
|
|> mapToSignal { account in
|
|
return account.viewTracker.featuredStickerPacks()
|
|
}
|
|
|
|
let signal = combineLatest(queue: Queue.mainQueue(), accountValue.get(), updatedPresentationData, statePromise.get(), peerView, combineLatest(queue: Queue.mainQueue(), proxyPreferences, notifyExceptions.get(), notificationsAuthorizationStatus.get(), notificationsWarningSuppressed.get()), combineLatest(featuredStickerPacks, archivedPacks.get()), combineLatest(hasPassport.get(), hasWatchApp.get()), accountsAndPeers)
|
|
|> map { account, presentationData, state, view, preferencesAndExceptions, featuredAndArchived, hasPassportAndWatch, accountsAndPeers -> (ItemListControllerState, (ItemListNodeState<SettingsEntry>, SettingsEntry.ItemGenerationArguments)) in
|
|
let proxySettings: ProxySettings = preferencesAndExceptions.0.values[PreferencesKeys.proxySettings] as? ProxySettings ?? ProxySettings.defaultSettings
|
|
|
|
let rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Edit), style: .regular, enabled: true, action: {
|
|
arguments.openEditing()
|
|
})
|
|
|
|
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Settings_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
|
|
|
var unreadTrendingStickerPacks = 0
|
|
for item in featuredAndArchived.0 {
|
|
if item.unread {
|
|
unreadTrendingStickerPacks += 1
|
|
}
|
|
}
|
|
|
|
let (hasPassport, hasWatchApp) = hasPassportAndWatch
|
|
let listState = ItemListNodeState(entries: settingsEntries(account: account, presentationData: presentationData, state: state, view: view, proxySettings: proxySettings, notifyExceptions: preferencesAndExceptions.1, notificationsAuthorizationStatus: preferencesAndExceptions.2, notificationsWarningSuppressed: preferencesAndExceptions.3, unreadTrendingStickerPacks: unreadTrendingStickerPacks, archivedPacks: featuredAndArchived.1, hasPassport: hasPassport, hasWatchApp: hasWatchApp, accountsAndPeers: accountsAndPeers), style: .blocks)
|
|
|
|
return (controllerState, (listState, arguments))
|
|
}
|
|
|> afterDisposed {
|
|
actionsDisposable.dispose()
|
|
}
|
|
|
|
let icon: UIImage?
|
|
if (useSpecialTabBarIcons()) {
|
|
icon = UIImage(bundleImageName: "Chat List/Tabs/NY/IconSettings")
|
|
} else {
|
|
icon = UIImage(bundleImageName: "Chat List/Tabs/IconSettings")
|
|
}
|
|
|
|
let controller = SettingsControllerImpl(currentAccount: currentAccount, accountValue: accountValue, state: signal, tabBarItem: combineLatest(updatedPresentationData, notificationsAuthorizationStatus.get(), notificationsWarningSuppressed.get()) |> map { presentationData, notificationsAuthorizationStatus, notificationsWarningSuppressed in
|
|
let notificationsWarning = shouldDisplayNotificationsPermissionWarning(status: notificationsAuthorizationStatus, suppressed: notificationsWarningSuppressed)
|
|
return ItemListControllerTabBarItem(title: presentationData.strings.Settings_Title, image: icon, selectedImage: icon, badgeValue: notificationsWarning ? "!" : nil)
|
|
})
|
|
pushControllerImpl = { [weak controller] value in
|
|
(controller?.navigationController as? NavigationController)?.replaceAllButRootController(value, animated: true)
|
|
}
|
|
presentControllerImpl = { [weak controller] value, arguments in
|
|
controller?.present(value, in: .window(.root), with: arguments ?? ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
}
|
|
getNavigationControllerImpl = { [weak controller] in
|
|
return (controller?.navigationController as? NavigationController)
|
|
}
|
|
avatarGalleryTransitionArguments = { [weak controller] entry in
|
|
if let controller = controller {
|
|
var result: ((ASDisplayNode, () -> UIView?), CGRect)?
|
|
controller.forEachItemNode { itemNode in
|
|
if let itemNode = itemNode as? ItemListAvatarAndNameInfoItemNode {
|
|
result = itemNode.avatarTransitionNode()
|
|
}
|
|
}
|
|
if let (node, _) = result {
|
|
return GalleryTransitionArguments(transitionNode: node, addToTransitionSurface: { _ in
|
|
})
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
updateHiddenAvatarImpl = { [weak controller] in
|
|
if let controller = controller {
|
|
controller.forEachItemNode { itemNode in
|
|
if let itemNode = itemNode as? ItemListAvatarAndNameInfoItemNode {
|
|
itemNode.updateAvatarHidden()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
openSavedMessagesImpl = { [weak controller] in
|
|
let _ = (accountValue.get()
|
|
|> take(1)
|
|
|> deliverOnMainQueue).start(next: { account in
|
|
if let controller = controller, let navigationController = controller.navigationController as? NavigationController {
|
|
navigateToChatController(navigationController: navigationController, account: account, chatLocation: .peer(account.peerId))
|
|
}
|
|
})
|
|
}
|
|
controller.tabBarItemDebugTapAction = {
|
|
let _ = (accountValue.get()
|
|
|> take(1)
|
|
|> deliverOnMainQueue).start(next: { account in
|
|
pushControllerImpl?(debugController(account: account, accountManager: accountManager))
|
|
})
|
|
}
|
|
|
|
displayCopyContextMenuImpl = { [weak controller] peer in
|
|
let _ = (accountValue.get()
|
|
|> take(1)
|
|
|> deliverOnMainQueue).start(next: { account in
|
|
if let strongController = controller {
|
|
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
|
var resultItemNode: ListViewItemNode?
|
|
let _ = strongController.frameForItemNode({ itemNode in
|
|
if let itemNode = itemNode as? ItemListAvatarAndNameInfoItemNode {
|
|
resultItemNode = itemNode
|
|
return true
|
|
}
|
|
return false
|
|
})
|
|
if let resultItemNode = resultItemNode, let user = peer as? TelegramUser {
|
|
var actions: [ContextMenuAction] = []
|
|
|
|
if let phone = user.phone, !phone.isEmpty {
|
|
actions.append(ContextMenuAction(content: .text(presentationData.strings.Settings_CopyPhoneNumber), action: {
|
|
UIPasteboard.general.string = formatPhoneNumber(phone)
|
|
}))
|
|
}
|
|
|
|
if let username = user.username, !username.isEmpty {
|
|
actions.append(ContextMenuAction(content: .text(presentationData.strings.Settings_CopyUsername), action: {
|
|
UIPasteboard.general.string = username
|
|
}))
|
|
}
|
|
|
|
let contextMenuController = ContextMenuController(actions: actions)
|
|
strongController.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak resultItemNode] in
|
|
if let strongController = controller, let resultItemNode = resultItemNode {
|
|
return (resultItemNode, resultItemNode.contentBounds.insetBy(dx: 0.0, dy: -2.0), strongController.displayNode, strongController.view.bounds)
|
|
} else {
|
|
return nil
|
|
}
|
|
}))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
switchToAccountImpl = { [weak controller] id in
|
|
AccountStore.switchToAccount(id: id, fromSettingsController: controller)
|
|
}
|
|
controller.didAppear = { _ in
|
|
updatePassport()
|
|
updateNotifyExceptions()
|
|
}
|
|
return controller
|
|
}
|
|
|