mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
327 lines
18 KiB
Swift
327 lines
18 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import Display
|
|
import AsyncDisplayKit
|
|
import Postbox
|
|
import TelegramCore
|
|
import SyncCore
|
|
import SwiftSignalKit
|
|
import TelegramPresentationData
|
|
import AccountContext
|
|
import ContactListUI
|
|
import CallListUI
|
|
import ChatListUI
|
|
import SettingsUI
|
|
import AppBundle
|
|
|
|
public final class TelegramRootController: NavigationController {
|
|
private let context: AccountContext
|
|
|
|
public var rootTabController: TabBarController?
|
|
|
|
public var contactsController: ContactsController?
|
|
public var callListController: CallListController?
|
|
public var chatListController: ChatListController?
|
|
public var accountSettingsController: PeerInfoScreen?
|
|
|
|
private var permissionsDisposable: Disposable?
|
|
private var presentationDataDisposable: Disposable?
|
|
private var presentationData: PresentationData
|
|
|
|
public init(context: AccountContext) {
|
|
self.context = context
|
|
|
|
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
|
|
let navigationDetailsBackgroundMode: NavigationEmptyDetailsBackgoundMode?
|
|
switch presentationData.chatWallpaper {
|
|
case .color:
|
|
let image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/EmptyMasterDetailIcon"), color: presentationData.theme.chatList.messageTextColor.withAlphaComponent(0.2))
|
|
navigationDetailsBackgroundMode = image != nil ? .image(image!) : nil
|
|
default:
|
|
let image = chatControllerBackgroundImage(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper, mediaBox: context.account.postbox.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper)
|
|
navigationDetailsBackgroundMode = image != nil ? .wallpaper(image!) : nil
|
|
}
|
|
|
|
super.init(mode: .automaticMasterDetail, theme: NavigationControllerTheme(presentationTheme: self.presentationData.theme), backgroundDetailsMode: navigationDetailsBackgroundMode)
|
|
|
|
self.presentationDataDisposable = (context.sharedContext.presentationData
|
|
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
|
if let strongSelf = self {
|
|
if presentationData.chatWallpaper != strongSelf.presentationData.chatWallpaper {
|
|
let navigationDetailsBackgroundMode: NavigationEmptyDetailsBackgoundMode?
|
|
switch presentationData.chatWallpaper {
|
|
case .color:
|
|
let image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/EmptyMasterDetailIcon"), color: presentationData.theme.chatList.messageTextColor.withAlphaComponent(0.2))
|
|
navigationDetailsBackgroundMode = image != nil ? .image(image!) : nil
|
|
default:
|
|
navigationDetailsBackgroundMode = chatControllerBackgroundImage(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper, mediaBox: strongSelf.context.sharedContext.accountManager.mediaBox, knockoutMode: strongSelf.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper).flatMap(NavigationEmptyDetailsBackgoundMode.wallpaper)
|
|
}
|
|
strongSelf.updateBackgroundDetailsMode(navigationDetailsBackgroundMode, transition: .immediate)
|
|
}
|
|
|
|
let previousTheme = strongSelf.presentationData.theme
|
|
strongSelf.presentationData = presentationData
|
|
if previousTheme !== presentationData.theme {
|
|
strongSelf.rootTabController?.updateTheme(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData), theme: TabBarControllerTheme(rootControllerTheme: presentationData.theme))
|
|
strongSelf.rootTabController?.statusBar.statusBarStyle = presentationData.theme.rootController.statusBarStyle.style
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
required public init(coder aDecoder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
deinit {
|
|
self.permissionsDisposable?.dispose()
|
|
self.presentationDataDisposable?.dispose()
|
|
}
|
|
|
|
public func addRootControllers(showCallsTab: Bool) {
|
|
let tabBarController = TabBarController(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), theme: TabBarControllerTheme(rootControllerTheme: self.presentationData.theme))
|
|
tabBarController.navigationPresentation = .master
|
|
let chatListController = self.context.sharedContext.makeChatListController(context: self.context, groupId: .root, controlsHistoryPreload: true, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: !GlobalExperimentalSettings.isAppStoreBuild)
|
|
if let sharedContext = self.context.sharedContext as? SharedAccountContextImpl {
|
|
chatListController.tabBarItem.badgeValue = sharedContext.switchingData.chatListBadge
|
|
}
|
|
let callListController = CallListController(context: self.context, mode: .tab)
|
|
|
|
var controllers: [ViewController] = []
|
|
|
|
let contactsController = ContactsController(context: self.context)
|
|
contactsController.switchToChatsController = { [weak self] in
|
|
self?.openChatsController(activateSearch: false)
|
|
}
|
|
controllers.append(contactsController)
|
|
|
|
if showCallsTab {
|
|
controllers.append(callListController)
|
|
}
|
|
controllers.append(chatListController)
|
|
|
|
var restoreSettignsController: (ViewController & SettingsController)?
|
|
if let sharedContext = self.context.sharedContext as? SharedAccountContextImpl {
|
|
restoreSettignsController = sharedContext.switchingData.settingsController
|
|
}
|
|
restoreSettignsController?.updateContext(context: self.context)
|
|
if let sharedContext = self.context.sharedContext as? SharedAccountContextImpl {
|
|
sharedContext.switchingData = (nil, nil, nil)
|
|
}
|
|
|
|
let accountSettingsController = PeerInfoScreen(context: self.context, peerId: self.context.account.peerId, avatarInitiallyExpanded: false, isOpenedFromChat: false, nearbyPeerDistance: nil, callMessages: [], isSettings: true)
|
|
accountSettingsController.tabBarItemDebugTapAction = { [weak self, weak accountSettingsController] in
|
|
guard let strongSelf = self, let accountSettingsController = accountSettingsController else {
|
|
return
|
|
}
|
|
accountSettingsController.push(debugController(sharedContext: strongSelf.context.sharedContext, context: strongSelf.context))
|
|
}
|
|
controllers.append(accountSettingsController)
|
|
|
|
tabBarController.setControllers(controllers, selectedIndex: restoreSettignsController != nil ? (controllers.count - 1) : (controllers.count - 2))
|
|
|
|
self.contactsController = contactsController
|
|
self.callListController = callListController
|
|
self.chatListController = chatListController
|
|
self.accountSettingsController = accountSettingsController
|
|
self.rootTabController = tabBarController
|
|
self.pushViewController(tabBarController, animated: false)
|
|
|
|
// Queue.mainQueue().after(2.0) {
|
|
// let messageId = MessageId(peerId: PeerId(namespace: 2, id: 1488156064), namespace: 0, id: 528)
|
|
// let _ = ((self.context.account.postbox.transaction { transaction in
|
|
// return transaction.getMessage(messageId)
|
|
// }) |> deliverOnMainQueue).start(next: { [weak self] message in
|
|
// guard let strongSelf = self, let message = message else {
|
|
// return
|
|
// }
|
|
//
|
|
// let layout = ContainerViewLayout(size: CGSize(width: 414.0, height: 896.0), metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact), deviceMetrics: .iPhoneX, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: 0.0, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false)
|
|
// let renderer = MessageStoryRenderer(context: strongSelf.context, message: message)
|
|
// let image = renderer.update(layout: layout)
|
|
//
|
|
// let node = renderer.containerNode
|
|
// node.frame = CGRect(origin: CGPoint(), size: layout.size)
|
|
// strongSelf.displayNode.addSubnode(node)
|
|
// })
|
|
// }
|
|
}
|
|
|
|
public func updateRootControllers(showCallsTab: Bool) {
|
|
guard let rootTabController = self.rootTabController else {
|
|
return
|
|
}
|
|
var controllers: [ViewController] = []
|
|
controllers.append(self.contactsController!)
|
|
if showCallsTab {
|
|
controllers.append(self.callListController!)
|
|
}
|
|
controllers.append(self.chatListController!)
|
|
controllers.append(self.accountSettingsController!)
|
|
|
|
rootTabController.setControllers(controllers, selectedIndex: nil)
|
|
}
|
|
|
|
public func openChatsController(activateSearch: Bool) {
|
|
guard let rootTabController = self.rootTabController else {
|
|
return
|
|
}
|
|
|
|
if activateSearch {
|
|
self.popToRoot(animated: false)
|
|
}
|
|
|
|
if let index = rootTabController.controllers.firstIndex(where: { $0 is ChatListController}) {
|
|
rootTabController.selectedIndex = index
|
|
}
|
|
|
|
if activateSearch {
|
|
self.chatListController?.activateSearch()
|
|
}
|
|
}
|
|
|
|
public func openRootCompose() {
|
|
self.chatListController?.activateCompose()
|
|
}
|
|
|
|
public func openRootCamera() {
|
|
guard let controller = self.viewControllers.last as? ViewController else {
|
|
return
|
|
}
|
|
controller.view.endEditing(true)
|
|
presentedLegacyShortcutCamera(context: self.context, saveCapturedMedia: false, saveEditedPhotos: false, mediaGrouping: true, parentController: controller)
|
|
}
|
|
}
|
|
|
|
class MessageStoryRenderer {
|
|
private let context: AccountContext
|
|
private let presentationData: PresentationData
|
|
private let message: Message
|
|
|
|
let containerNode: ASDisplayNode
|
|
private let instantChatBackgroundNode: WallpaperBackgroundNode
|
|
private let messagesContainerNode: ASDisplayNode
|
|
private var dateHeaderNode: ListViewItemHeaderNode?
|
|
private var messageNodes: [ListViewItemNode]?
|
|
private let addressNode: ImmediateTextNode
|
|
|
|
init(context: AccountContext, message: Message) {
|
|
self.context = context
|
|
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
self.message = message
|
|
|
|
self.containerNode = ASDisplayNode()
|
|
|
|
self.instantChatBackgroundNode = WallpaperBackgroundNode()
|
|
self.instantChatBackgroundNode.displaysAsynchronously = false
|
|
self.instantChatBackgroundNode.image = chatControllerBackgroundImage(theme: presentationData.theme, wallpaper: .builtin(WallpaperSettings()), mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper)
|
|
|
|
self.messagesContainerNode = ASDisplayNode()
|
|
self.messagesContainerNode.clipsToBounds = true
|
|
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
|
|
|
|
let peer = message.peers[message.id.peerId]!
|
|
self.addressNode = ImmediateTextNode()
|
|
self.addressNode.displaysAsynchronously = false
|
|
self.addressNode.attributedText = NSAttributedString(string: "t.me/\(peer.addressName ?? "")", font: Font.medium(14.0), textColor: UIColor(rgb: 0xa8b7c4))
|
|
// self.addressNode.textShadowColor = .black
|
|
|
|
self.containerNode.addSubnode(self.instantChatBackgroundNode)
|
|
self.containerNode.addSubnode(self.messagesContainerNode)
|
|
self.containerNode.addSubnode(self.addressNode)
|
|
}
|
|
|
|
// func update(layout: ContainerViewLayout, completion: (UIImage?) -> Void) {
|
|
// self.updateMessagesLayout(layout: layout)
|
|
//
|
|
// Queue.mainQueue().after(0.01) {
|
|
// UIGraphicsBeginImageContextWithOptions(layout.size, false, 3.0)
|
|
// self.containerNode.view.drawHierarchy(in: CGRect(origin: CGPoint(), size: layout.size), afterScreenUpdates: true)
|
|
// let img = UIGraphicsGetImageFromCurrentImageContext()
|
|
// UIGraphicsEndImageContext()
|
|
// completion(img)
|
|
// }
|
|
// }
|
|
|
|
private func updateMessagesLayout(layout: ContainerViewLayout) {
|
|
let size = layout.size
|
|
self.containerNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
|
self.instantChatBackgroundNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
|
self.instantChatBackgroundNode.updateLayout(size: size, transition: .immediate)
|
|
self.messagesContainerNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
|
|
|
let addressLayout = self.addressNode.updateLayout(size)
|
|
|
|
let theme = self.presentationData.theme.withUpdated(preview: true)
|
|
let headerItem = self.context.sharedContext.makeChatMessageDateHeaderItem(context: self.context, timestamp: self.message.timestamp, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder)
|
|
|
|
var items: [ListViewItem] = []
|
|
let sampleMessages: [Message] = [self.message]
|
|
|
|
items = sampleMessages.reversed().map { message in
|
|
self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.theme.chat.defaultWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)
|
|
}
|
|
|
|
let inset: CGFloat = 16.0
|
|
let width = layout.size.width - inset * 2.0
|
|
let params = ListViewItemLayoutParams(width: width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
|
|
if let messageNodes = self.messageNodes {
|
|
for i in 0 ..< items.count {
|
|
let itemNode = messageNodes[i]
|
|
items[i].updateNode(async: { $0() }, node: {
|
|
return itemNode
|
|
}, params: params, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], animation: .None, completion: { (layout, apply) in
|
|
let nodeFrame = CGRect(origin: CGPoint(x: 0.0, y: floor((size.height - layout.size.height) / 2.0)), size: CGSize(width: width, height: layout.size.height))
|
|
|
|
itemNode.contentSize = layout.contentSize
|
|
itemNode.insets = layout.insets
|
|
itemNode.frame = nodeFrame
|
|
itemNode.isUserInteractionEnabled = false
|
|
|
|
apply(ListViewItemApply(isOnScreen: true))
|
|
})
|
|
}
|
|
} else {
|
|
var messageNodes: [ListViewItemNode] = []
|
|
for i in 0 ..< items.count {
|
|
var itemNode: ListViewItemNode?
|
|
items[i].nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: true, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], completion: { node, apply in
|
|
itemNode = node
|
|
apply().1(ListViewItemApply(isOnScreen: true))
|
|
})
|
|
itemNode!.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
|
|
itemNode!.isUserInteractionEnabled = false
|
|
messageNodes.append(itemNode!)
|
|
self.messagesContainerNode.addSubnode(itemNode!)
|
|
}
|
|
self.messageNodes = messageNodes
|
|
}
|
|
|
|
var bottomOffset: CGFloat = 0.0
|
|
if let messageNodes = self.messageNodes {
|
|
for itemNode in messageNodes {
|
|
itemNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((size.height - itemNode.frame.height) / 2.0)), size: itemNode.frame.size)
|
|
bottomOffset += itemNode.frame.maxY
|
|
itemNode.updateFrame(itemNode.frame, within: layout.size)
|
|
}
|
|
}
|
|
|
|
self.addressNode.frame = CGRect(origin: CGPoint(x: inset + 16.0, y: bottomOffset + 3.0), size: CGSize(width: addressLayout.width, height: addressLayout.height + 3.0))
|
|
|
|
let dateHeaderNode: ListViewItemHeaderNode
|
|
if let currentDateHeaderNode = self.dateHeaderNode {
|
|
dateHeaderNode = currentDateHeaderNode
|
|
headerItem.updateNode(dateHeaderNode, previous: nil, next: headerItem)
|
|
} else {
|
|
dateHeaderNode = headerItem.node()
|
|
dateHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
|
|
self.messagesContainerNode.addSubnode(dateHeaderNode)
|
|
self.dateHeaderNode = dateHeaderNode
|
|
}
|
|
|
|
dateHeaderNode.frame = CGRect(origin: CGPoint(x: 0.0, y: bottomOffset), size: CGSize(width: layout.size.width, height: headerItem.height))
|
|
dateHeaderNode.updateLayout(size: self.containerNode.frame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
|
|
}
|
|
}
|