mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2026-04-02 03:50:06 +00:00
396 lines
22 KiB
Swift
396 lines
22 KiB
Swift
import SGStrings
|
|
import SGSettingsUI
|
|
import Foundation
|
|
import UIKit
|
|
import Display
|
|
import AccountContext
|
|
import SwiftSignalKit
|
|
import Postbox
|
|
import TelegramCore
|
|
import SettingsUI
|
|
import PeerInfoStoryGridScreen
|
|
import CallListUI
|
|
import PassportUI
|
|
import AccountUtils
|
|
import OverlayStatusController
|
|
import PremiumUI
|
|
import TelegramPresentationData
|
|
import PresentationDataUtils
|
|
import PasswordSetupUI
|
|
import InstantPageCache
|
|
|
|
extension PeerInfoScreenNode {
|
|
func openSettings(section: PeerInfoSettingsSection) {
|
|
let push: (ViewController) -> Void = { [weak self] c in
|
|
guard let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController else {
|
|
return
|
|
}
|
|
|
|
if strongSelf.isMyProfile {
|
|
navigationController.pushViewController(c)
|
|
} else {
|
|
var updatedControllers = navigationController.viewControllers
|
|
for controller in navigationController.viewControllers.reversed() {
|
|
if controller !== strongSelf && !(controller is TabBarController) {
|
|
updatedControllers.removeLast()
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
updatedControllers.append(c)
|
|
|
|
var animated = true
|
|
if let validLayout = strongSelf.validLayout?.0, case .regular = validLayout.metrics.widthClass {
|
|
animated = false
|
|
}
|
|
navigationController.setViewControllers(updatedControllers, animated: animated)
|
|
}
|
|
}
|
|
switch section {
|
|
case .swiftgram:
|
|
self.controller?.push(sgSettingsController(context: self.context))
|
|
case .swiftgramPro:
|
|
if self.context.sharedContext.immediateSGStatus.status > 1 {
|
|
self.controller?.push(self.context.sharedContext.makeSGProController(context: self.context))
|
|
} else {
|
|
if let payWallController = self.context.sharedContext.makeSGPayWallController(context: self.context) {
|
|
self.controller?.present(payWallController, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
} else {
|
|
self.controller?.present(self.context.sharedContext.makeSGUpdateIOSController(), animated: true)
|
|
}
|
|
}
|
|
case .avatar:
|
|
self.controller?.openAvatarForEditing()
|
|
case .edit:
|
|
self.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil)
|
|
case .proxy:
|
|
self.controller?.push(proxySettingsController(context: self.context))
|
|
case .profile:
|
|
self.controller?.push(PeerInfoScreenImpl(
|
|
context: self.context,
|
|
updatedPresentationData: self.controller?.updatedPresentationData,
|
|
peerId: self.context.account.peerId,
|
|
avatarInitiallyExpanded: false,
|
|
isOpenedFromChat: false,
|
|
nearbyPeerDistance: nil,
|
|
reactionSourceMessageId: nil,
|
|
callMessages: [],
|
|
isMyProfile: true,
|
|
profileGiftsContext: self.data?.profileGiftsContext
|
|
))
|
|
case .stories:
|
|
push(PeerInfoStoryGridScreen(context: self.context, peerId: self.context.account.peerId, scope: .saved))
|
|
case .savedMessages:
|
|
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId))
|
|
|> deliverOnMainQueue).startStandalone(next: { [weak self] peer in
|
|
guard let self, let peer = peer else {
|
|
return
|
|
}
|
|
if let controller = self.controller, let navigationController = controller.navigationController as? NavigationController {
|
|
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer)))
|
|
}
|
|
})
|
|
case .recentCalls:
|
|
push(CallListController(context: context, mode: .navigation))
|
|
case .devices:
|
|
let _ = (self.activeSessionsContextAndCount.get()
|
|
|> take(1)
|
|
|> deliverOnMainQueue).startStandalone(next: { [weak self] activeSessionsContextAndCount in
|
|
if let strongSelf = self, let activeSessionsContextAndCount = activeSessionsContextAndCount {
|
|
let (activeSessionsContext, _, webSessionsContext) = activeSessionsContextAndCount
|
|
push(recentSessionsController(context: strongSelf.context, activeSessionsContext: activeSessionsContext, webSessionsContext: webSessionsContext, websitesOnly: false))
|
|
}
|
|
})
|
|
case .chatFolders:
|
|
let controller = self.context.sharedContext.makeFilterSettingsController(context: self.context, modal: false, scrollToTags: false, dismissed: nil)
|
|
push(controller)
|
|
case .notificationsAndSounds:
|
|
if let settings = self.data?.globalSettings {
|
|
push(notificationsAndSoundsController(context: self.context, exceptionsList: settings.notificationExceptions))
|
|
}
|
|
case .privacyAndSecurity:
|
|
if let settings = self.data?.globalSettings {
|
|
let _ = (combineLatest(self.blockedPeers.get(), self.hasTwoStepAuth.get())
|
|
|> take(1)
|
|
|> deliverOnMainQueue).startStandalone(next: { [weak self] blockedPeersContext, hasTwoStepAuth in
|
|
if let strongSelf = self {
|
|
let loginEmailPattern = strongSelf.twoStepAuthData.get() |> map { data -> String? in
|
|
return data?.loginEmailPattern
|
|
}
|
|
push(privacyAndSecurityController(context: strongSelf.context, initialSettings: settings.privacySettings, updatedSettings: { [weak self] settings in
|
|
self?.privacySettings.set(.single(settings))
|
|
}, updatedBlockedPeers: { [weak self] blockedPeersContext in
|
|
self?.blockedPeers.set(.single(blockedPeersContext))
|
|
}, updatedHasTwoStepAuth: { [weak self] hasTwoStepAuthValue in
|
|
self?.hasTwoStepAuth.set(.single(hasTwoStepAuthValue))
|
|
}, focusOnItemTag: nil, activeSessionsContext: settings.activeSessionsContext, webSessionsContext: settings.webSessionsContext, blockedPeersContext: blockedPeersContext, hasTwoStepAuth: hasTwoStepAuth, loginEmailPattern: loginEmailPattern, updatedTwoStepAuthData: { [weak self] in
|
|
if let strongSelf = self {
|
|
strongSelf.twoStepAuthData.set(
|
|
strongSelf.context.engine.auth.twoStepAuthData()
|
|
|> map(Optional.init)
|
|
|> `catch` { _ -> Signal<TwoStepAuthData?, NoError> in
|
|
return .single(nil)
|
|
}
|
|
)
|
|
}
|
|
}, requestPublicPhotoSetup: { [weak self] completion in
|
|
if let self {
|
|
self.controller?.openAvatarForEditing(mode: .fallback, completion: completion)
|
|
}
|
|
}, requestPublicPhotoRemove: { [weak self] completion in
|
|
if let self {
|
|
self.controller?.openAvatarRemoval(mode: .fallback, completion: completion)
|
|
}
|
|
}))
|
|
}
|
|
})
|
|
}
|
|
case .passwordSetup:
|
|
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.6, execute: { [weak self] in
|
|
guard let self else {
|
|
return
|
|
}
|
|
let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupPassword.id).startStandalone()
|
|
})
|
|
|
|
let controller = self.context.sharedContext.makeSetupTwoFactorAuthController(context: self.context)
|
|
push(controller)
|
|
case .dataAndStorage:
|
|
push(dataAndStorageController(context: self.context))
|
|
case .appearance:
|
|
push(themeSettingsController(context: self.context))
|
|
case .language:
|
|
push(LocalizationListController(context: self.context))
|
|
case .premium:
|
|
let controller = self.context.sharedContext.makePremiumIntroController(context: self.context, source: .settings, forceDark: false, dismissed: nil)
|
|
self.controller?.push(controller)
|
|
case .premiumGift:
|
|
guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else {
|
|
return
|
|
}
|
|
let _ = (self.context.account.stateManager.contactBirthdays
|
|
|> take(1)
|
|
|> deliverOnMainQueue).start(next: { [weak self] birthdays in
|
|
guard let self else {
|
|
return
|
|
}
|
|
let giftsController = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .settings(birthdays), completion: nil)
|
|
self.controller?.push(giftsController)
|
|
})
|
|
case .stickers:
|
|
if let settings = self.data?.globalSettings {
|
|
push(installedStickerPacksController(context: self.context, mode: .general, archivedPacks: settings.archivedStickerPacks, updatedPacks: { [weak self] packs in
|
|
self?.archivedPacks.set(.single(packs))
|
|
}))
|
|
}
|
|
case .passport:
|
|
self.controller?.push(SecureIdAuthController(context: self.context, mode: .list))
|
|
case .watch:
|
|
push(watchSettingsController(context: self.context))
|
|
case .support:
|
|
let supportPeer = Promise<PeerId?>()
|
|
supportPeer.set(context.engine.peers.supportPeerId())
|
|
|
|
self.controller?.present(textAlertController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, title: nil, text: self.presentationData.strings.Settings_FAQ_Intro, actions: [
|
|
TextAlertAction(type: .genericAction, title: presentationData.strings.Settings_FAQ_Button, action: { [weak self] in
|
|
self?.openFaq()
|
|
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { [weak self] in
|
|
guard let self else {
|
|
return
|
|
}
|
|
self.supportPeerDisposable.set((supportPeer.get() |> take(1) |> deliverOnMainQueue).startStrict(next: { [weak self] peerId in
|
|
if let strongSelf = self, let peerId = peerId {
|
|
push(strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(.default), params: nil))
|
|
}
|
|
}))
|
|
})]), in: .window(.root))
|
|
case .faq:
|
|
self.openFaq()
|
|
case .tips:
|
|
self.openTips()
|
|
case .phoneNumber:
|
|
guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else {
|
|
return
|
|
}
|
|
if let user = self.data?.peer as? TelegramUser, let phoneNumber = user.phone {
|
|
let introController = PrivacyIntroController(context: self.context, mode: .changePhoneNumber(phoneNumber), proceedAction: { [weak self] in
|
|
if let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController {
|
|
navigationController.replaceTopController(ChangePhoneNumberController(context: strongSelf.context), animated: true)
|
|
}
|
|
})
|
|
push(introController)
|
|
}
|
|
case .username:
|
|
guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else {
|
|
return
|
|
}
|
|
push(usernameSetupController(context: self.context))
|
|
case .addAccount:
|
|
let _ = (activeAccountsAndPeers(context: context)
|
|
|> take(1)
|
|
|> deliverOnMainQueue
|
|
).startStandalone(next: { [weak self] accountAndPeer, accountsAndPeers in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
var maximumAvailableAccounts: Int = maximumSwiftgramNumberOfAccounts
|
|
if accountAndPeer?.1.isPremium == true && !strongSelf.context.account.testingEnvironment {
|
|
maximumAvailableAccounts = maximumSwiftgramNumberOfAccounts
|
|
}
|
|
var count: Int = 1
|
|
for (accountContext, peer, _) in accountsAndPeers {
|
|
if !accountContext.account.testingEnvironment {
|
|
if peer.isPremium {
|
|
maximumAvailableAccounts = maximumSwiftgramNumberOfAccounts
|
|
}
|
|
count += 1
|
|
}
|
|
}
|
|
|
|
if count >= maximumAvailableAccounts {
|
|
var replaceImpl: ((ViewController) -> Void)?
|
|
let controller = PremiumLimitScreen(context: strongSelf.context, subject: .accounts, count: Int32(count), action: {
|
|
let controller = PremiumIntroScreen(context: strongSelf.context, source: .accounts)
|
|
replaceImpl?(controller)
|
|
return true
|
|
})
|
|
replaceImpl = { [weak controller] c in
|
|
controller?.replace(with: c)
|
|
}
|
|
if let navigationController = strongSelf.context.sharedContext.mainWindow?.viewController as? NavigationController {
|
|
navigationController.pushViewController(controller)
|
|
}
|
|
} else {
|
|
// MARK: Swiftgram
|
|
if count + 1 > maximumSafeNumberOfAccounts {
|
|
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
|
let alertController = textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, title: presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: i18n("Auth.AccountBackupReminder", presentationData.strings.baseLanguageCode), actions: [
|
|
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
|
|
strongSelf.context.sharedContext.beginNewAuth(testingEnvironment: strongSelf.context.account.testingEnvironment)
|
|
})
|
|
])
|
|
if let controller = strongSelf.controller {
|
|
controller.present(alertController, in: .window(.root))
|
|
} else {
|
|
strongSelf.context.sharedContext.beginNewAuth(testingEnvironment: strongSelf.context.account.testingEnvironment)
|
|
}
|
|
} else {
|
|
strongSelf.context.sharedContext.beginNewAuth(testingEnvironment: strongSelf.context.account.testingEnvironment)
|
|
}
|
|
//
|
|
}
|
|
})
|
|
case .logout:
|
|
if let user = self.data?.peer as? TelegramUser, let phoneNumber = user.phone {
|
|
if let controller = self.controller, let navigationController = controller.navigationController as? NavigationController {
|
|
self.controller?.push(logoutOptionsController(context: self.context, navigationController: navigationController, canAddAccounts: true, phoneNumber: phoneNumber))
|
|
}
|
|
}
|
|
case .rememberPassword:
|
|
let context = self.context
|
|
let controller = TwoFactorDataInputScreen(sharedContext: self.context.sharedContext, engine: .authorized(self.context.engine), mode: .rememberPassword(doneText: self.presentationData.strings.TwoFactorSetup_Done_Action), stateUpdated: { _ in
|
|
}, presentation: .modalInLargeLayout)
|
|
controller.twoStepAuthSettingsController = { configuration in
|
|
return twoStepVerificationUnlockSettingsController(context: context, mode: .access(intro: false, data: .single(TwoStepVerificationUnlockSettingsControllerData.access(configuration: TwoStepVerificationAccessConfiguration(configuration: configuration, password: nil)))))
|
|
}
|
|
controller.passwordRemembered = {
|
|
let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.validatePassword.id).startStandalone()
|
|
}
|
|
push(controller)
|
|
case .emojiStatus:
|
|
self.headerNode.invokeDisplayPremiumIntro()
|
|
case .profileColor:
|
|
self.interaction.editingOpenNameColorSetup()
|
|
case .powerSaving:
|
|
push(energySavingSettingsScreen(context: self.context))
|
|
case .businessSetup:
|
|
guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else {
|
|
return
|
|
}
|
|
push(self.context.sharedContext.makeBusinessSetupScreen(context: self.context))
|
|
case .premiumManagement:
|
|
guard let controller = self.controller else {
|
|
return
|
|
}
|
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
|
|
let url = premiumConfiguration.subscriptionManagementUrl
|
|
guard !url.isEmpty else {
|
|
return
|
|
}
|
|
self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: !url.hasPrefix("tg://") && !url.contains("?start="), presentationData: self.context.sharedContext.currentPresentationData.with({$0}), navigationController: controller.navigationController as? NavigationController, dismissInput: {})
|
|
case .stars:
|
|
if let starsContext = self.controller?.starsContext {
|
|
push(self.context.sharedContext.makeStarsTransactionsScreen(context: self.context, starsContext: starsContext))
|
|
}
|
|
case .ton:
|
|
if let tonContext = self.controller?.tonContext {
|
|
push(self.context.sharedContext.makeStarsTransactionsScreen(context: self.context, starsContext: tonContext))
|
|
}
|
|
}
|
|
}
|
|
|
|
func setupFaqIfNeeded() {
|
|
if !self.didSetCachedFaq {
|
|
self.cachedFaq.set(.single(nil) |> then(cachedFaqInstantPage(context: self.context) |> map(Optional.init)))
|
|
self.didSetCachedFaq = true
|
|
}
|
|
}
|
|
|
|
func openFaq(anchor: String? = nil) {
|
|
self.setupFaqIfNeeded()
|
|
|
|
let presentationData = self.presentationData
|
|
let progressSignal = Signal<Never, NoError> { [weak self] subscriber in
|
|
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
|
|
self?.controller?.present(controller, in: .window(.root))
|
|
return ActionDisposable { [weak controller] in
|
|
Queue.mainQueue().async() {
|
|
controller?.dismiss()
|
|
}
|
|
}
|
|
}
|
|
|> runOn(Queue.mainQueue())
|
|
|> delay(0.15, queue: Queue.mainQueue())
|
|
let progressDisposable = progressSignal.start()
|
|
|
|
let _ = (self.cachedFaq.get()
|
|
|> filter { $0 != nil }
|
|
|> take(1)
|
|
|> deliverOnMainQueue).start(next: { [weak self] resolvedUrl in
|
|
progressDisposable.dispose()
|
|
|
|
if let strongSelf = self, let resolvedUrl = resolvedUrl {
|
|
var resolvedUrl = resolvedUrl
|
|
if case let .instantView(webPage, _) = resolvedUrl, let customAnchor = anchor {
|
|
resolvedUrl = .instantView(webPage, customAnchor)
|
|
}
|
|
strongSelf.context.sharedContext.openResolvedUrl(resolvedUrl, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.controller?.navigationController as? NavigationController, forceExternal: false, forceUpdate: false, openPeer: { peer, navigation in
|
|
}, sendFile: nil, sendSticker: nil, sendEmoji: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { [weak self] controller, arguments in
|
|
self?.controller?.push(controller)
|
|
}, dismissInput: {}, contentContext: nil, progress: nil, completion: nil)
|
|
}
|
|
})
|
|
}
|
|
|
|
private func openTips() {
|
|
let controller = OverlayStatusController(theme: self.presentationData.theme, type: .loading(cancelled: nil))
|
|
self.controller?.present(controller, in: .window(.root))
|
|
|
|
let context = self.context
|
|
let navigationController = self.controller?.navigationController as? NavigationController
|
|
self.tipsPeerDisposable.set((self.context.engine.peers.resolvePeerByName(name: self.presentationData.strings.Settings_TipsUsername, referrer: nil)
|
|
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
|
guard case let .result(result) = result else {
|
|
return .complete()
|
|
}
|
|
return .single(result)
|
|
}
|
|
|> deliverOnMainQueue).startStrict(next: { [weak controller] peer in
|
|
controller?.dismiss()
|
|
if let peer = peer, let navigationController = navigationController {
|
|
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer)))
|
|
}
|
|
}))
|
|
}
|
|
}
|