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)) // MARK: - GLEGram case .gleGram: self.controller?.push(gleGramSettingsController(context: self.context)) // MARK: - End GLEGram 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 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() 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 { [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 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))) } })) } }