Files
ghostgram/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenSettingsActions.swift
T
ichmagmaus 812 db53826061 chore: migrate to new version + fixed several critical bugs
- Migrated project to latest Telegram iOS base (v12.3.2+)
- Fixed circular dependency between GhostModeManager and MiscSettingsManager
- Fixed multiple Bazel build configuration errors (select() default conditions)
- Fixed duplicate type definitions in PeerInfoScreen
- Fixed swiftmodule directory resolution in build scripts
- Added Ghostgram Settings tab in main Settings menu with all 5 features
- Cleared sensitive credentials from config.json (template-only now)
- Excluded bazel-cache from version control
2026-02-23 23:04:32 +01:00

357 lines
20 KiB
Swift

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
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 .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 = 3
if accountAndPeer?.1.isPremium == true && !strongSelf.context.account.testingEnvironment {
maximumAvailableAccounts = 4
}
var count: Int = 1
for (accountContext, peer, _) in accountsAndPeers {
if !accountContext.account.testingEnvironment {
if peer.isPremium {
maximumAvailableAccounts = 4
}
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 {
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))
}
case .ghostgram:
push(ghostgramSettingsController(context: self.context))
}
}
func openFaq(anchor: String? = nil) {
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()
|> 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)))
}
}))
}
}