mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2026-04-08 06:07:52 +00:00
Fixes
fix localeWithStrings globally (#30)
Fix badge on zoomed devices. closes #9
Hide channel bottom panel closes #27
Another attempt to fix badge on some Zoomed devices
Force System Share sheet tg://sg/debug
fixes for device badge
New Crowdin updates (#34)
* New translations sglocalizable.strings (Chinese Traditional)
* New translations sglocalizable.strings (Chinese Simplified)
* New translations sglocalizable.strings (Chinese Traditional)
Fix input panel hidden on selection (#31)
* added if check for selectionState != nil
* same order of subnodes
Revert "Fix input panel hidden on selection (#31)"
This reverts commit e8a8bb1496.
Fix input panel for channels Closes #37
Quickly share links with system's share menu
force tabbar when editing
increase height for correct animation
New translations sglocalizable.strings (Ukrainian) (#38)
Hide Post Story button
Fix 10.15.1
Fix archive option for long-tap
Enable in-app Safari
Disable some unsupported purchases
disableDeleteChatSwipeOption + refactor restart alert
Hide bot in suggestions list
Fix merge v11.0
Fix exceptions for safari webview controller
New Crowdin updates (#47)
* New translations sglocalizable.strings (Romanian)
* New translations sglocalizable.strings (French)
* New translations sglocalizable.strings (Spanish)
* New translations sglocalizable.strings (Afrikaans)
* New translations sglocalizable.strings (Arabic)
* New translations sglocalizable.strings (Catalan)
* New translations sglocalizable.strings (Czech)
* New translations sglocalizable.strings (Danish)
* New translations sglocalizable.strings (German)
* New translations sglocalizable.strings (Greek)
* New translations sglocalizable.strings (Finnish)
* New translations sglocalizable.strings (Hebrew)
* New translations sglocalizable.strings (Hungarian)
* New translations sglocalizable.strings (Italian)
* New translations sglocalizable.strings (Japanese)
* New translations sglocalizable.strings (Korean)
* New translations sglocalizable.strings (Dutch)
* New translations sglocalizable.strings (Norwegian)
* New translations sglocalizable.strings (Polish)
* New translations sglocalizable.strings (Portuguese)
* New translations sglocalizable.strings (Serbian (Cyrillic))
* New translations sglocalizable.strings (Swedish)
* New translations sglocalizable.strings (Turkish)
* New translations sglocalizable.strings (Vietnamese)
* New translations sglocalizable.strings (Indonesian)
* New translations sglocalizable.strings (Hindi)
* New translations sglocalizable.strings (Uzbek)
New Crowdin updates (#49)
* New translations sglocalizable.strings (Arabic)
* New translations sglocalizable.strings (Arabic)
New translations sglocalizable.strings (Russian) (#51)
Call confirmation
WIP Settings search
Settings Search
Localize placeholder
Update AccountUtils.swift
mark mutual contact
Align back context action to left
New Crowdin updates (#54)
* New translations sglocalizable.strings (Chinese Simplified)
* New translations sglocalizable.strings (Chinese Traditional)
* New translations sglocalizable.strings (Ukrainian)
Independent Playground app for simulator
New translations sglocalizable.strings (Ukrainian) (#55)
Playground UIKit base and controllers
Inject SwiftUI view with overflow to AsyncDisplayKit
Launch Playgound project on simulator
Create .swiftformat
Move Playground to example
Update .swiftformat
Init SwiftUIViewController
wip
New translations sglocalizable.strings (Chinese Traditional) (#57)
Xcode 16 fixes
Fix
New translations sglocalizable.strings (Italian) (#59)
New translations sglocalizable.strings (Chinese Simplified) (#63)
Force disable CallKit integration due to missing NSE Entitlement
Fix merge
Fix whole chat translator
Sweetpad config
Bump version
11.3.1 fixes
Mutual contact placement fix
Disable Video PIP swipe
Update versions.json
Fix PIP crash
1038 lines
58 KiB
Swift
1038 lines
58 KiB
Swift
// MARK: Swiftgram
|
|
import SGSimpleSettings
|
|
import Foundation
|
|
import Intents
|
|
import TelegramPresentationData
|
|
import TelegramUIPreferences
|
|
import SwiftSignalKit
|
|
import Postbox
|
|
import TelegramCore
|
|
import Display
|
|
import LegacyComponents
|
|
import DeviceAccess
|
|
import TelegramUpdateUI
|
|
import AccountContext
|
|
import AlertUI
|
|
import PresentationDataUtils
|
|
import TelegramPermissions
|
|
import TelegramNotices
|
|
import LegacyUI
|
|
import TelegramPermissionsUI
|
|
import PasscodeUI
|
|
import ImageBlur
|
|
import FastBlur
|
|
import WatchBridge
|
|
import SettingsUI
|
|
import AppLock
|
|
import AccountUtils
|
|
import ContextUI
|
|
import TelegramCallsUI
|
|
import AuthorizationUI
|
|
import ChatListUI
|
|
import StoryContainerScreen
|
|
import ChatMessageNotificationItem
|
|
import PhoneNumberFormat
|
|
import AttachmentUI
|
|
import MinimizedContainer
|
|
import BrowserUI
|
|
|
|
final class UnauthorizedApplicationContext {
|
|
let sharedContext: SharedAccountContextImpl
|
|
let account: UnauthorizedAccount
|
|
|
|
let rootController: AuthorizationSequenceController
|
|
|
|
let isReady = Promise<Bool>()
|
|
|
|
var authorizationCompleted: Bool = false
|
|
|
|
private var serviceNotificationEventsDisposable: Disposable?
|
|
|
|
init(apiId: Int32, apiHash: String, sharedContext: SharedAccountContextImpl, account: UnauthorizedAccount, otherAccountPhoneNumbers: ((String, AccountRecordId, Bool)?, [(String, AccountRecordId, Bool)])) {
|
|
self.sharedContext = sharedContext
|
|
self.account = account
|
|
let presentationData = sharedContext.currentPresentationData.with { $0 }
|
|
|
|
var authorizationCompleted: (() -> Void)?
|
|
|
|
self.rootController = AuthorizationSequenceController(sharedContext: sharedContext, account: account, otherAccountPhoneNumbers: otherAccountPhoneNumbers, presentationData: presentationData, openUrl: sharedContext.applicationBindings.openUrl, apiId: apiId, apiHash: apiHash, authorizationCompleted: {
|
|
authorizationCompleted?()
|
|
})
|
|
(self.rootController as NavigationController).statusBarHost = sharedContext.mainWindow?.statusBarHost
|
|
|
|
authorizationCompleted = { [weak self] in
|
|
self?.authorizationCompleted = true
|
|
}
|
|
|
|
self.isReady.set(self.rootController.ready.get())
|
|
|
|
account.shouldBeServiceTaskMaster.set(sharedContext.applicationBindings.applicationInForeground |> map { value -> AccountServiceTaskMasterMode in
|
|
if value {
|
|
return .always
|
|
} else {
|
|
return .never
|
|
}
|
|
})
|
|
|
|
DeviceAccess.authorizeAccess(to: .cellularData, presentationData: sharedContext.currentPresentationData.with { $0 }, present: { [weak self] c, a in
|
|
if let strongSelf = self {
|
|
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(c, in: .window(.root))
|
|
}
|
|
}, openSettings: {
|
|
sharedContext.applicationBindings.openSettings()
|
|
}, { result in
|
|
ApplicationSpecificNotice.setPermissionWarning(accountManager: sharedContext.accountManager, permission: .cellularData, value: 0)
|
|
})
|
|
|
|
self.serviceNotificationEventsDisposable = (account.serviceNotificationEvents
|
|
|> deliverOnMainQueue).start(next: { [weak self] text in
|
|
if let strongSelf = self {
|
|
let presentationData = strongSelf.sharedContext.currentPresentationData.with { $0 }
|
|
let alertController = textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
|
|
|
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(alertController, in: .window(.root))
|
|
}
|
|
})
|
|
}
|
|
|
|
deinit {
|
|
self.serviceNotificationEventsDisposable?.dispose()
|
|
}
|
|
}
|
|
|
|
final class AuthorizedApplicationContext {
|
|
let sharedApplicationContext: SharedApplicationContext
|
|
let mainWindow: Window1
|
|
let lockedCoveringView: LockedWindowCoveringView
|
|
|
|
let context: AccountContextImpl
|
|
|
|
let rootController: TelegramRootController
|
|
let notificationController: NotificationContainerController
|
|
|
|
private let scheduledCallPeerDisposable = MetaDisposable()
|
|
private var scheduledOpenExternalUrl: URL?
|
|
|
|
private let passcodeStatusDisposable = MetaDisposable()
|
|
private let passcodeLockDisposable = MetaDisposable()
|
|
private let loggedOutDisposable = MetaDisposable()
|
|
private let inAppNotificationSettingsDisposable = MetaDisposable()
|
|
private let notificationMessagesDisposable = MetaDisposable()
|
|
private let termsOfServiceUpdatesDisposable = MetaDisposable()
|
|
private let termsOfServiceProceedToBotDisposable = MetaDisposable()
|
|
private let watchNavigateToMessageDisposable = MetaDisposable()
|
|
private let permissionsDisposable = MetaDisposable()
|
|
private let appUpdateInfoDisposable = MetaDisposable()
|
|
|
|
private var inAppNotificationSettings: InAppNotificationSettings?
|
|
|
|
var passcodeController: PasscodeEntryController?
|
|
|
|
private var currentAppUpdateInfo: AppUpdateInfo?
|
|
private var currentTermsOfServiceUpdate: TermsOfServiceUpdate?
|
|
private var currentPermissionsController: PermissionController?
|
|
private var currentPermissionsState: PermissionState?
|
|
|
|
private let unlockedStatePromise = Promise<Bool>()
|
|
var unlockedState: Signal<Bool, NoError> {
|
|
return self.unlockedStatePromise.get()
|
|
}
|
|
|
|
var applicationBadge: Signal<Int32, NoError> {
|
|
return renderedTotalUnreadCount(accountManager: self.context.sharedContext.accountManager, engine: self.context.engine)
|
|
|> map {
|
|
$0.0
|
|
}
|
|
}
|
|
|
|
let isReady = Promise<Bool>()
|
|
|
|
private var presentationDataDisposable: Disposable?
|
|
private var displayAlertsDisposable: Disposable?
|
|
private var removeNotificationsDisposable: Disposable?
|
|
|
|
private var applicationInForegroundDisposable: Disposable?
|
|
|
|
private var showContactsTab: Bool
|
|
private var showCallsTab: Bool
|
|
private var showCallsTabDisposable: Disposable?
|
|
private var enablePostboxTransactionsDiposable: Disposable?
|
|
|
|
init(sharedApplicationContext: SharedApplicationContext, mainWindow: Window1, watchManagerArguments: Signal<WatchManagerArguments?, NoError>, context: AccountContextImpl, accountManager: AccountManager<TelegramAccountManagerTypes>, showContactsTab: Bool, showCallsTab: Bool, reinitializedNotificationSettings: @escaping () -> Void) {
|
|
self.sharedApplicationContext = sharedApplicationContext
|
|
|
|
setupLegacyComponents(context: context)
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
|
|
self.mainWindow = mainWindow
|
|
self.lockedCoveringView = LockedWindowCoveringView(theme: presentationData.theme)
|
|
|
|
self.context = context
|
|
|
|
self.showContactsTab = showContactsTab
|
|
|
|
self.showCallsTab = showCallsTab
|
|
|
|
self.notificationController = NotificationContainerController(context: context)
|
|
|
|
self.rootController = TelegramRootController(showTabNames: SGSimpleSettings.shared.showTabNames, context: context)
|
|
self.rootController.minimizedContainer = self.sharedApplicationContext.minimizedContainer[context.account.id]
|
|
self.rootController.minimizedContainerUpdated = { [weak self] minimizedContainer in
|
|
guard let self else {
|
|
return
|
|
}
|
|
self.sharedApplicationContext.minimizedContainer[self.context.account.id] = minimizedContainer
|
|
}
|
|
|
|
self.rootController.globalOverlayControllersUpdated = { [weak self] in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
var hasContext = false
|
|
for controller in strongSelf.rootController.globalOverlayControllers {
|
|
if controller is ContextController {
|
|
hasContext = true
|
|
break
|
|
}
|
|
}
|
|
|
|
strongSelf.notificationController.updateIsTemporaryHidden(hasContext)
|
|
}
|
|
|
|
if KeyShortcutsController.isAvailable {
|
|
let keyShortcutsController = KeyShortcutsController { [weak self] f in
|
|
if let strongSelf = self, let appLockContext = strongSelf.context.sharedContext.appLockContext as? AppLockContextImpl {
|
|
let _ = (appLockContext.isCurrentlyLocked
|
|
|> take(1)
|
|
|> deliverOnMainQueue).start(next: { locked in
|
|
guard !locked else {
|
|
return
|
|
}
|
|
if let tabController = strongSelf.rootController.rootTabController {
|
|
let selectedController = tabController.controllers[tabController.selectedIndex]
|
|
|
|
if let index = strongSelf.rootController.viewControllers.lastIndex(where: { controller in
|
|
guard let controller = controller as? ViewController else {
|
|
return false
|
|
}
|
|
if controller === tabController {
|
|
return false
|
|
}
|
|
switch controller.navigationPresentation {
|
|
case .master:
|
|
return true
|
|
default:
|
|
break
|
|
}
|
|
return false
|
|
}), let controller = strongSelf.rootController.viewControllers[index] as? ViewController {
|
|
if !f(controller) {
|
|
return
|
|
}
|
|
} else {
|
|
if !f(selectedController) {
|
|
return
|
|
}
|
|
}
|
|
|
|
if let controller = strongSelf.rootController.topViewController as? ViewController, controller !== selectedController {
|
|
if !f(controller) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
strongSelf.mainWindow.forEachViewController(f)
|
|
if let globalOverlayController = strongSelf.rootController.globalOverlayControllers.last {
|
|
if !f(globalOverlayController) {
|
|
return
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
context.keyShortcutsController = keyShortcutsController
|
|
}
|
|
|
|
if self.rootController.rootTabController == nil {
|
|
self.rootController.addRootControllers(hidePhoneInSettings: SGSimpleSettings.shared.hidePhoneInSettings, showContactsTab: self.showContactsTab, showCallsTab: self.showCallsTab)
|
|
}
|
|
if let tabsController = self.rootController.viewControllers.first as? TabBarController, !tabsController.controllers.isEmpty, tabsController.selectedIndex >= 0 {
|
|
let controller = tabsController.controllers[tabsController.selectedIndex]
|
|
let combinedReady = combineLatest(tabsController.ready.get(), controller.ready.get())
|
|
|> map { $0 && $1 }
|
|
|> filter { $0 }
|
|
|> take(1)
|
|
self.isReady.set(combinedReady)
|
|
} else {
|
|
self.isReady.set(.single(true))
|
|
}
|
|
|
|
let accountId = context.account.id
|
|
self.loggedOutDisposable.set((context.account.loggedOut
|
|
|> deliverOnMainQueue).start(next: { [weak self] value in
|
|
if value {
|
|
Logger.shared.log("ApplicationContext", "account logged out")
|
|
let _ = logoutFromAccount(id: accountId, accountManager: accountManager, alreadyLoggedOutRemotely: false).start()
|
|
if let strongSelf = self {
|
|
strongSelf.rootController.currentWindow?.forEachController { controller in
|
|
if let controller = controller as? TermsOfServiceController {
|
|
controller.dismiss()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}))
|
|
|
|
self.inAppNotificationSettingsDisposable.set(((context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.inAppNotificationSettings])) |> deliverOnMainQueue).start(next: { [weak self] sharedData in
|
|
if let strongSelf = self {
|
|
if let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.inAppNotificationSettings]?.get(InAppNotificationSettings.self) {
|
|
let previousSettings = strongSelf.inAppNotificationSettings
|
|
strongSelf.inAppNotificationSettings = settings
|
|
if let previousSettings = previousSettings, previousSettings.displayNameOnLockscreen != settings.displayNameOnLockscreen {
|
|
reinitializedNotificationSettings()
|
|
}
|
|
}
|
|
}
|
|
}))
|
|
|
|
let engine = context.engine
|
|
self.notificationMessagesDisposable.set((context.account.stateManager.notificationMessages
|
|
|> mapToSignal { messageList -> Signal<[([Message], PeerGroupId, Bool, MessageHistoryThreadData?)], NoError> in
|
|
return engine.data.get(EngineDataMap(
|
|
messageList.compactMap { item -> TelegramEngine.EngineData.Item.Messages.ChatListIndex? in
|
|
if let message = item.0.first {
|
|
return TelegramEngine.EngineData.Item.Messages.ChatListIndex(id: message.id.peerId)
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
))
|
|
|> map { chatListIndexMap -> [([Message], PeerGroupId, Bool, MessageHistoryThreadData?)] in
|
|
return messageList.filter { item in
|
|
guard let message = item.0.first else {
|
|
return false
|
|
}
|
|
if let maybeChatListIndex = chatListIndexMap[message.id.peerId], maybeChatListIndex != nil {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|> deliverOn(Queue.mainQueue())).start(next: { [weak self] messageList in
|
|
if messageList.isEmpty {
|
|
return
|
|
}
|
|
|
|
if let strongSelf = self, let (messages, _, notify, threadData) = messageList.last, let firstMessage = messages.first {
|
|
if UIApplication.shared.applicationState == .active {
|
|
let chatLocation: NavigateToChatControllerParams.Location
|
|
if let _ = threadData, let threadId = firstMessage.threadId {
|
|
chatLocation = .replyThread(ChatReplyThreadMessage(
|
|
peerId: firstMessage.id.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false
|
|
).normalized)
|
|
} else {
|
|
guard let peer = firstMessage.peers[firstMessage.id.peerId] else {
|
|
return
|
|
}
|
|
chatLocation = .peer(EnginePeer(peer))
|
|
}
|
|
|
|
var chatIsVisible = false
|
|
if let topController = strongSelf.rootController.topViewController as? ChatControllerImpl, topController.traceVisibility() {
|
|
if topController.chatLocation.peerId == firstMessage.id.peerId, (topController.chatLocation.threadId == nil || topController.chatLocation.threadId == firstMessage.threadId) {
|
|
chatIsVisible = true
|
|
}
|
|
}
|
|
|
|
if !notify {
|
|
chatIsVisible = true
|
|
}
|
|
|
|
if !chatIsVisible {
|
|
strongSelf.mainWindow.forEachViewController({ controller in
|
|
if let controller = controller as? ChatControllerImpl, controller.chatLocation.peerId == chatLocation.peerId, (chatLocation.threadId == nil || chatLocation.threadId == controller.chatLocation.threadId) {
|
|
chatIsVisible = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
|
|
let inAppNotificationSettings: InAppNotificationSettings
|
|
if let current = strongSelf.inAppNotificationSettings {
|
|
inAppNotificationSettings = current
|
|
} else {
|
|
inAppNotificationSettings = InAppNotificationSettings.defaultSettings
|
|
}
|
|
|
|
if let appLockContext = strongSelf.context.sharedContext.appLockContext as? AppLockContextImpl {
|
|
let _ = (appLockContext.isCurrentlyLocked
|
|
|> take(1)
|
|
|> deliverOnMainQueue).start(next: { locked in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
|
|
guard !locked else {
|
|
return
|
|
}
|
|
let isMuted = firstMessage.attributes.contains(where: { attribute in
|
|
if let attribute = attribute as? NotificationInfoMessageAttribute {
|
|
return attribute.flags.contains(.muted)
|
|
} else {
|
|
return false
|
|
}
|
|
})
|
|
if !isMuted {
|
|
if firstMessage.id.peerId == context.account.peerId, !firstMessage.flags.contains(.WasScheduled) {
|
|
} else {
|
|
if inAppNotificationSettings.playSounds {
|
|
serviceSoundManager.playIncomingMessageSound()
|
|
}
|
|
if inAppNotificationSettings.vibrate {
|
|
serviceSoundManager.playVibrationSound()
|
|
}
|
|
}
|
|
}
|
|
if let forwardInfo = firstMessage.forwardInfo, forwardInfo.flags.contains(.isImported) {
|
|
return
|
|
}
|
|
for media in firstMessage.media {
|
|
if let action = media as? TelegramMediaAction {
|
|
if case .messageAutoremoveTimeoutUpdated = action.action {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
if chatIsVisible {
|
|
return
|
|
}
|
|
|
|
if firstMessage.restrictionReason(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) != nil {
|
|
return
|
|
}
|
|
if let chatPeer = firstMessage.peers[firstMessage.id.peerId] {
|
|
if EnginePeer(chatPeer).restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
if inAppNotificationSettings.displayPreviews {
|
|
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
|
strongSelf.notificationController.enqueue(ChatMessageNotificationItem(context: strongSelf.context, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, messages: messages, threadData: threadData, tapAction: {
|
|
if let strongSelf = self {
|
|
var foundOverlay = false
|
|
strongSelf.mainWindow.forEachViewController({ controller in
|
|
if isOverlayControllerForChatNotificationOverlayPresentation(controller) {
|
|
foundOverlay = true
|
|
return false
|
|
}
|
|
return true
|
|
}, excludeNavigationSubControllers: true)
|
|
|
|
if foundOverlay {
|
|
return true
|
|
}
|
|
|
|
if let topController = strongSelf.rootController.topViewController as? ViewController, isInlineControllerForChatNotificationOverlayPresentation(topController) {
|
|
return true
|
|
}
|
|
|
|
if let topController = strongSelf.rootController.topViewController as? ChatControllerImpl, topController.chatLocation.peerId == chatLocation.peerId, (topController.chatLocation.threadId == nil || topController.chatLocation.threadId == chatLocation.threadId) {
|
|
strongSelf.notificationController.removeItemsWithGroupingKey(firstMessage.id.peerId)
|
|
|
|
return false
|
|
}
|
|
|
|
if let minimizedContainer = strongSelf.rootController.minimizedContainer, minimizedContainer.isExpanded {
|
|
minimizedContainer.collapse()
|
|
} else if let topContoller = strongSelf.rootController.topViewController as? AttachmentController {
|
|
topContoller.minimizeIfNeeded()
|
|
} else if let topContoller = strongSelf.rootController.topViewController as? BrowserScreen {
|
|
topContoller.requestMinimize(topEdgeOffset: nil, initialVelocity: nil)
|
|
}
|
|
|
|
for controller in strongSelf.rootController.viewControllers {
|
|
if let controller = controller as? ChatControllerImpl, controller.chatLocation.peerId == chatLocation.peerId, (controller.chatLocation.threadId == nil || controller.chatLocation.threadId == chatLocation.threadId) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
strongSelf.notificationController.removeItemsWithGroupingKey(firstMessage.id.peerId)
|
|
|
|
var processed = false
|
|
for media in firstMessage.media {
|
|
if let action = media as? TelegramMediaAction, case .geoProximityReached = action.action {
|
|
strongSelf.context.sharedContext.openLocationScreen(context: strongSelf.context, messageId: firstMessage.id, navigationController: strongSelf.rootController)
|
|
processed = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !processed {
|
|
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: strongSelf.rootController, context: strongSelf.context, chatLocation: chatLocation))
|
|
}
|
|
}
|
|
return false
|
|
}, expandAction: { expandData in
|
|
if let strongSelf = self {
|
|
let chatController = ChatControllerImpl(context: strongSelf.context, chatLocation: chatLocation.asChatLocation, mode: .overlay(strongSelf.rootController))
|
|
chatController.presentationArguments = ChatControllerOverlayPresentationData(expandData: expandData())
|
|
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(chatController, in: .window(.root), with: ChatControllerOverlayPresentationData(expandData: expandData()))
|
|
}
|
|
}))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}))
|
|
|
|
self.termsOfServiceUpdatesDisposable.set((context.account.stateManager.termsOfServiceUpdate
|
|
|> deliverOnMainQueue).start(next: { [weak self] termsOfServiceUpdate in
|
|
guard let strongSelf = self, strongSelf.currentTermsOfServiceUpdate != termsOfServiceUpdate else {
|
|
return
|
|
}
|
|
|
|
strongSelf.currentTermsOfServiceUpdate = termsOfServiceUpdate
|
|
if let termsOfServiceUpdate = termsOfServiceUpdate {
|
|
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
|
var acceptImpl: ((String?) -> Void)?
|
|
var declineImpl: (() -> Void)?
|
|
let controller = TermsOfServiceController(presentationData: presentationData, text: termsOfServiceUpdate.text, entities: termsOfServiceUpdate.entities, ageConfirmation: termsOfServiceUpdate.ageConfirmation, signingUp: false, accept: { proccedBot in
|
|
acceptImpl?(proccedBot)
|
|
}, decline: {
|
|
declineImpl?()
|
|
}, openUrl: { url in
|
|
if let parsedUrl = URL(string: url) {
|
|
UIApplication.shared.open(parsedUrl, options: [:], completionHandler: nil)
|
|
}
|
|
})
|
|
|
|
acceptImpl = { [weak controller] botName in
|
|
controller?.inProgress = true
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
let _ = (strongSelf.context.engine.accountData.acceptTermsOfService(id: termsOfServiceUpdate.id)
|
|
|> deliverOnMainQueue).start(completed: {
|
|
controller?.dismiss()
|
|
if let strongSelf = self, let botName = botName {
|
|
strongSelf.termsOfServiceProceedToBotDisposable.set((strongSelf.context.engine.peers.resolvePeerByName(name: botName, referrer: nil, ageLimit: 10)
|
|
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
|
guard case let .result(result) = result else {
|
|
return .complete()
|
|
}
|
|
return .single(result)
|
|
}
|
|
|> deliverOnMainQueue).start(next: { peer in
|
|
if let strongSelf = self, let peer = peer {
|
|
self?.rootController.pushViewController(ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(id: peer.id)))
|
|
}
|
|
}))
|
|
}
|
|
})
|
|
}
|
|
|
|
declineImpl = { [weak controller] in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
let accountId = strongSelf.context.account.id
|
|
let accountManager = strongSelf.context.sharedContext.accountManager
|
|
let _ = (strongSelf.context.engine.auth.deleteAccount(reason: "GDPR", password: nil)
|
|
|> deliverOnMainQueue).start(error: { _ in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
|
let controller = textAlertController(context: strongSelf.context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
|
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root))
|
|
}, completed: {
|
|
controller?.dismiss()
|
|
let _ = logoutFromAccount(id: accountId, accountManager: accountManager, alreadyLoggedOutRemotely: true).start()
|
|
})
|
|
}
|
|
|
|
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root))
|
|
}
|
|
}))
|
|
|
|
self.appUpdateInfoDisposable.set((context.account.stateManager.appUpdateInfo
|
|
|> deliverOnMainQueue).start(next: { [weak self] appUpdateInfo in
|
|
guard let strongSelf = self, strongSelf.currentAppUpdateInfo != appUpdateInfo else {
|
|
return
|
|
}
|
|
|
|
strongSelf.currentAppUpdateInfo = appUpdateInfo
|
|
if let appUpdateInfo = appUpdateInfo {
|
|
let controller = updateInfoController(context: strongSelf.context, appUpdateInfo: appUpdateInfo)
|
|
strongSelf.mainWindow.present(controller, on: .update)
|
|
}
|
|
}))
|
|
|
|
if #available(iOS 10.0, *) {
|
|
let permissionsPosition = ValuePromise(0, ignoreRepeated: true)
|
|
self.permissionsDisposable.set((combineLatest(queue: .mainQueue(), requiredPermissions(context: context), permissionUISplitTest(postbox: context.account.postbox), permissionsPosition.get(), context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.permissionWarningKey(permission: .contacts)!), context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.permissionWarningKey(permission: .notifications)!), context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.permissionWarningKey(permission: .cellularData)!))
|
|
|> deliverOnMainQueue).start(next: { [weak self] required, splitTest, position, contactsPermissionWarningNotice, notificationsPermissionWarningNotice, cellularDataPermissionWarningNotice in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
|
|
let contactsTimestamp = contactsPermissionWarningNotice.value.flatMap({ ApplicationSpecificNotice.getTimestampValue($0) })
|
|
let notificationsTimestamp = notificationsPermissionWarningNotice.value.flatMap({ ApplicationSpecificNotice.getTimestampValue($0) })
|
|
let cellularDataTimestamp = cellularDataPermissionWarningNotice.value.flatMap({ ApplicationSpecificNotice.getTimestampValue($0) })
|
|
if contactsTimestamp == nil, case .requestable = required.0.status {
|
|
ApplicationSpecificNotice.setPermissionWarning(accountManager: context.sharedContext.accountManager, permission: .contacts, value: 1)
|
|
}
|
|
if notificationsTimestamp == nil, case .requestable = required.1.status {
|
|
ApplicationSpecificNotice.setPermissionWarning(accountManager: context.sharedContext.accountManager, permission: .notifications, value: 1)
|
|
}
|
|
|
|
let config = splitTest.configuration
|
|
var order = config.order
|
|
if !order.contains(.cellularData) {
|
|
order.append(.cellularData)
|
|
}
|
|
if !order.contains(.siri) {
|
|
order.append(.siri)
|
|
}
|
|
var requestedPermissions: [(PermissionState, Bool)] = []
|
|
var i: Int = 0
|
|
for subject in order {
|
|
if i < position {
|
|
i += 1
|
|
continue
|
|
}
|
|
var modal = false
|
|
switch subject {
|
|
case .contacts:
|
|
if case .modal = config.contacts {
|
|
modal = true
|
|
}
|
|
if case .requestable = required.0.status, contactsTimestamp != 0 {
|
|
requestedPermissions.append((required.0, modal))
|
|
}
|
|
case .notifications:
|
|
if case .modal = config.notifications {
|
|
modal = true
|
|
}
|
|
if case .requestable = required.1.status, notificationsTimestamp != 0 {
|
|
requestedPermissions.append((required.1, modal))
|
|
}
|
|
case .cellularData:
|
|
if case .denied = required.2.status, cellularDataTimestamp != 0 {
|
|
requestedPermissions.append((required.2, true))
|
|
}
|
|
case .siri:
|
|
if case .requestable = required.3.status {
|
|
requestedPermissions.append((required.3, false))
|
|
}
|
|
default:
|
|
break
|
|
}
|
|
i += 1
|
|
}
|
|
|
|
if let (state, modal) = requestedPermissions.first {
|
|
if modal {
|
|
var didAppear = false
|
|
let controller: PermissionController
|
|
if let currentController = strongSelf.currentPermissionsController {
|
|
controller = currentController
|
|
didAppear = true
|
|
} else {
|
|
controller = PermissionController(context: context, splitTest: splitTest)
|
|
strongSelf.currentPermissionsController = controller
|
|
}
|
|
|
|
controller.setState(.permission(state), animated: didAppear)
|
|
controller.proceed = { resolved in
|
|
permissionsPosition.set(position + 1)
|
|
switch state {
|
|
case .contacts:
|
|
ApplicationSpecificNotice.setPermissionWarning(accountManager: context.sharedContext.accountManager, permission: .contacts, value: 0)
|
|
case .notifications:
|
|
ApplicationSpecificNotice.setPermissionWarning(accountManager: context.sharedContext.accountManager, permission: .notifications, value: 0)
|
|
case .cellularData:
|
|
ApplicationSpecificNotice.setPermissionWarning(accountManager: context.sharedContext.accountManager, permission: .cellularData, value: 0)
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
|
|
if !didAppear {
|
|
Queue.mainQueue().after(0.15, {
|
|
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
})
|
|
}
|
|
} else {
|
|
if strongSelf.currentPermissionsState != state {
|
|
strongSelf.currentPermissionsState = state
|
|
switch state {
|
|
case .contacts:
|
|
splitTest.addEvent(.ContactsRequest)
|
|
DeviceAccess.authorizeAccess(to: .contacts, presentationData: context.sharedContext.currentPresentationData.with { $0 }, { result in
|
|
if result {
|
|
splitTest.addEvent(.ContactsAllowed)
|
|
} else {
|
|
splitTest.addEvent(.ContactsDenied)
|
|
}
|
|
permissionsPosition.set(position + 1)
|
|
ApplicationSpecificNotice.setPermissionWarning(accountManager: context.sharedContext.accountManager, permission: .contacts, value: 0)
|
|
})
|
|
case .notifications:
|
|
splitTest.addEvent(.NotificationsRequest)
|
|
DeviceAccess.authorizeAccess(to: .notifications, registerForNotifications: { result in
|
|
context.sharedContext.applicationBindings.registerForNotifications(result)
|
|
}, { result in
|
|
if result {
|
|
splitTest.addEvent(.NotificationsAllowed)
|
|
} else {
|
|
splitTest.addEvent(.NotificationsDenied)
|
|
}
|
|
permissionsPosition.set(position + 1)
|
|
ApplicationSpecificNotice.setPermissionWarning(accountManager: context.sharedContext.accountManager, permission: .notifications, value: 0)
|
|
})
|
|
case .cellularData:
|
|
DeviceAccess.authorizeAccess(to: .cellularData, presentationData: context.sharedContext.currentPresentationData.with { $0 }, present: { [weak self] c, a in
|
|
if let strongSelf = self {
|
|
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(c, in: .window(.root))
|
|
}
|
|
}, openSettings: {
|
|
context.sharedContext.applicationBindings.openSettings()
|
|
}, { result in
|
|
permissionsPosition.set(position + 1)
|
|
ApplicationSpecificNotice.setPermissionWarning(accountManager: context.sharedContext.accountManager, permission: .cellularData, value: 0)
|
|
})
|
|
case .siri:
|
|
DeviceAccess.authorizeAccess(to: .siri, requestSiriAuthorization: { completion in
|
|
return context.sharedContext.applicationBindings.requestSiriAuthorization(completion)
|
|
}, { result in
|
|
permissionsPosition.set(position + 1)
|
|
})
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if let controller = strongSelf.currentPermissionsController {
|
|
strongSelf.currentPermissionsController = nil
|
|
controller.dismiss(completion: {})
|
|
}
|
|
strongSelf.currentPermissionsState = nil
|
|
}
|
|
}))
|
|
}
|
|
|
|
self.displayAlertsDisposable = (context.account.stateManager.displayAlerts
|
|
|> deliverOnMainQueue).start(next: { [weak self] alerts in
|
|
if let strongSelf = self {
|
|
for (text, isDropAuth) in alerts {
|
|
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
|
let actions: [TextAlertAction]
|
|
if isDropAuth {
|
|
actions = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.LogoutOptions_LogOut, action: {
|
|
if let strongSelf = self {
|
|
let _ = logoutFromAccount(id: strongSelf.context.account.id, accountManager: strongSelf.context.sharedContext.accountManager, alreadyLoggedOutRemotely: false).start()
|
|
}
|
|
})]
|
|
} else {
|
|
actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]
|
|
}
|
|
let controller = textAlertController(context: strongSelf.context, title: nil, text: text, actions: actions)
|
|
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root))
|
|
}
|
|
}
|
|
})
|
|
|
|
self.removeNotificationsDisposable = (context.account.stateManager.appliedIncomingReadMessages
|
|
|> deliverOnMainQueue).start(next: { [weak self] ids in
|
|
if let strongSelf = self {
|
|
strongSelf.context.sharedContext.applicationBindings.clearMessageNotifications(ids)
|
|
}
|
|
})
|
|
|
|
let importableContacts = self.context.sharedContext.contactDataManager?.importable() ?? .single([:])
|
|
let optionalImportableContacts = self.context.account.postbox.preferencesView(keys: [PreferencesKeys.contactsSettings])
|
|
|> mapToSignal { preferences -> Signal<[DeviceContactNormalizedPhoneNumber: ImportableDeviceContactData], NoError> in
|
|
let settings: ContactsSettings = preferences.values[PreferencesKeys.contactsSettings]?.get(ContactsSettings.self) ?? .defaultSettings
|
|
if settings.synchronizeContacts {
|
|
return importableContacts
|
|
} else {
|
|
return .single([:])
|
|
}
|
|
}
|
|
self.context.account.importableContacts.set(optionalImportableContacts)
|
|
self.context.sharedContext.deviceContactPhoneNumbers.set(optionalImportableContacts
|
|
|> map { contacts in
|
|
return Set(contacts.keys.map { cleanPhoneNumber($0.rawValue) })
|
|
})
|
|
|
|
let previousTheme = Atomic<PresentationTheme?>(value: nil)
|
|
self.presentationDataDisposable = (context.sharedContext.presentationData
|
|
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
|
if let strongSelf = self {
|
|
if previousTheme.swap(presentationData.theme) !== presentationData.theme {
|
|
strongSelf.lockedCoveringView.updateTheme(presentationData.theme)
|
|
strongSelf.rootController.updateTheme(NavigationControllerTheme(presentationTheme: presentationData.theme))
|
|
}
|
|
}
|
|
})
|
|
|
|
let showCallsTabSignal = context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.callListSettings])
|
|
|> map { sharedData -> (Bool, Bool) in
|
|
var showCallsTabValue = CallListSettings.defaultSettings.showTab
|
|
var showContactsTabValue = CallListSettings.defaultSettings.showContactsTab
|
|
if let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.callListSettings]?.get(CallListSettings.self) {
|
|
showCallsTabValue = settings.showTab
|
|
showContactsTabValue = settings.showContactsTab
|
|
}
|
|
return (showContactsTabValue, showCallsTabValue)
|
|
}
|
|
self.showCallsTabDisposable = (showCallsTabSignal |> deliverOnMainQueue).start(next: { [weak self] showContactsTabValue, showCallsTabValue in
|
|
if let strongSelf = self {
|
|
var needControllersUpdate = false
|
|
if strongSelf.showCallsTab != showCallsTabValue {
|
|
needControllersUpdate = true
|
|
strongSelf.showCallsTab = showCallsTabValue
|
|
}
|
|
if strongSelf.showContactsTab != showContactsTabValue {
|
|
needControllersUpdate = true
|
|
strongSelf.showContactsTab = showContactsTabValue
|
|
}
|
|
if needControllersUpdate {
|
|
strongSelf.rootController.updateRootControllers(showContactsTab: showContactsTabValue, showCallsTab: showCallsTabValue)
|
|
}
|
|
}
|
|
})
|
|
|
|
let _ = (watchManagerArguments
|
|
|> deliverOnMainQueue).start(next: { [weak self] arguments in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
|
|
let watchManager = WatchManagerImpl(arguments: arguments)
|
|
strongSelf.context.watchManager = watchManager
|
|
|
|
strongSelf.watchNavigateToMessageDisposable.set((strongSelf.context.sharedContext.applicationBindings.applicationInForeground |> mapToSignal({ applicationInForeground -> Signal<(Bool, MessageId), NoError> in
|
|
return watchManager.navigateToMessageRequested
|
|
|> map { messageId in
|
|
return (applicationInForeground, messageId)
|
|
}
|
|
|> deliverOnMainQueue
|
|
})).start(next: { [weak self] applicationInForeground, messageId in
|
|
if let strongSelf = self {
|
|
if applicationInForeground {
|
|
var chatIsVisible = false
|
|
if let controller = strongSelf.rootController.viewControllers.last as? ChatControllerImpl, case .peer(messageId.peerId) = controller.chatLocation {
|
|
chatIsVisible = true
|
|
}
|
|
|
|
let navigateToMessage = {
|
|
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: messageId.peerId))
|
|
|> deliverOnMainQueue).start(next: { peer in
|
|
guard let peer = peer else {
|
|
return
|
|
}
|
|
|
|
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: strongSelf.rootController, context: strongSelf.context, chatLocation: .peer(peer), subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false)))
|
|
})
|
|
}
|
|
|
|
if chatIsVisible {
|
|
navigateToMessage()
|
|
} else {
|
|
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
|
let controller = textAlertController(context: strongSelf.context, title: presentationData.strings.WatchRemote_AlertTitle, text: presentationData.strings.WatchRemote_AlertText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.WatchRemote_AlertOpen, action:navigateToMessage)])
|
|
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root))
|
|
}
|
|
} else {
|
|
//strongSelf.notificationManager.presentWatchContinuityNotification(context: strongSelf.context, messageId: messageId)
|
|
}
|
|
}
|
|
}))
|
|
})
|
|
|
|
self.rootController.setForceInCallStatusBar((self.context.sharedContext as! SharedAccountContextImpl).currentCallStatusBarNode)
|
|
if let groupCallController = self.context.sharedContext.currentGroupCallController as? VoiceChatController {
|
|
if let overlayController = groupCallController.currentOverlayController {
|
|
groupCallController.parentNavigationController = self.rootController
|
|
self.rootController.presentOverlay(controller: overlayController, inGlobal: true, blockInteraction: false)
|
|
}
|
|
}
|
|
}
|
|
|
|
deinit {
|
|
self.context.account.postbox.clearCaches()
|
|
self.context.account.shouldKeepOnlinePresence.set(.single(false))
|
|
self.context.account.shouldBeServiceTaskMaster.set(.single(.never))
|
|
self.loggedOutDisposable.dispose()
|
|
self.inAppNotificationSettingsDisposable.dispose()
|
|
self.notificationMessagesDisposable.dispose()
|
|
self.termsOfServiceUpdatesDisposable.dispose()
|
|
self.passcodeLockDisposable.dispose()
|
|
self.passcodeStatusDisposable.dispose()
|
|
self.displayAlertsDisposable?.dispose()
|
|
self.removeNotificationsDisposable?.dispose()
|
|
self.presentationDataDisposable?.dispose()
|
|
self.enablePostboxTransactionsDiposable?.dispose()
|
|
self.termsOfServiceProceedToBotDisposable.dispose()
|
|
self.watchNavigateToMessageDisposable.dispose()
|
|
self.permissionsDisposable.dispose()
|
|
self.scheduledCallPeerDisposable.dispose()
|
|
}
|
|
|
|
func openNotificationSettings() {
|
|
self.rootController.pushViewController(notificationsAndSoundsController(context: self.context, exceptionsList: nil))
|
|
}
|
|
|
|
func startCall(peerId: PeerId, isVideo: Bool) {
|
|
guard let appLockContext = self.context.sharedContext.appLockContext as? AppLockContextImpl else {
|
|
return
|
|
}
|
|
self.scheduledCallPeerDisposable.set((appLockContext.isCurrentlyLocked
|
|
|> filter {
|
|
!$0
|
|
}
|
|
|> take(1)
|
|
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
let _ = strongSelf.context.sharedContext.callManager?.requestCall(context: strongSelf.context, peerId: peerId, isVideo: isVideo, endCurrentIfAny: false)
|
|
}))
|
|
}
|
|
|
|
func openChatWithPeerId(peerId: PeerId, threadId: Int64?, messageId: MessageId? = nil, activateInput: Bool = false, storyId: StoryId?, openAppIfAny: Bool = false) {
|
|
if let storyId {
|
|
var controllers = self.rootController.viewControllers
|
|
controllers = controllers.filter { c in
|
|
if c is StoryContainerScreen {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
self.rootController.setViewControllers(controllers, animated: false)
|
|
|
|
self.rootController.chatListController?.openStoriesFromNotification(peerId: storyId.peerId, storyId: storyId.id)
|
|
} else {
|
|
var visiblePeerId: PeerId?
|
|
if let controller = self.rootController.topViewController as? ChatControllerImpl, controller.chatLocation.peerId == peerId, controller.chatLocation.threadId == threadId {
|
|
visiblePeerId = peerId
|
|
}
|
|
|
|
if visiblePeerId != peerId || messageId != nil {
|
|
let isOutgoingMessage: Signal<Bool, NoError>
|
|
if let messageId {
|
|
let accountPeerId = self.context.account.peerId
|
|
isOutgoingMessage = self.context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: messageId))
|
|
|> map { message -> Bool in
|
|
if let message {
|
|
return !message._asMessage().effectivelyIncoming(accountPeerId)
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
} else {
|
|
isOutgoingMessage = .single(false)
|
|
}
|
|
let _ = combineLatest(
|
|
queue: Queue.mainQueue(),
|
|
self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)),
|
|
isOutgoingMessage
|
|
).start(next: { peer, isOutgoingMessage in
|
|
guard let peer = peer else {
|
|
return
|
|
}
|
|
|
|
let chatLocation: NavigateToChatControllerParams.Location
|
|
if let threadId = threadId {
|
|
chatLocation = .replyThread(ChatReplyThreadMessage(
|
|
peerId: peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false
|
|
))
|
|
} else {
|
|
chatLocation = .peer(peer)
|
|
}
|
|
|
|
if openAppIfAny, case let .user(user) = peer, let botInfo = user.botInfo, botInfo.flags.contains(.hasWebApp), let parentController = self.rootController.viewControllers.last as? ViewController {
|
|
self.context.sharedContext.openWebApp(context: self.context, parentController: parentController, updatedPresentationData: nil, botPeer: peer, chatPeer: nil, threadId: nil, buttonText: "", url: "", simple: true, source: .generic, skipTermsOfService: true, payload: nil)
|
|
} else {
|
|
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: self.rootController, context: self.context, chatLocation: chatLocation, subject: isOutgoingMessage ? messageId.flatMap { .message(id: .id($0), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false) } : nil, activateInput: activateInput ? .text : nil))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func openUrl(_ url: URL) {
|
|
if self.rootController.rootTabController != nil {
|
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
|
self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url.absoluteString, forceExternal: false, presentationData: presentationData, navigationController: self.rootController, dismissInput: { [weak self] in
|
|
self?.rootController.view.endEditing(true)
|
|
})
|
|
} else {
|
|
self.scheduledOpenExternalUrl = url
|
|
}
|
|
}
|
|
|
|
func openRootSearch() {
|
|
self.rootController.openChatsController(activateSearch: true)
|
|
}
|
|
|
|
func openRootCompose() {
|
|
self.rootController.openRootCompose()
|
|
}
|
|
|
|
func openRootCamera() {
|
|
self.rootController.openRootCamera()
|
|
}
|
|
|
|
func openAppIcon() {
|
|
self.rootController.openAppIcon()
|
|
}
|
|
|
|
func switchAccount() {
|
|
let _ = (activeAccountsAndPeers(context: self.context)
|
|
|> take(1)
|
|
|> map { primaryAndAccounts -> (AccountContext, EnginePeer, Int32)? in
|
|
return primaryAndAccounts.1.first
|
|
}
|
|
|> map { accountAndPeer -> AccountContext? in
|
|
if let (context, _, _) = accountAndPeer {
|
|
return context
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|> deliverOnMainQueue).start(next: { [weak self] context in
|
|
guard let strongSelf = self, let context = context else {
|
|
return
|
|
}
|
|
strongSelf.context.sharedContext.switchToAccount(id: context.account.id, fromSettingsController: nil, withChatListController: nil)
|
|
})
|
|
}
|
|
|
|
private func updateCoveringViewSnaphot(_ visible: Bool) {
|
|
if visible {
|
|
let scale: CGFloat = 0.5
|
|
let unscaledSize = self.mainWindow.hostView.containerView.frame.size
|
|
let image = generateImage(CGSize(width: floor(unscaledSize.width * scale), height: floor(unscaledSize.height * scale)), rotatedContext: { size, context in
|
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
|
context.scaleBy(x: scale, y: scale)
|
|
UIGraphicsPushContext(context)
|
|
self.mainWindow.hostView.containerView.drawHierarchy(in: CGRect(origin: CGPoint(), size: unscaledSize), afterScreenUpdates: false)
|
|
UIGraphicsPopContext()
|
|
}).flatMap(applyScreenshotEffectToImage)
|
|
self.lockedCoveringView.updateSnapshot(image)
|
|
} else {
|
|
self.lockedCoveringView.updateSnapshot(nil)
|
|
}
|
|
}
|
|
}
|