mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-02 00:17:02 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
863ccdf297
@ -1,7 +1,24 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
import UIKit
|
||||||
import Display
|
import Display
|
||||||
|
|
||||||
|
public final class OverlayMediaControllerEmbeddingItem {
|
||||||
|
public let position: CGPoint
|
||||||
|
public let itemNode: OverlayMediaItemNode
|
||||||
|
|
||||||
|
public init(
|
||||||
|
position: CGPoint,
|
||||||
|
itemNode: OverlayMediaItemNode
|
||||||
|
) {
|
||||||
|
self.position = position
|
||||||
|
self.itemNode = itemNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public protocol OverlayMediaController: class {
|
public protocol OverlayMediaController: class {
|
||||||
|
var updatePossibleEmbeddingItem: ((OverlayMediaControllerEmbeddingItem?) -> Void)? { get set }
|
||||||
|
var embedPossibleEmbeddingItem: ((OverlayMediaControllerEmbeddingItem) -> Bool)? { get set }
|
||||||
|
|
||||||
var hasNodes: Bool { get }
|
var hasNodes: Bool { get }
|
||||||
func addNode(_ node: OverlayMediaItemNode, customTransition: Bool)
|
func addNode(_ node: OverlayMediaItemNode, customTransition: Bool)
|
||||||
func removeNode(_ node: OverlayMediaItemNode, customTransition: Bool)
|
func removeNode(_ node: OverlayMediaItemNode, customTransition: Bool)
|
||||||
@ -10,10 +27,21 @@ public protocol OverlayMediaController: class {
|
|||||||
public final class OverlayMediaManager {
|
public final class OverlayMediaManager {
|
||||||
public var controller: (OverlayMediaController & ViewController)?
|
public var controller: (OverlayMediaController & ViewController)?
|
||||||
|
|
||||||
|
public var updatePossibleEmbeddingItem: ((OverlayMediaControllerEmbeddingItem?) -> Void)?
|
||||||
|
public var embedPossibleEmbeddingItem: ((OverlayMediaControllerEmbeddingItem) -> Bool)?
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func attachOverlayMediaController(_ controller: OverlayMediaController & ViewController) {
|
public func attachOverlayMediaController(_ controller: OverlayMediaController & ViewController) {
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
|
|
||||||
|
controller.updatePossibleEmbeddingItem = { [weak self] item in
|
||||||
|
self?.updatePossibleEmbeddingItem?(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.embedPossibleEmbeddingItem = { [weak self] item in
|
||||||
|
return self?.embedPossibleEmbeddingItem?(item) ?? false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,19 @@ private final class NavigationControllerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public protocol NavigationControllerDropContentItem: class {
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class NavigationControllerDropContent {
|
||||||
|
public let position: CGPoint
|
||||||
|
public let item: NavigationControllerDropContentItem
|
||||||
|
|
||||||
|
public init(position: CGPoint, item: NavigationControllerDropContentItem) {
|
||||||
|
self.position = position
|
||||||
|
self.item = item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
open class NavigationController: UINavigationController, ContainableController, UIGestureRecognizerDelegate {
|
open class NavigationController: UINavigationController, ContainableController, UIGestureRecognizerDelegate {
|
||||||
public var isOpaqueWhenInOverlay: Bool = true
|
public var isOpaqueWhenInOverlay: Bool = true
|
||||||
public var blocksBackgroundWhenInOverlay: Bool = true
|
public var blocksBackgroundWhenInOverlay: Bool = true
|
||||||
@ -1221,6 +1234,35 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func updatePossibleControllerDropContent(content: NavigationControllerDropContent?) {
|
||||||
|
if let rootContainer = self.rootContainer {
|
||||||
|
switch rootContainer {
|
||||||
|
case let .flat(container):
|
||||||
|
if let controller = container.controllers.last {
|
||||||
|
controller.updatePossibleControllerDropContent(content: content)
|
||||||
|
}
|
||||||
|
case .split:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func acceptPossibleControllerDropContent(content: NavigationControllerDropContent) -> Bool {
|
||||||
|
if let rootContainer = self.rootContainer {
|
||||||
|
switch rootContainer {
|
||||||
|
case let .flat(container):
|
||||||
|
if let controller = container.controllers.last {
|
||||||
|
if controller.acceptPossibleControllerDropContent(content: content) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case .split:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
override open func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
|
override open func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||||
preconditionFailure()
|
preconditionFailure()
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ public final class NavigationBarBadgeNode: ASDisplayNode {
|
|||||||
self.textColor = textColor
|
self.textColor = textColor
|
||||||
self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: fillColor, strokeColor: strokeColor, strokeWidth: 1.0)
|
self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: fillColor, strokeColor: strokeColor, strokeWidth: 1.0)
|
||||||
self.textNode.attributedText = NSAttributedString(string: self.text, font: self.font, textColor: self.textColor)
|
self.textNode.attributedText = NSAttributedString(string: self.text, font: self.font, textColor: self.textColor)
|
||||||
|
self.textNode.redrawIfPossible()
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
override public func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
||||||
|
@ -644,6 +644,13 @@ public enum TabBarItemContextActionType {
|
|||||||
|
|
||||||
open func tabBarItemSwipeAction(direction: TabBarItemSwipeDirection) {
|
open func tabBarItemSwipeAction(direction: TabBarItemSwipeDirection) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open func updatePossibleControllerDropContent(content: NavigationControllerDropContent?) {
|
||||||
|
}
|
||||||
|
|
||||||
|
open func acceptPossibleControllerDropContent(content: NavigationControllerDropContent) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func traceIsOpaque(layer: CALayer, rect: CGRect) -> Bool {
|
func traceIsOpaque(layer: CALayer, rect: CGRect) -> Bool {
|
||||||
|
@ -1287,7 +1287,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
mediaManager?.setOverlayVideoNode(nil)
|
mediaManager?.setOverlayVideoNode(nil)
|
||||||
})
|
})
|
||||||
expandImpl = { [weak overlayNode] in
|
expandImpl = { [weak overlayNode] in
|
||||||
guard let contentInfo = item.contentInfo else {
|
guard let contentInfo = item.contentInfo, let overlayNode = overlayNode else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1302,7 +1302,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
|
|
||||||
baseNavigationController?.view.endEditing(true)
|
baseNavigationController?.view.endEditing(true)
|
||||||
|
|
||||||
(baseNavigationController?.topViewController as? ViewController)?.present(gallery, in: .window(.root), with: GalleryControllerPresentationArguments(transitionArguments: { id, media in
|
(baseNavigationController?.topViewController as? ViewController)?.present(gallery, in: .window(.root), with: GalleryControllerPresentationArguments(transitionArguments: { [weak overlayNode] id, media in
|
||||||
if let overlayNode = overlayNode, let overlaySupernode = overlayNode.supernode {
|
if let overlayNode = overlayNode, let overlaySupernode = overlayNode.supernode {
|
||||||
return GalleryTransitionArguments(transitionNode: (overlayNode, overlayNode.bounds, { [weak overlayNode] in
|
return GalleryTransitionArguments(transitionNode: (overlayNode, overlayNode.bounds, { [weak overlayNode] in
|
||||||
return (overlayNode?.view.snapshotContentTree(), nil)
|
return (overlayNode?.view.snapshotContentTree(), nil)
|
||||||
|
@ -42,6 +42,7 @@ private enum DebugControllerSection: Int32 {
|
|||||||
case logs
|
case logs
|
||||||
case logging
|
case logging
|
||||||
case experiments
|
case experiments
|
||||||
|
case videoExperiments
|
||||||
case info
|
case info
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,6 +71,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
case knockoutWallpaper(PresentationTheme, Bool)
|
case knockoutWallpaper(PresentationTheme, Bool)
|
||||||
case alternativeFolderTabs(Bool)
|
case alternativeFolderTabs(Bool)
|
||||||
case videoCalls(Bool)
|
case videoCalls(Bool)
|
||||||
|
case videoCallsInfo(PresentationTheme, String)
|
||||||
case hostInfo(PresentationTheme, String)
|
case hostInfo(PresentationTheme, String)
|
||||||
case versionInfo(PresentationTheme)
|
case versionInfo(PresentationTheme)
|
||||||
|
|
||||||
@ -83,8 +85,10 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
return DebugControllerSection.logging.rawValue
|
return DebugControllerSection.logging.rawValue
|
||||||
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
|
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
|
||||||
return DebugControllerSection.experiments.rawValue
|
return DebugControllerSection.experiments.rawValue
|
||||||
case .clearTips, .reimport, .resetData, .resetDatabase, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .alternativeFolderTabs, .videoCalls:
|
case .clearTips, .reimport, .resetData, .resetDatabase, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .alternativeFolderTabs:
|
||||||
return DebugControllerSection.experiments.rawValue
|
return DebugControllerSection.experiments.rawValue
|
||||||
|
case .videoCalls, .videoCallsInfo:
|
||||||
|
return DebugControllerSection.videoExperiments.rawValue
|
||||||
case .hostInfo, .versionInfo:
|
case .hostInfo, .versionInfo:
|
||||||
return DebugControllerSection.info.rawValue
|
return DebugControllerSection.info.rawValue
|
||||||
}
|
}
|
||||||
@ -140,10 +144,12 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
return 23
|
return 23
|
||||||
case .videoCalls:
|
case .videoCalls:
|
||||||
return 24
|
return 24
|
||||||
case .hostInfo:
|
case .videoCallsInfo:
|
||||||
return 25
|
return 25
|
||||||
case .versionInfo:
|
case .hostInfo:
|
||||||
return 26
|
return 26
|
||||||
|
case .versionInfo:
|
||||||
|
return 27
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -542,7 +548,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
}).start()
|
}).start()
|
||||||
})
|
})
|
||||||
case let .videoCalls(value):
|
case let .videoCalls(value):
|
||||||
return ItemListSwitchItem(presentationData: presentationData, title: "Video", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
return ItemListSwitchItem(presentationData: presentationData, title: "Experimental Feature", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||||
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
||||||
var settings = settings as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings
|
var settings = settings as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings
|
||||||
@ -551,6 +557,8 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
})
|
})
|
||||||
}).start()
|
}).start()
|
||||||
})
|
})
|
||||||
|
case let .videoCallsInfo(_, text):
|
||||||
|
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||||
case let .hostInfo(theme, string):
|
case let .hostInfo(theme, string):
|
||||||
return ItemListTextItem(presentationData: presentationData, text: .plain(string), sectionId: self.section)
|
return ItemListTextItem(presentationData: presentationData, text: .plain(string), sectionId: self.section)
|
||||||
case let .versionInfo(theme):
|
case let .versionInfo(theme):
|
||||||
@ -595,6 +603,7 @@ private func debugControllerEntries(presentationData: PresentationData, loggingS
|
|||||||
entries.append(.knockoutWallpaper(presentationData.theme, experimentalSettings.knockoutWallpaper))
|
entries.append(.knockoutWallpaper(presentationData.theme, experimentalSettings.knockoutWallpaper))
|
||||||
entries.append(.alternativeFolderTabs(experimentalSettings.foldersTabAtBottom))
|
entries.append(.alternativeFolderTabs(experimentalSettings.foldersTabAtBottom))
|
||||||
entries.append(.videoCalls(experimentalSettings.videoCalls))
|
entries.append(.videoCalls(experimentalSettings.videoCalls))
|
||||||
|
entries.append(.videoCallsInfo(presentationData.theme, "Enables experimental transmission of electromagnetic radiation synchronized with pressure waves. Needs to be enabled on both sides."))
|
||||||
|
|
||||||
if let backupHostOverride = networkSettings?.backupHostOverride {
|
if let backupHostOverride = networkSettings?.backupHostOverride {
|
||||||
entries.append(.hostInfo(presentationData.theme, "Host: \(backupHostOverride)"))
|
entries.append(.hostInfo(presentationData.theme, "Host: \(backupHostOverride)"))
|
||||||
|
@ -45,9 +45,9 @@ public extension TabBarControllerTheme {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public extension NavigationBarTheme {
|
public extension NavigationBarTheme {
|
||||||
convenience init(rootControllerTheme: PresentationTheme) {
|
convenience init(rootControllerTheme: PresentationTheme, hideBackground: Bool = false) {
|
||||||
let theme = rootControllerTheme.rootController.navigationBar
|
let theme = rootControllerTheme.rootController.navigationBar
|
||||||
self.init(buttonColor: theme.buttonColor, disabledButtonColor: theme.disabledButtonColor, primaryTextColor: theme.primaryTextColor, backgroundColor: theme.backgroundColor, separatorColor: theme.separatorColor, badgeBackgroundColor: theme.badgeBackgroundColor, badgeStrokeColor: theme.badgeStrokeColor, badgeTextColor: theme.badgeTextColor)
|
self.init(buttonColor: theme.buttonColor, disabledButtonColor: theme.disabledButtonColor, primaryTextColor: theme.primaryTextColor, backgroundColor: hideBackground ? .clear : theme.backgroundColor, separatorColor: hideBackground ? .clear : theme.separatorColor, badgeBackgroundColor: theme.badgeBackgroundColor, badgeStrokeColor: hideBackground ? .clear : theme.badgeStrokeColor, badgeTextColor: theme.badgeTextColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,6 +62,10 @@ public extension NavigationBarPresentationData {
|
|||||||
self.init(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme), strings: NavigationBarStrings(presentationStrings: presentationData.strings))
|
self.init(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme), strings: NavigationBarStrings(presentationStrings: presentationData.strings))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
convenience init(presentationData: PresentationData, hideBackground: Bool) {
|
||||||
|
self.init(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme, hideBackground: hideBackground), strings: NavigationBarStrings(presentationStrings: presentationData.strings))
|
||||||
|
}
|
||||||
|
|
||||||
convenience init(presentationTheme: PresentationTheme, presentationStrings: PresentationStrings) {
|
convenience init(presentationTheme: PresentationTheme, presentationStrings: PresentationStrings) {
|
||||||
self.init(theme: NavigationBarTheme(rootControllerTheme: presentationTheme), strings: NavigationBarStrings(presentationStrings: presentationStrings))
|
self.init(theme: NavigationBarTheme(rootControllerTheme: presentationTheme), strings: NavigationBarStrings(presentationStrings: presentationStrings))
|
||||||
}
|
}
|
||||||
|
@ -320,6 +320,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|
|
||||||
private let peekData: ChatPeekTimeout?
|
private let peekData: ChatPeekTimeout?
|
||||||
private let peekTimerDisposable = MetaDisposable()
|
private let peekTimerDisposable = MetaDisposable()
|
||||||
|
|
||||||
|
private var hasEmbeddedTitleContent = false
|
||||||
|
|
||||||
public override var customData: Any? {
|
public override var customData: Any? {
|
||||||
return self.chatLocation
|
return self.chatLocation
|
||||||
@ -373,7 +375,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
case .inline:
|
case .inline:
|
||||||
navigationBarPresentationData = nil
|
navigationBarPresentationData = nil
|
||||||
default:
|
default:
|
||||||
navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData)
|
navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData, hideBackground: true)
|
||||||
}
|
}
|
||||||
super.init(context: context, navigationBarPresentationData: navigationBarPresentationData, mediaAccessoryPanelVisibility: mediaAccessoryPanelVisibility, locationBroadcastPanelSource: locationBroadcastPanelSource)
|
super.init(context: context, navigationBarPresentationData: navigationBarPresentationData, mediaAccessoryPanelVisibility: mediaAccessoryPanelVisibility, locationBroadcastPanelSource: locationBroadcastPanelSource)
|
||||||
|
|
||||||
@ -2779,8 +2781,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
case .inline:
|
case .inline:
|
||||||
self.statusBar.statusBarStyle = .Ignore
|
self.statusBar.statusBarStyle = .Ignore
|
||||||
}
|
}
|
||||||
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))
|
self.updateNavigationBarPresentation()
|
||||||
self.chatTitleView?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings)
|
|
||||||
self.updateChatPresentationInterfaceState(animated: false, interactive: false, { state in
|
self.updateChatPresentationInterfaceState(animated: false, interactive: false, { state in
|
||||||
var state = state
|
var state = state
|
||||||
state = state.updatedTheme(self.presentationData.theme)
|
state = state.updatedTheme(self.presentationData.theme)
|
||||||
@ -2794,6 +2795,20 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
self.currentContextController?.updateTheme(presentationData: self.presentationData)
|
self.currentContextController?.updateTheme(presentationData: self.presentationData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func updateNavigationBarPresentation() {
|
||||||
|
let navigationBarTheme: NavigationBarTheme
|
||||||
|
|
||||||
|
if self.hasEmbeddedTitleContent {
|
||||||
|
navigationBarTheme = NavigationBarTheme(rootControllerTheme: defaultDarkPresentationTheme, hideBackground: true)
|
||||||
|
} else {
|
||||||
|
navigationBarTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme, hideBackground: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)))
|
||||||
|
|
||||||
|
self.chatTitleView?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings, hasEmbeddedTitleContent: self.hasEmbeddedTitleContent)
|
||||||
|
}
|
||||||
|
|
||||||
override public func loadDisplayNode() {
|
override public func loadDisplayNode() {
|
||||||
self.displayNode = ChatControllerNode(context: self.context, chatLocation: self.chatLocation, subject: self.subject, controllerInteraction: self.controllerInteraction!, chatPresentationInterfaceState: self.presentationInterfaceState, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, navigationBar: self.navigationBar, controller: self)
|
self.displayNode = ChatControllerNode(context: self.context, chatLocation: self.chatLocation, subject: self.subject, controllerInteraction: self.controllerInteraction!, chatPresentationInterfaceState: self.presentationInterfaceState, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, navigationBar: self.navigationBar, controller: self)
|
||||||
|
|
||||||
@ -4735,6 +4750,32 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.chatDisplayNode.updateHasEmbeddedTitleContent = { [weak self] hasEmbeddedTitleContent in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if strongSelf.hasEmbeddedTitleContent != hasEmbeddedTitleContent {
|
||||||
|
strongSelf.hasEmbeddedTitleContent = hasEmbeddedTitleContent
|
||||||
|
|
||||||
|
if strongSelf.hasEmbeddedTitleContent {
|
||||||
|
strongSelf.statusBar.statusBarStyle = .White
|
||||||
|
} else {
|
||||||
|
strongSelf.statusBar.statusBarStyle = strongSelf.presentationData.theme.rootController.statusBarStyle.style
|
||||||
|
}
|
||||||
|
|
||||||
|
if let navigationBar = strongSelf.navigationBar {
|
||||||
|
if let navigationBarCopy = navigationBar.view.snapshotContentTree() {
|
||||||
|
navigationBar.view.superview?.insertSubview(navigationBarCopy, aboveSubview: navigationBar.view)
|
||||||
|
navigationBarCopy.alpha = 0.0
|
||||||
|
navigationBarCopy.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak navigationBarCopy] _ in
|
||||||
|
navigationBarCopy?.removeFromSuperview()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strongSelf.updateNavigationBarPresentation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.interfaceInteraction = interfaceInteraction
|
self.interfaceInteraction = interfaceInteraction
|
||||||
|
|
||||||
if let search = self.focusOnSearchAfterAppearance {
|
if let search = self.focusOnSearchAfterAppearance {
|
||||||
@ -5143,7 +5184,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|
|
||||||
switch self.presentationInterfaceState.mode {
|
switch self.presentationInterfaceState.mode {
|
||||||
case .standard, .inline:
|
case .standard, .inline:
|
||||||
break
|
break
|
||||||
case .overlay:
|
case .overlay:
|
||||||
if case .Ignore = self.statusBar.statusBarStyle {
|
if case .Ignore = self.statusBar.statusBarStyle {
|
||||||
} else if layout.safeInsets.top.isZero {
|
} else if layout.safeInsets.top.isZero {
|
||||||
@ -5503,7 +5544,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|
|
||||||
switch updatedChatPresentationInterfaceState.mode {
|
switch updatedChatPresentationInterfaceState.mode {
|
||||||
case .standard:
|
case .standard:
|
||||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
if self.hasEmbeddedTitleContent {
|
||||||
|
self.statusBar.statusBarStyle = .White
|
||||||
|
} else {
|
||||||
|
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||||
|
}
|
||||||
self.deferScreenEdgeGestures = []
|
self.deferScreenEdgeGestures = []
|
||||||
case .overlay:
|
case .overlay:
|
||||||
self.deferScreenEdgeGestures = [.top]
|
self.deferScreenEdgeGestures = [.top]
|
||||||
@ -9256,6 +9301,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
self.focusOnSearchAfterAppearance = (domain, query)
|
self.focusOnSearchAfterAppearance = (domain, query)
|
||||||
self.interfaceInteraction?.beginMessageSearch(domain, query)
|
self.interfaceInteraction?.beginMessageSearch(domain, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override public func updatePossibleControllerDropContent(content: NavigationControllerDropContent?) {
|
||||||
|
self.chatDisplayNode.updateEmbeddedTitlePeekContent(content: content)
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func acceptPossibleControllerDropContent(content: NavigationControllerDropContent) -> Bool {
|
||||||
|
return self.chatDisplayNode.acceptEmbeddedTitlePeekContent(content: content)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
|
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
|
||||||
|
@ -12,6 +12,15 @@ import TextFormat
|
|||||||
import AccountContext
|
import AccountContext
|
||||||
import TelegramNotices
|
import TelegramNotices
|
||||||
import ReactionSelectionNode
|
import ReactionSelectionNode
|
||||||
|
import TelegramUniversalVideoContent
|
||||||
|
|
||||||
|
final class VideoNavigationControllerDropContentItem: NavigationControllerDropContentItem {
|
||||||
|
let itemNode: OverlayMediaItemNode
|
||||||
|
|
||||||
|
init(itemNode: OverlayMediaItemNode) {
|
||||||
|
self.itemNode = itemNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final class ChatControllerNodeView: UITracingLayerView, WindowInputAccessoryHeightProvider, PreviewingHostView {
|
private final class ChatControllerNodeView: UITracingLayerView, WindowInputAccessoryHeightProvider, PreviewingHostView {
|
||||||
var inputAccessoryHeight: (() -> CGFloat)?
|
var inputAccessoryHeight: (() -> CGFloat)?
|
||||||
@ -56,6 +65,175 @@ private struct ChatControllerNodeDerivedLayoutState {
|
|||||||
var upperInputPositionBound: CGFloat?
|
var upperInputPositionBound: CGFloat?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class ChatEmbeddedTitleContentNode: ASDisplayNode {
|
||||||
|
private let context: AccountContext
|
||||||
|
private let backgroundNode: ASDisplayNode
|
||||||
|
private let videoNode: OverlayUniversalVideoNode
|
||||||
|
|
||||||
|
private var validLayout: (CGSize, CGFloat, CGFloat)?
|
||||||
|
|
||||||
|
private let dismissed: () -> Void
|
||||||
|
private let interactiveExtensionUpdated: (ContainedViewLayoutTransition) -> Void
|
||||||
|
|
||||||
|
private(set) var interactiveExtension: CGFloat = 0.0
|
||||||
|
private var freezeInteractiveExtension = false
|
||||||
|
|
||||||
|
init(context: AccountContext, videoNode: OverlayUniversalVideoNode, interactiveExtensionUpdated: @escaping (ContainedViewLayoutTransition) -> Void, dismissed: @escaping () -> Void) {
|
||||||
|
self.dismissed = dismissed
|
||||||
|
self.interactiveExtensionUpdated = interactiveExtensionUpdated
|
||||||
|
|
||||||
|
self.context = context
|
||||||
|
|
||||||
|
self.backgroundNode = ASDisplayNode()
|
||||||
|
self.backgroundNode.backgroundColor = .black
|
||||||
|
|
||||||
|
self.videoNode = videoNode
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.clipsToBounds = true
|
||||||
|
|
||||||
|
self.addSubnode(self.backgroundNode)
|
||||||
|
|
||||||
|
self.view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))))
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||||
|
switch recognizer.state {
|
||||||
|
case .began:
|
||||||
|
break
|
||||||
|
case .changed:
|
||||||
|
let translation = recognizer.translation(in: self.view)
|
||||||
|
|
||||||
|
func rubberBandingOffset(offset: CGFloat, bandingStart: CGFloat) -> CGFloat {
|
||||||
|
let bandedOffset = offset - bandingStart
|
||||||
|
let range: CGFloat = 600.0
|
||||||
|
let coefficient: CGFloat = 0.4
|
||||||
|
return bandingStart + (1.0 - (1.0 / ((bandedOffset * coefficient / range) + 1.0))) * range
|
||||||
|
}
|
||||||
|
|
||||||
|
let offset = rubberBandingOffset(offset: translation.y, bandingStart: 0.0)
|
||||||
|
|
||||||
|
if translation.y > 80.0 {
|
||||||
|
self.freezeInteractiveExtension = true
|
||||||
|
self.videoNode.customExpand?()
|
||||||
|
} else {
|
||||||
|
self.interactiveExtension = max(0.0, offset)
|
||||||
|
self.interactiveExtensionUpdated(.immediate)
|
||||||
|
}
|
||||||
|
case .cancelled, .ended:
|
||||||
|
if !freezeInteractiveExtension {
|
||||||
|
self.interactiveExtension = 0.0
|
||||||
|
self.interactiveExtensionUpdated(.animated(duration: 0.3, curve: .spring))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateHeight(width: CGFloat) -> CGFloat {
|
||||||
|
return self.videoNode.content.dimensions.aspectFilled(CGSize(width: width, height: 16.0)).height
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateLayout(size: CGSize, topInset: CGFloat, interactiveExtension: CGFloat, transition: ContainedViewLayoutTransition, transitionSurface: ASDisplayNode?, navigationBar: NavigationBar?) {
|
||||||
|
let isFirstTime = self.validLayout == nil
|
||||||
|
|
||||||
|
self.validLayout = (size, topInset, interactiveExtension)
|
||||||
|
|
||||||
|
let videoFrame = CGRect(origin: CGPoint(x: 0.0, y: topInset + interactiveExtension), size: CGSize(width: size.width, height: size.height - topInset - interactiveExtension))
|
||||||
|
|
||||||
|
if isFirstTime, let transitionSurface = transitionSurface {
|
||||||
|
let sourceFrame = self.videoNode.view.convert(self.videoNode.bounds, to: transitionSurface.view)
|
||||||
|
let targetFrame = self.view.convert(videoFrame, to: transitionSurface.view)
|
||||||
|
|
||||||
|
self.context.sharedContext.mediaManager.setOverlayVideoNode(nil)
|
||||||
|
transitionSurface.addSubnode(self.videoNode)
|
||||||
|
|
||||||
|
let navigationBarCopy = navigationBar?.view.snapshotView(afterScreenUpdates: true)
|
||||||
|
let navigationBarContainer = UIView()
|
||||||
|
navigationBarContainer.frame = targetFrame
|
||||||
|
navigationBarContainer.clipsToBounds = true
|
||||||
|
transitionSurface.view.addSubview(navigationBarContainer)
|
||||||
|
|
||||||
|
navigationBarContainer.layer.animateFrame(from: sourceFrame, to: targetFrame, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
|
||||||
|
if let navigationBar = navigationBar, let navigationBarCopy = navigationBarCopy {
|
||||||
|
let navigationFrame = navigationBar.view.convert(navigationBar.bounds, to: transitionSurface.view)
|
||||||
|
let navigationSourceFrame = navigationFrame.offsetBy(dx: -sourceFrame.minX, dy: -sourceFrame.minY)
|
||||||
|
let navigationTargetFrame = navigationFrame.offsetBy(dx: -targetFrame.minX, dy: -targetFrame.minY)
|
||||||
|
navigationBarCopy.frame = navigationTargetFrame
|
||||||
|
navigationBarContainer.addSubview(navigationBarCopy)
|
||||||
|
|
||||||
|
navigationBarCopy.layer.animateFrame(from: navigationSourceFrame, to: navigationTargetFrame, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
navigationBarCopy.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.videoNode.updateRoundCorners(false, transition: .animated(duration: 0.25, curve: .spring))
|
||||||
|
self.videoNode.showControls()
|
||||||
|
|
||||||
|
self.videoNode.updateLayout(targetFrame.size, transition: .animated(duration: 0.25, curve: .spring))
|
||||||
|
self.videoNode.frame = targetFrame
|
||||||
|
self.videoNode.layer.animateFrame(from: sourceFrame, to: targetFrame, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak self] _ in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
navigationBarContainer.removeFromSuperview()
|
||||||
|
strongSelf.addSubnode(strongSelf.videoNode)
|
||||||
|
if let (size, topInset, interactiveExtension) = strongSelf.validLayout {
|
||||||
|
strongSelf.updateLayout(size: size, topInset: topInset, interactiveExtension: interactiveExtension, transition: .immediate, transitionSurface: nil, navigationBar: nil)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
self.backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
|
|
||||||
|
self.videoNode.customExpand = { [weak self] in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let transition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .spring)
|
||||||
|
|
||||||
|
strongSelf.videoNode.customExpand = nil
|
||||||
|
strongSelf.videoNode.customClose = nil
|
||||||
|
|
||||||
|
let previousFrame = strongSelf.videoNode.frame
|
||||||
|
strongSelf.context.sharedContext.mediaManager.setOverlayVideoNode(strongSelf.videoNode)
|
||||||
|
strongSelf.videoNode.updateRoundCorners(true, transition: transition)
|
||||||
|
|
||||||
|
if let targetSuperview = strongSelf.videoNode.view.superview {
|
||||||
|
let sourceFrame = strongSelf.view.convert(previousFrame, to: targetSuperview)
|
||||||
|
let targetFrame = strongSelf.videoNode.frame
|
||||||
|
strongSelf.videoNode.frame = sourceFrame
|
||||||
|
strongSelf.videoNode.updateLayout(sourceFrame.size, transition: .immediate)
|
||||||
|
|
||||||
|
transition.updateFrame(node: strongSelf.videoNode, frame: targetFrame)
|
||||||
|
strongSelf.videoNode.updateLayout(targetFrame.size, transition: transition)
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.dismissed()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.videoNode.customClose = { [weak self] in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.videoNode.customClose = nil
|
||||||
|
strongSelf.dismissed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
|
if self.videoNode.supernode == self {
|
||||||
|
self.videoNode.layer.transform = CATransform3DIdentity
|
||||||
|
transition.updateFrame(node: self.videoNode, frame: videoFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ChatEmbeddedTitlePeekContent: Equatable {
|
||||||
|
case none
|
||||||
|
case peek
|
||||||
|
}
|
||||||
|
|
||||||
class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let chatLocation: ChatLocation
|
let chatLocation: ChatLocation
|
||||||
@ -63,6 +241,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
private weak var controller: ChatControllerImpl?
|
private weak var controller: ChatControllerImpl?
|
||||||
|
|
||||||
let navigationBar: NavigationBar?
|
let navigationBar: NavigationBar?
|
||||||
|
private let navigationBarBackroundNode: ASDisplayNode
|
||||||
|
private let navigationBarSeparatorNode: ASDisplayNode
|
||||||
|
|
||||||
private var backgroundEffectNode: ASDisplayNode?
|
private var backgroundEffectNode: ASDisplayNode?
|
||||||
private var containerBackgroundNode: ASImageNode?
|
private var containerBackgroundNode: ASImageNode?
|
||||||
@ -204,6 +384,10 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
|
|
||||||
private var onLayoutCompletions: [(ContainedViewLayoutTransition) -> Void] = []
|
private var onLayoutCompletions: [(ContainedViewLayoutTransition) -> Void] = []
|
||||||
|
|
||||||
|
private var embeddedTitlePeekContent: ChatEmbeddedTitlePeekContent = .none
|
||||||
|
private var embeddedTitleContentNode: ChatEmbeddedTitleContentNode?
|
||||||
|
private var dismissedEmbeddedTitleContentNode: ChatEmbeddedTitleContentNode?
|
||||||
|
|
||||||
init(context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject?, controllerInteraction: ChatControllerInteraction, chatPresentationInterfaceState: ChatPresentationInterfaceState, automaticMediaDownloadSettings: MediaAutoDownloadSettings, navigationBar: NavigationBar?, controller: ChatControllerImpl?) {
|
init(context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject?, controllerInteraction: ChatControllerInteraction, chatPresentationInterfaceState: ChatPresentationInterfaceState, automaticMediaDownloadSettings: MediaAutoDownloadSettings, navigationBar: NavigationBar?, controller: ChatControllerImpl?) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.chatLocation = chatLocation
|
self.chatLocation = chatLocation
|
||||||
@ -248,6 +432,12 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
self.navigateButtons = ChatHistoryNavigationButtons(theme: self.chatPresentationInterfaceState.theme)
|
self.navigateButtons = ChatHistoryNavigationButtons(theme: self.chatPresentationInterfaceState.theme)
|
||||||
self.navigateButtons.accessibilityElementsHidden = true
|
self.navigateButtons.accessibilityElementsHidden = true
|
||||||
|
|
||||||
|
self.navigationBarBackroundNode = ASDisplayNode()
|
||||||
|
self.navigationBarBackroundNode.backgroundColor = chatPresentationInterfaceState.theme.rootController.navigationBar.backgroundColor
|
||||||
|
|
||||||
|
self.navigationBarSeparatorNode = ASDisplayNode()
|
||||||
|
self.navigationBarSeparatorNode.backgroundColor = chatPresentationInterfaceState.theme.rootController.navigationBar.separatorColor
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.controller?.presentationContext.topLevelSubview = { [weak self] in
|
self.controller?.presentationContext.topLevelSubview = { [weak self] in
|
||||||
@ -329,6 +519,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
|
|
||||||
self.addSubnode(self.navigateButtons)
|
self.addSubnode(self.navigateButtons)
|
||||||
|
|
||||||
|
self.addSubnode(self.navigationBarBackroundNode)
|
||||||
|
self.addSubnode(self.navigationBarSeparatorNode)
|
||||||
|
|
||||||
self.historyNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
|
self.historyNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
|
||||||
|
|
||||||
self.textInputPanelNode = ChatTextInputPanelNode(presentationInterfaceState: chatPresentationInterfaceState, presentController: { [weak self] controller in
|
self.textInputPanelNode = ChatTextInputPanelNode(presentationInterfaceState: chatPresentationInterfaceState, presentController: { [weak self] controller in
|
||||||
@ -696,12 +889,63 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
} else {
|
} else {
|
||||||
insets = layout.insets(options: [.input])
|
insets = layout.insets(options: [.input])
|
||||||
}
|
}
|
||||||
if case .overlay = self.chatPresentationInterfaceState.mode {
|
|
||||||
insets.top = 44.0
|
let statusBarHeight = layout.insets(options: [.statusBar]).top
|
||||||
|
|
||||||
|
if let embeddedTitleContentNode = self.embeddedTitleContentNode {
|
||||||
|
let embeddedSize = CGSize(width: layout.size.width, height: min(400.0, embeddedTitleContentNode.calculateHeight(width: layout.size.width)) + statusBarHeight + embeddedTitleContentNode.interactiveExtension)
|
||||||
|
if embeddedTitleContentNode.supernode == nil {
|
||||||
|
self.insertSubnode(embeddedTitleContentNode, aboveSubnode: self.navigationBarBackroundNode)
|
||||||
|
|
||||||
|
var previousTopInset = insets.top
|
||||||
|
if case .overlay = self.chatPresentationInterfaceState.mode {
|
||||||
|
previousTopInset = 44.0
|
||||||
|
} else {
|
||||||
|
previousTopInset += navigationBarHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
if case .peek = self.embeddedTitlePeekContent {
|
||||||
|
previousTopInset += 32.0
|
||||||
|
}
|
||||||
|
|
||||||
|
embeddedTitleContentNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: previousTopInset))
|
||||||
|
transition.updateFrame(node: embeddedTitleContentNode, frame: CGRect(origin: CGPoint(), size: embeddedSize))
|
||||||
|
embeddedTitleContentNode.updateLayout(size: embeddedSize, topInset: statusBarHeight, interactiveExtension: embeddedTitleContentNode.interactiveExtension, transition: .immediate, transitionSurface: self, navigationBar: self.navigationBar)
|
||||||
|
} else {
|
||||||
|
transition.updateFrame(node: embeddedTitleContentNode, frame: CGRect(origin: CGPoint(), size: embeddedSize))
|
||||||
|
embeddedTitleContentNode.updateLayout(size: embeddedSize, topInset: statusBarHeight, interactiveExtension: embeddedTitleContentNode.interactiveExtension, transition: transition, transitionSurface: self, navigationBar: self.navigationBar)
|
||||||
|
}
|
||||||
|
|
||||||
|
insets.top += embeddedSize.height
|
||||||
} else {
|
} else {
|
||||||
insets.top += navigationBarHeight
|
if case .overlay = self.chatPresentationInterfaceState.mode {
|
||||||
|
insets.top = 44.0
|
||||||
|
} else {
|
||||||
|
insets.top += navigationBarHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
if case .peek = self.embeddedTitlePeekContent {
|
||||||
|
insets.top += 32.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let dismissedEmbeddedTitleContentNode = self.dismissedEmbeddedTitleContentNode {
|
||||||
|
self.dismissedEmbeddedTitleContentNode = nil
|
||||||
|
if transition.isAnimated {
|
||||||
|
dismissedEmbeddedTitleContentNode.alpha = 0.0
|
||||||
|
dismissedEmbeddedTitleContentNode.layer.allowsGroupOpacity = true
|
||||||
|
dismissedEmbeddedTitleContentNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, completion: { [weak dismissedEmbeddedTitleContentNode] _ in
|
||||||
|
dismissedEmbeddedTitleContentNode?.removeFromSupernode()
|
||||||
|
})
|
||||||
|
transition.updateFrame(node: dismissedEmbeddedTitleContentNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: insets.top)))
|
||||||
|
} else {
|
||||||
|
dismissedEmbeddedTitleContentNode.removeFromSupernode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.navigationBarBackroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: insets.top)))
|
||||||
|
transition.updateFrame(node: self.navigationBarSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
|
||||||
|
|
||||||
var wrappingInsets = UIEdgeInsets()
|
var wrappingInsets = UIEdgeInsets()
|
||||||
if case .overlay = self.chatPresentationInterfaceState.mode {
|
if case .overlay = self.chatPresentationInterfaceState.mode {
|
||||||
let containerWidth = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: 8.0 + layout.safeInsets.left)
|
let containerWidth = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: 8.0 + layout.safeInsets.left)
|
||||||
@ -1518,6 +1762,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
self.updatePlainInputSeparator(transition: .immediate)
|
self.updatePlainInputSeparator(transition: .immediate)
|
||||||
self.inputPanelBackgroundSeparatorNode.backgroundColor = self.chatPresentationInterfaceState.theme.chat.inputPanel.panelSeparatorColor
|
self.inputPanelBackgroundSeparatorNode.backgroundColor = self.chatPresentationInterfaceState.theme.chat.inputPanel.panelSeparatorColor
|
||||||
|
|
||||||
|
self.navigationBarBackroundNode.backgroundColor = chatPresentationInterfaceState.theme.rootController.navigationBar.backgroundColor
|
||||||
|
self.navigationBarSeparatorNode.backgroundColor = chatPresentationInterfaceState.theme.rootController.navigationBar.separatorColor
|
||||||
}
|
}
|
||||||
|
|
||||||
let keepSendButtonEnabled = chatPresentationInterfaceState.interfaceState.forwardMessageIds != nil || chatPresentationInterfaceState.interfaceState.editMessage != nil
|
let keepSendButtonEnabled = chatPresentationInterfaceState.interfaceState.forwardMessageIds != nil || chatPresentationInterfaceState.interfaceState.editMessage != nil
|
||||||
@ -2384,4 +2631,58 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
func animateQuizCorrectOptionSelected() {
|
func animateQuizCorrectOptionSelected() {
|
||||||
self.view.insertSubview(ConfettiView(frame: self.view.bounds), aboveSubview: self.historyNode.view)
|
self.view.insertSubview(ConfettiView(frame: self.view.bounds), aboveSubview: self.historyNode.view)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateEmbeddedTitlePeekContent(content: NavigationControllerDropContent?) {
|
||||||
|
guard let (_, navigationHeight) = self.validLayout else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var peekContent: ChatEmbeddedTitlePeekContent = .none
|
||||||
|
if let content = content, let item = content.item as? VideoNavigationControllerDropContentItem, let _ = item.itemNode as? OverlayUniversalVideoNode {
|
||||||
|
if content.position.y < navigationHeight + 32.0 {
|
||||||
|
peekContent = .peek
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.embeddedTitlePeekContent != peekContent {
|
||||||
|
self.embeddedTitlePeekContent = peekContent
|
||||||
|
self.requestLayout(.animated(duration: 0.3, curve: .spring))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var updateHasEmbeddedTitleContent: ((Bool) -> Void)?
|
||||||
|
|
||||||
|
func acceptEmbeddedTitlePeekContent(content: NavigationControllerDropContent) -> Bool {
|
||||||
|
guard let (_, navigationHeight) = self.validLayout else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if content.position.y >= navigationHeight + 32.0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if let item = content.item as? VideoNavigationControllerDropContentItem, let itemNode = item.itemNode as? OverlayUniversalVideoNode {
|
||||||
|
let embeddedTitleContentNode = ChatEmbeddedTitleContentNode(context: self.context, videoNode: itemNode, interactiveExtensionUpdated: { [weak self] transition in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.requestLayout(transition)
|
||||||
|
}, dismissed: { [weak self] in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let embeddedTitleContentNode = strongSelf.embeddedTitleContentNode {
|
||||||
|
strongSelf.embeddedTitleContentNode = nil
|
||||||
|
strongSelf.dismissedEmbeddedTitleContentNode = embeddedTitleContentNode
|
||||||
|
strongSelf.requestLayout(.animated(duration: 0.25, curve: .spring))
|
||||||
|
strongSelf.updateHasEmbeddedTitleContent?(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
self.embeddedTitleContentNode = embeddedTitleContentNode
|
||||||
|
self.embeddedTitlePeekContent = .none
|
||||||
|
self.updateHasEmbeddedTitleContent?(true)
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.requestLayout(.animated(duration: 0.25, curve: .spring))
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,61 +23,6 @@ enum ChatTitleContent {
|
|||||||
case custom(String)
|
case custom(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class ChatTitleNetworkStatusNode: ASDisplayNode {
|
|
||||||
private var theme: PresentationTheme
|
|
||||||
|
|
||||||
private let titleNode: ImmediateTextNode
|
|
||||||
private let activityIndicator: ActivityIndicator
|
|
||||||
|
|
||||||
var title: String = "" {
|
|
||||||
didSet {
|
|
||||||
if self.title != oldValue {
|
|
||||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.bold(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init(theme: PresentationTheme) {
|
|
||||||
self.theme = theme
|
|
||||||
|
|
||||||
self.titleNode = ImmediateTextNode()
|
|
||||||
self.titleNode.isUserInteractionEnabled = false
|
|
||||||
self.titleNode.displaysAsynchronously = false
|
|
||||||
self.titleNode.maximumNumberOfLines = 1
|
|
||||||
self.titleNode.isOpaque = false
|
|
||||||
self.titleNode.isUserInteractionEnabled = false
|
|
||||||
|
|
||||||
self.activityIndicator = ActivityIndicator(type: .custom(theme.rootController.navigationBar.primaryTextColor, 22.0, 1.5, false), speed: .slow)
|
|
||||||
let activityIndicatorSize = self.activityIndicator.measure(CGSize(width: 100.0, height: 100.0))
|
|
||||||
self.activityIndicator.frame = CGRect(origin: CGPoint(), size: activityIndicatorSize)
|
|
||||||
|
|
||||||
super.init()
|
|
||||||
|
|
||||||
self.addSubnode(self.titleNode)
|
|
||||||
self.addSubnode(self.activityIndicator)
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateTheme(theme: PresentationTheme) {
|
|
||||||
self.theme = theme
|
|
||||||
|
|
||||||
self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.medium(24.0), textColor: self.theme.rootController.navigationBar.primaryTextColor)
|
|
||||||
self.activityIndicator.type = .custom(self.theme.rootController.navigationBar.primaryTextColor, 22.0, 1.5, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
|
||||||
let indicatorSize = self.activityIndicator.bounds.size
|
|
||||||
let indicatorPadding = indicatorSize.width + 6.0
|
|
||||||
|
|
||||||
let titleSize = self.titleNode.updateLayout(CGSize(width: max(1.0, size.width - indicatorPadding), height: size.height))
|
|
||||||
let combinedHeight = titleSize.height
|
|
||||||
|
|
||||||
let titleFrame = CGRect(origin: CGPoint(x: indicatorPadding + floor((size.width - titleSize.width - indicatorPadding) / 2.0), y: floor((size.height - combinedHeight) / 2.0)), size: titleSize)
|
|
||||||
transition.updateFrame(node: self.titleNode, frame: titleFrame)
|
|
||||||
|
|
||||||
transition.updateFrame(node: self.activityIndicator, frame: CGRect(origin: CGPoint(x: titleFrame.minX - indicatorSize.width - 4.0, y: titleFrame.minY - 1.0), size: indicatorSize))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum ChatTitleIcon {
|
private enum ChatTitleIcon {
|
||||||
case none
|
case none
|
||||||
case lock
|
case lock
|
||||||
@ -88,6 +33,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
private let account: Account
|
private let account: Account
|
||||||
|
|
||||||
private var theme: PresentationTheme
|
private var theme: PresentationTheme
|
||||||
|
private var hasEmbeddedTitleContent: Bool = false
|
||||||
private var strings: PresentationStrings
|
private var strings: PresentationStrings
|
||||||
private var dateTimeFormat: PresentationDateTimeFormat
|
private var dateTimeFormat: PresentationDateTimeFormat
|
||||||
private var nameDisplayOrder: PresentationPersonNameOrder
|
private var nameDisplayOrder: PresentationPersonNameOrder
|
||||||
@ -120,43 +66,6 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func updateNetworkStatusNode(networkState: AccountNetworkState, layout: ContainerViewLayout?) {
|
private func updateNetworkStatusNode(networkState: AccountNetworkState, layout: ContainerViewLayout?) {
|
||||||
var isOnline = false
|
|
||||||
if case .online = networkState {
|
|
||||||
isOnline = true
|
|
||||||
}
|
|
||||||
|
|
||||||
/*if isOnline || layout?.metrics.widthClass == .regular {
|
|
||||||
self.contentContainer.isHidden = false
|
|
||||||
if let networkStatusNode = self.networkStatusNode {
|
|
||||||
self.networkStatusNode = nil
|
|
||||||
networkStatusNode.removeFromSupernode()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.contentContainer.isHidden = true
|
|
||||||
let statusNode: ChatTitleNetworkStatusNode
|
|
||||||
if let current = self.networkStatusNode {
|
|
||||||
statusNode = current
|
|
||||||
} else {
|
|
||||||
statusNode = ChatTitleNetworkStatusNode(theme: self.theme)
|
|
||||||
self.networkStatusNode = statusNode
|
|
||||||
self.insertSubview(statusNode.view, aboveSubview: self.contentContainer.view)
|
|
||||||
}
|
|
||||||
switch self.networkState {
|
|
||||||
case .waitingForNetwork:
|
|
||||||
statusNode.title = self.strings.State_WaitingForNetwork
|
|
||||||
case let .connecting(proxy):
|
|
||||||
if let layout = layout, proxy != nil && layout.size.width > 320.0 {
|
|
||||||
statusNode.title = self.strings.State_ConnectingToProxy
|
|
||||||
} else {
|
|
||||||
statusNode.title = self.strings.State_Connecting
|
|
||||||
}
|
|
||||||
case .updating:
|
|
||||||
statusNode.title = self.strings.State_Updating
|
|
||||||
case .online:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
self.setNeedsLayout()
|
self.setNeedsLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,6 +92,8 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
var titleContent: ChatTitleContent? {
|
var titleContent: ChatTitleContent? {
|
||||||
didSet {
|
didSet {
|
||||||
if let titleContent = self.titleContent {
|
if let titleContent = self.titleContent {
|
||||||
|
let titleTheme = self.hasEmbeddedTitleContent ? defaultDarkPresentationTheme : self.theme
|
||||||
|
|
||||||
var string: NSAttributedString?
|
var string: NSAttributedString?
|
||||||
var titleLeftIcon: ChatTitleIcon = .none
|
var titleLeftIcon: ChatTitleIcon = .none
|
||||||
var titleRightIcon: ChatTitleIcon = .none
|
var titleRightIcon: ChatTitleIcon = .none
|
||||||
@ -192,20 +103,20 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
case let .peer(peerView, _, isScheduledMessages):
|
case let .peer(peerView, _, isScheduledMessages):
|
||||||
if isScheduledMessages {
|
if isScheduledMessages {
|
||||||
if peerView.peerId == self.account.peerId {
|
if peerView.peerId == self.account.peerId {
|
||||||
string = NSAttributedString(string: self.strings.ScheduledMessages_RemindersTitle, font: Font.medium(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor)
|
string = NSAttributedString(string: self.strings.ScheduledMessages_RemindersTitle, font: Font.medium(17.0), textColor: titleTheme.rootController.navigationBar.primaryTextColor)
|
||||||
} else {
|
} else {
|
||||||
string = NSAttributedString(string: self.strings.ScheduledMessages_Title, font: Font.medium(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor)
|
string = NSAttributedString(string: self.strings.ScheduledMessages_Title, font: Font.medium(17.0), textColor: titleTheme.rootController.navigationBar.primaryTextColor)
|
||||||
}
|
}
|
||||||
isEnabled = false
|
isEnabled = false
|
||||||
} else {
|
} else {
|
||||||
if let peer = peerViewMainPeer(peerView) {
|
if let peer = peerViewMainPeer(peerView) {
|
||||||
if peerView.peerId == self.account.peerId {
|
if peerView.peerId == self.account.peerId {
|
||||||
string = NSAttributedString(string: self.strings.Conversation_SavedMessages, font: Font.medium(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor)
|
string = NSAttributedString(string: self.strings.Conversation_SavedMessages, font: Font.medium(17.0), textColor: titleTheme.rootController.navigationBar.primaryTextColor)
|
||||||
} else {
|
} else {
|
||||||
if !peerView.peerIsContact, let user = peer as? TelegramUser, !user.flags.contains(.isSupport), user.botInfo == nil, let phone = user.phone, !phone.isEmpty {
|
if !peerView.peerIsContact, let user = peer as? TelegramUser, !user.flags.contains(.isSupport), user.botInfo == nil, let phone = user.phone, !phone.isEmpty {
|
||||||
string = NSAttributedString(string: formatPhoneNumber(phone), font: Font.medium(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor)
|
string = NSAttributedString(string: formatPhoneNumber(phone), font: Font.medium(17.0), textColor: titleTheme.rootController.navigationBar.primaryTextColor)
|
||||||
} else {
|
} else {
|
||||||
string = NSAttributedString(string: peer.displayTitle(strings: self.strings, displayOrder: self.nameDisplayOrder), font: Font.medium(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor)
|
string = NSAttributedString(string: peer.displayTitle(strings: self.strings, displayOrder: self.nameDisplayOrder), font: Font.medium(17.0), textColor: titleTheme.rootController.navigationBar.primaryTextColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
titleScamIcon = peer.isScam
|
titleScamIcon = peer.isScam
|
||||||
@ -220,9 +131,9 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case .group:
|
case .group:
|
||||||
string = NSAttributedString(string: "Feed", font: Font.medium(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor)
|
string = NSAttributedString(string: "Feed", font: Font.medium(17.0), textColor: titleTheme.rootController.navigationBar.primaryTextColor)
|
||||||
case let .custom(text):
|
case let .custom(text):
|
||||||
string = NSAttributedString(string: text, font: Font.medium(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor)
|
string = NSAttributedString(string: text, font: Font.medium(17.0), textColor: titleTheme.rootController.navigationBar.primaryTextColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let string = string, self.titleNode.attributedText == nil || !self.titleNode.attributedText!.isEqual(to: string) {
|
if let string = string, self.titleNode.attributedText == nil || !self.titleNode.attributedText!.isEqual(to: string) {
|
||||||
@ -234,7 +145,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
self.titleLeftIcon = titleLeftIcon
|
self.titleLeftIcon = titleLeftIcon
|
||||||
switch titleLeftIcon {
|
switch titleLeftIcon {
|
||||||
case .lock:
|
case .lock:
|
||||||
self.titleLeftIconNode.image = PresentationResourcesChat.chatTitleLockIcon(self.theme)
|
self.titleLeftIconNode.image = PresentationResourcesChat.chatTitleLockIcon(titleTheme)
|
||||||
default:
|
default:
|
||||||
self.titleLeftIconNode.image = nil
|
self.titleLeftIconNode.image = nil
|
||||||
}
|
}
|
||||||
@ -243,7 +154,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
|
|
||||||
if titleScamIcon != self.titleScamIcon {
|
if titleScamIcon != self.titleScamIcon {
|
||||||
self.titleScamIcon = titleScamIcon
|
self.titleScamIcon = titleScamIcon
|
||||||
self.titleCredibilityIconNode.image = titleScamIcon ? PresentationResourcesChatList.scamIcon(self.theme, type: .regular) : nil
|
self.titleCredibilityIconNode.image = titleScamIcon ? PresentationResourcesChatList.scamIcon(titleTheme, type: .regular) : nil
|
||||||
self.setNeedsLayout()
|
self.setNeedsLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,7 +162,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
self.titleRightIcon = titleRightIcon
|
self.titleRightIcon = titleRightIcon
|
||||||
switch titleRightIcon {
|
switch titleRightIcon {
|
||||||
case .mute:
|
case .mute:
|
||||||
self.titleRightIconNode.image = PresentationResourcesChat.chatTitleMuteIcon(self.theme)
|
self.titleRightIconNode.image = PresentationResourcesChat.chatTitleMuteIcon(titleTheme)
|
||||||
default:
|
default:
|
||||||
self.titleRightIconNode.image = nil
|
self.titleRightIconNode.image = nil
|
||||||
}
|
}
|
||||||
@ -278,6 +189,8 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let titleTheme = self.hasEmbeddedTitleContent ? defaultDarkPresentationTheme : self.theme
|
||||||
|
|
||||||
var state = ChatTitleActivityNodeState.none
|
var state = ChatTitleActivityNodeState.none
|
||||||
switch self.networkState {
|
switch self.networkState {
|
||||||
case .waitingForNetwork, .connecting, .updating:
|
case .waitingForNetwork, .connecting, .updating:
|
||||||
@ -285,14 +198,14 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
switch self.networkState {
|
switch self.networkState {
|
||||||
case .waitingForNetwork:
|
case .waitingForNetwork:
|
||||||
infoText = self.strings.ChatState_WaitingForNetwork
|
infoText = self.strings.ChatState_WaitingForNetwork
|
||||||
case let .connecting(proxy):
|
case .connecting:
|
||||||
infoText = self.strings.ChatState_Connecting
|
infoText = self.strings.ChatState_Connecting
|
||||||
case .updating:
|
case .updating:
|
||||||
infoText = self.strings.ChatState_Updating
|
infoText = self.strings.ChatState_Updating
|
||||||
case .online:
|
case .online:
|
||||||
infoText = ""
|
infoText = ""
|
||||||
}
|
}
|
||||||
state = .info(NSAttributedString(string: infoText, font: Font.regular(13.0), textColor: self.theme.rootController.navigationBar.secondaryTextColor), .generic)
|
state = .info(NSAttributedString(string: infoText, font: Font.regular(13.0), textColor: titleTheme.rootController.navigationBar.secondaryTextColor), .generic)
|
||||||
case .online:
|
case .online:
|
||||||
if let (peerId, inputActivities) = self.inputActivities, !inputActivities.isEmpty, inputActivitiesAllowed {
|
if let (peerId, inputActivities) = self.inputActivities, !inputActivities.isEmpty, inputActivitiesAllowed {
|
||||||
var stringValue = ""
|
var stringValue = ""
|
||||||
@ -336,7 +249,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let color = self.theme.rootController.navigationBar.accentTextColor
|
let color = titleTheme.rootController.navigationBar.accentTextColor
|
||||||
let string = NSAttributedString(string: stringValue, font: Font.regular(13.0), textColor: color)
|
let string = NSAttributedString(string: stringValue, font: Font.regular(13.0), textColor: color)
|
||||||
switch mergedActivity {
|
switch mergedActivity {
|
||||||
case .typingText:
|
case .typingText:
|
||||||
@ -357,21 +270,21 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
if let peer = peerViewMainPeer(peerView) {
|
if let peer = peerViewMainPeer(peerView) {
|
||||||
let servicePeer = isServicePeer(peer)
|
let servicePeer = isServicePeer(peer)
|
||||||
if peer.id == self.account.peerId || isScheduledMessages {
|
if peer.id == self.account.peerId || isScheduledMessages {
|
||||||
let string = NSAttributedString(string: "", font: Font.regular(13.0), textColor: self.theme.rootController.navigationBar.secondaryTextColor)
|
let string = NSAttributedString(string: "", font: Font.regular(13.0), textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||||
state = .info(string, .generic)
|
state = .info(string, .generic)
|
||||||
} else if let user = peer as? TelegramUser {
|
} else if let user = peer as? TelegramUser {
|
||||||
if servicePeer {
|
if servicePeer {
|
||||||
let string = NSAttributedString(string: "", font: Font.regular(13.0), textColor: self.theme.rootController.navigationBar.secondaryTextColor)
|
let string = NSAttributedString(string: "", font: Font.regular(13.0), textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||||
state = .info(string, .generic)
|
state = .info(string, .generic)
|
||||||
} else if user.flags.contains(.isSupport) {
|
} else if user.flags.contains(.isSupport) {
|
||||||
let statusText = self.strings.Bot_GenericSupportStatus
|
let statusText = self.strings.Bot_GenericSupportStatus
|
||||||
|
|
||||||
let string = NSAttributedString(string: statusText, font: Font.regular(13.0), textColor: self.theme.rootController.navigationBar.secondaryTextColor)
|
let string = NSAttributedString(string: statusText, font: Font.regular(13.0), textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||||
state = .info(string, .generic)
|
state = .info(string, .generic)
|
||||||
} else if let _ = user.botInfo {
|
} else if let _ = user.botInfo {
|
||||||
let statusText = self.strings.Bot_GenericBotStatus
|
let statusText = self.strings.Bot_GenericBotStatus
|
||||||
|
|
||||||
let string = NSAttributedString(string: statusText, font: Font.regular(13.0), textColor: self.theme.rootController.navigationBar.secondaryTextColor)
|
let string = NSAttributedString(string: statusText, font: Font.regular(13.0), textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||||
state = .info(string, .generic)
|
state = .info(string, .generic)
|
||||||
} else if let peer = peerViewMainPeer(peerView) {
|
} else if let peer = peerViewMainPeer(peerView) {
|
||||||
let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
|
let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
|
||||||
@ -383,10 +296,10 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
userPresence = TelegramUserPresence(status: .none, lastActivity: 0)
|
userPresence = TelegramUserPresence(status: .none, lastActivity: 0)
|
||||||
}
|
}
|
||||||
let (string, activity) = stringAndActivityForUserPresence(strings: self.strings, dateTimeFormat: self.dateTimeFormat, presence: userPresence, relativeTo: Int32(timestamp))
|
let (string, activity) = stringAndActivityForUserPresence(strings: self.strings, dateTimeFormat: self.dateTimeFormat, presence: userPresence, relativeTo: Int32(timestamp))
|
||||||
let attributedString = NSAttributedString(string: string, font: Font.regular(13.0), textColor: activity ? self.theme.rootController.navigationBar.accentTextColor : self.theme.rootController.navigationBar.secondaryTextColor)
|
let attributedString = NSAttributedString(string: string, font: Font.regular(13.0), textColor: activity ? titleTheme.rootController.navigationBar.accentTextColor : titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||||
state = .info(attributedString, activity ? .online : .lastSeenTime)
|
state = .info(attributedString, activity ? .online : .lastSeenTime)
|
||||||
} else {
|
} else {
|
||||||
let string = NSAttributedString(string: "", font: Font.regular(13.0), textColor: self.theme.rootController.navigationBar.secondaryTextColor)
|
let string = NSAttributedString(string: "", font: Font.regular(13.0), textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||||
state = .info(string, .generic)
|
state = .info(string, .generic)
|
||||||
}
|
}
|
||||||
} else if let group = peer as? TelegramGroup {
|
} else if let group = peer as? TelegramGroup {
|
||||||
@ -408,11 +321,11 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
if onlineCount > 1 {
|
if onlineCount > 1 {
|
||||||
let string = NSMutableAttributedString()
|
let string = NSMutableAttributedString()
|
||||||
|
|
||||||
string.append(NSAttributedString(string: "\(strings.Conversation_StatusMembers(Int32(group.participantCount))), ", font: Font.regular(13.0), textColor: self.theme.rootController.navigationBar.secondaryTextColor))
|
string.append(NSAttributedString(string: "\(strings.Conversation_StatusMembers(Int32(group.participantCount))), ", font: Font.regular(13.0), textColor: titleTheme.rootController.navigationBar.secondaryTextColor))
|
||||||
string.append(NSAttributedString(string: strings.Conversation_StatusOnline(Int32(onlineCount)), font: Font.regular(13.0), textColor: self.theme.rootController.navigationBar.secondaryTextColor))
|
string.append(NSAttributedString(string: strings.Conversation_StatusOnline(Int32(onlineCount)), font: Font.regular(13.0), textColor: titleTheme.rootController.navigationBar.secondaryTextColor))
|
||||||
state = .info(string, .generic)
|
state = .info(string, .generic)
|
||||||
} else {
|
} else {
|
||||||
let string = NSAttributedString(string: strings.Conversation_StatusMembers(Int32(group.participantCount)), font: Font.regular(13.0), textColor: self.theme.rootController.navigationBar.secondaryTextColor)
|
let string = NSAttributedString(string: strings.Conversation_StatusMembers(Int32(group.participantCount)), font: Font.regular(13.0), textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||||
state = .info(string, .generic)
|
state = .info(string, .generic)
|
||||||
}
|
}
|
||||||
} else if let channel = peer as? TelegramChannel {
|
} else if let channel = peer as? TelegramChannel {
|
||||||
@ -420,17 +333,17 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
if memberCount == 0 {
|
if memberCount == 0 {
|
||||||
let string: NSAttributedString
|
let string: NSAttributedString
|
||||||
if case .group = channel.info {
|
if case .group = channel.info {
|
||||||
string = NSAttributedString(string: strings.Group_Status, font: Font.regular(13.0), textColor: self.theme.rootController.navigationBar.secondaryTextColor)
|
string = NSAttributedString(string: strings.Group_Status, font: Font.regular(13.0), textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||||
} else {
|
} else {
|
||||||
string = NSAttributedString(string: strings.Channel_Status, font: Font.regular(13.0), textColor: self.theme.rootController.navigationBar.secondaryTextColor)
|
string = NSAttributedString(string: strings.Channel_Status, font: Font.regular(13.0), textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||||
}
|
}
|
||||||
state = .info(string, .generic)
|
state = .info(string, .generic)
|
||||||
} else {
|
} else {
|
||||||
if case .group = channel.info, let onlineMemberCount = onlineMemberCount, onlineMemberCount > 1 {
|
if case .group = channel.info, let onlineMemberCount = onlineMemberCount, onlineMemberCount > 1 {
|
||||||
let string = NSMutableAttributedString()
|
let string = NSMutableAttributedString()
|
||||||
|
|
||||||
string.append(NSAttributedString(string: "\(strings.Conversation_StatusMembers(Int32(memberCount))), ", font: Font.regular(13.0), textColor: self.theme.rootController.navigationBar.secondaryTextColor))
|
string.append(NSAttributedString(string: "\(strings.Conversation_StatusMembers(Int32(memberCount))), ", font: Font.regular(13.0), textColor: titleTheme.rootController.navigationBar.secondaryTextColor))
|
||||||
string.append(NSAttributedString(string: strings.Conversation_StatusOnline(Int32(onlineMemberCount)), font: Font.regular(13.0), textColor: self.theme.rootController.navigationBar.secondaryTextColor))
|
string.append(NSAttributedString(string: strings.Conversation_StatusOnline(Int32(onlineMemberCount)), font: Font.regular(13.0), textColor: titleTheme.rootController.navigationBar.secondaryTextColor))
|
||||||
state = .info(string, .generic)
|
state = .info(string, .generic)
|
||||||
} else {
|
} else {
|
||||||
let membersString: String
|
let membersString: String
|
||||||
@ -439,17 +352,17 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
} else {
|
} else {
|
||||||
membersString = strings.Conversation_StatusSubscribers(memberCount)
|
membersString = strings.Conversation_StatusSubscribers(memberCount)
|
||||||
}
|
}
|
||||||
let string = NSAttributedString(string: membersString, font: Font.regular(13.0), textColor: self.theme.rootController.navigationBar.secondaryTextColor)
|
let string = NSAttributedString(string: membersString, font: Font.regular(13.0), textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||||
state = .info(string, .generic)
|
state = .info(string, .generic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch channel.info {
|
switch channel.info {
|
||||||
case .group:
|
case .group:
|
||||||
let string = NSAttributedString(string: strings.Group_Status, font: Font.regular(13.0), textColor: self.theme.rootController.navigationBar.secondaryTextColor)
|
let string = NSAttributedString(string: strings.Group_Status, font: Font.regular(13.0), textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||||
state = .info(string, .generic)
|
state = .info(string, .generic)
|
||||||
case .broadcast:
|
case .broadcast:
|
||||||
let string = NSAttributedString(string: strings.Channel_Status, font: Font.regular(13.0), textColor: self.theme.rootController.navigationBar.secondaryTextColor)
|
let string = NSAttributedString(string: strings.Channel_Status, font: Font.regular(13.0), textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||||
state = .info(string, .generic)
|
state = .info(string, .generic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -551,11 +464,11 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) {
|
func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, hasEmbeddedTitleContent: Bool) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
|
self.hasEmbeddedTitleContent = hasEmbeddedTitleContent
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
|
|
||||||
//self.networkStatusNode?.updateTheme(theme: theme)
|
|
||||||
let titleContent = self.titleContent
|
let titleContent = self.titleContent
|
||||||
self.titleContent = titleContent
|
self.titleContent = titleContent
|
||||||
self.updateStatus()
|
self.updateStatus()
|
||||||
@ -568,8 +481,6 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition) {
|
func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition) {
|
||||||
self.validLayout = (size, clearBounds)
|
self.validLayout = (size, clearBounds)
|
||||||
|
|
||||||
let transition: ContainedViewLayoutTransition = .immediate
|
|
||||||
|
|
||||||
self.button.frame = clearBounds
|
self.button.frame = clearBounds
|
||||||
self.contentContainer.frame = clearBounds
|
self.contentContainer.frame = clearBounds
|
||||||
|
|
||||||
|
@ -11,6 +11,9 @@ public final class OverlayMediaControllerImpl: ViewController, OverlayMediaContr
|
|||||||
return self.displayNode as! OverlayMediaControllerNode
|
return self.displayNode as! OverlayMediaControllerNode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var updatePossibleEmbeddingItem: ((OverlayMediaControllerEmbeddingItem?) -> Void)?
|
||||||
|
public var embedPossibleEmbeddingItem: ((OverlayMediaControllerEmbeddingItem) -> Bool)?
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
super.init(navigationBarPresentationData: nil)
|
super.init(navigationBarPresentationData: nil)
|
||||||
|
|
||||||
@ -22,7 +25,11 @@ public final class OverlayMediaControllerImpl: ViewController, OverlayMediaContr
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public func loadDisplayNode() {
|
override public func loadDisplayNode() {
|
||||||
self.displayNode = OverlayMediaControllerNode()
|
self.displayNode = OverlayMediaControllerNode(updatePossibleEmbeddingItem: { [weak self] item in
|
||||||
|
self?.updatePossibleEmbeddingItem?(item)
|
||||||
|
}, embedPossibleEmbeddingItem: { [weak self] item in
|
||||||
|
return self?.embedPossibleEmbeddingItem?(item) ?? false
|
||||||
|
})
|
||||||
self.displayNodeDidLoad()
|
self.displayNodeDidLoad()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,12 @@ private final class OverlayMediaVideoNodeData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
final class OverlayMediaControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
final class OverlayMediaControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||||
|
private let updatePossibleEmbeddingItem: (OverlayMediaControllerEmbeddingItem?) -> Void
|
||||||
|
private let embedPossibleEmbeddingItem: (OverlayMediaControllerEmbeddingItem) -> Bool
|
||||||
|
|
||||||
private var videoNodes: [OverlayMediaVideoNodeData] = []
|
private var videoNodes: [OverlayMediaVideoNodeData] = []
|
||||||
private var validLayout: ContainerViewLayout?
|
private var validLayout: ContainerViewLayout?
|
||||||
|
|
||||||
@ -40,7 +45,10 @@ final class OverlayMediaControllerNode: ASDisplayNode, UIGestureRecognizerDelega
|
|||||||
private var pinchingNode: OverlayMediaItemNode?
|
private var pinchingNode: OverlayMediaItemNode?
|
||||||
private var pinchingNodeInitialSize: CGSize?
|
private var pinchingNodeInitialSize: CGSize?
|
||||||
|
|
||||||
override init() {
|
init(updatePossibleEmbeddingItem: @escaping (OverlayMediaControllerEmbeddingItem?) -> Void, embedPossibleEmbeddingItem: @escaping (OverlayMediaControllerEmbeddingItem) -> Bool) {
|
||||||
|
self.updatePossibleEmbeddingItem = updatePossibleEmbeddingItem
|
||||||
|
self.embedPossibleEmbeddingItem = embedPossibleEmbeddingItem
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.setViewBlock({
|
self.setViewBlock({
|
||||||
@ -329,34 +337,46 @@ final class OverlayMediaControllerNode: ASDisplayNode, UIGestureRecognizerDelega
|
|||||||
draggingNode.updateMinimizedEdge(nil, adjusting: true)
|
draggingNode.updateMinimizedEdge(nil, adjusting: true)
|
||||||
}
|
}
|
||||||
draggingNode.frame = nodeFrame
|
draggingNode.frame = nodeFrame
|
||||||
|
self.updatePossibleEmbeddingItem(OverlayMediaControllerEmbeddingItem(
|
||||||
|
position: nodeFrame.center,
|
||||||
|
itemNode: draggingNode
|
||||||
|
))
|
||||||
}
|
}
|
||||||
case .ended, .cancelled:
|
case .ended, .cancelled:
|
||||||
if let draggingNode = self.draggingNode, let validLayout = self.validLayout, let index = self.videoNodes.firstIndex(where: { $0.node === draggingNode }){
|
if let draggingNode = self.draggingNode, let validLayout = self.validLayout, let index = self.videoNodes.firstIndex(where: { $0.node === draggingNode }){
|
||||||
let nodeSize = self.videoNodes[index].currentSize
|
let nodeSize = self.videoNodes[index].currentSize
|
||||||
let previousFrame = draggingNode.frame
|
let previousFrame = draggingNode.frame
|
||||||
|
|
||||||
let (updatedLocation, shouldDismiss) = self.nodeLocationForPosition(layout: validLayout, position: CGPoint(x: previousFrame.midX, y: previousFrame.midY), velocity: recognizer.velocity(in: self.view), size: nodeSize, tempExtendedTopInset: draggingNode.tempExtendedTopInset)
|
if self.embedPossibleEmbeddingItem(OverlayMediaControllerEmbeddingItem(
|
||||||
|
position: previousFrame.center,
|
||||||
if shouldDismiss && draggingNode.isMinimizeable {
|
itemNode: draggingNode
|
||||||
draggingNode.updateMinimizedEdge(updatedLocation.x.isZero ? .left : .right, adjusting: false)
|
)) {
|
||||||
self.videoNodes[index].isMinimized = true
|
self.draggingNode = nil
|
||||||
} else {
|
} else {
|
||||||
draggingNode.updateMinimizedEdge(nil, adjusting: true)
|
let (updatedLocation, shouldDismiss) = self.nodeLocationForPosition(layout: validLayout, position: CGPoint(x: previousFrame.midX, y: previousFrame.midY), velocity: recognizer.velocity(in: self.view), size: nodeSize, tempExtendedTopInset: draggingNode.tempExtendedTopInset)
|
||||||
self.videoNodes[index].isMinimized = false
|
|
||||||
}
|
if shouldDismiss && draggingNode.isMinimizeable {
|
||||||
|
draggingNode.updateMinimizedEdge(updatedLocation.x.isZero ? .left : .right, adjusting: false)
|
||||||
if let group = draggingNode.group {
|
self.videoNodes[index].isMinimized = true
|
||||||
self.locationByGroup[group] = updatedLocation
|
} else {
|
||||||
}
|
draggingNode.updateMinimizedEdge(nil, adjusting: true)
|
||||||
self.videoNodes[index].location = updatedLocation
|
self.videoNodes[index].isMinimized = false
|
||||||
|
}
|
||||||
draggingNode.frame = CGRect(origin: self.nodePosition(layout: validLayout, size: nodeSize, location: updatedLocation, hidden: !draggingNode.hasAttachedContext, isMinimized: self.videoNodes[index].isMinimized, tempExtendedTopInset: draggingNode.tempExtendedTopInset), size: nodeSize)
|
|
||||||
draggingNode.layer.animateFrame(from: previousFrame, to: draggingNode.frame, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
if let group = draggingNode.group {
|
||||||
self.draggingNode = nil
|
self.locationByGroup[group] = updatedLocation
|
||||||
|
}
|
||||||
if shouldDismiss && !draggingNode.isMinimizeable {
|
self.videoNodes[index].location = updatedLocation
|
||||||
draggingNode.dismiss()
|
|
||||||
|
draggingNode.frame = CGRect(origin: self.nodePosition(layout: validLayout, size: nodeSize, location: updatedLocation, hidden: !draggingNode.hasAttachedContext, isMinimized: self.videoNodes[index].isMinimized, tempExtendedTopInset: draggingNode.tempExtendedTopInset), size: nodeSize)
|
||||||
|
draggingNode.layer.animateFrame(from: previousFrame, to: draggingNode.frame, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
self.draggingNode = nil
|
||||||
|
|
||||||
|
if shouldDismiss && !draggingNode.isMinimizeable {
|
||||||
|
draggingNode.dismiss()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
self.updatePossibleEmbeddingItem(nil)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
|
@ -2281,14 +2281,14 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
if buttonKeys.count > 3 {
|
if buttonKeys.count > 3 {
|
||||||
if self.isOpenedFromChat {
|
if self.isOpenedFromChat {
|
||||||
switch buttonKey {
|
switch buttonKey {
|
||||||
case .message, .search:
|
case .message, .search, .videoCall:
|
||||||
hiddenWhileExpanded = true
|
hiddenWhileExpanded = true
|
||||||
default:
|
default:
|
||||||
hiddenWhileExpanded = false
|
hiddenWhileExpanded = false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch buttonKey {
|
switch buttonKey {
|
||||||
case .mute, .search:
|
case .mute, .search, .videoCall:
|
||||||
hiddenWhileExpanded = true
|
hiddenWhileExpanded = true
|
||||||
default:
|
default:
|
||||||
hiddenWhileExpanded = false
|
hiddenWhileExpanded = false
|
||||||
|
@ -207,6 +207,43 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
|
|
||||||
self.mediaManager = MediaManagerImpl(accountManager: accountManager, inForeground: applicationBindings.applicationInForeground, presentationData: presentationData)
|
self.mediaManager = MediaManagerImpl(accountManager: accountManager, inForeground: applicationBindings.applicationInForeground, presentationData: presentationData)
|
||||||
|
|
||||||
|
self.mediaManager.overlayMediaManager.updatePossibleEmbeddingItem = { [weak self] item in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let navigationController = strongSelf.mainWindow?.viewController as? NavigationController else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var content: NavigationControllerDropContent?
|
||||||
|
if let item = item {
|
||||||
|
content = NavigationControllerDropContent(
|
||||||
|
position: item.position,
|
||||||
|
item: VideoNavigationControllerDropContentItem(
|
||||||
|
itemNode: item.itemNode
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
navigationController.updatePossibleControllerDropContent(content: content)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.mediaManager.overlayMediaManager.embedPossibleEmbeddingItem = { [weak self] item in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
guard let navigationController = strongSelf.mainWindow?.viewController as? NavigationController else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let content = NavigationControllerDropContent(
|
||||||
|
position: item.position,
|
||||||
|
item: VideoNavigationControllerDropContentItem(
|
||||||
|
itemNode: item.itemNode
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return navigationController.acceptPossibleControllerDropContent(content: content)
|
||||||
|
}
|
||||||
|
|
||||||
self._autodownloadSettings.set(.single(initialPresentationDataAndSettings.autodownloadSettings)
|
self._autodownloadSettings.set(.single(initialPresentationDataAndSettings.autodownloadSettings)
|
||||||
|> then(accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings])
|
|> then(accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings])
|
||||||
|> map { sharedData in
|
|> map { sharedData in
|
||||||
|
@ -10,7 +10,7 @@ import TelegramAudio
|
|||||||
import AccountContext
|
import AccountContext
|
||||||
|
|
||||||
public final class OverlayUniversalVideoNode: OverlayMediaItemNode {
|
public final class OverlayUniversalVideoNode: OverlayMediaItemNode {
|
||||||
private let content: UniversalVideoContent
|
public let content: UniversalVideoContent
|
||||||
private let videoNode: UniversalVideoNode
|
private let videoNode: UniversalVideoNode
|
||||||
private let decoration: OverlayVideoDecoration
|
private let decoration: OverlayVideoDecoration
|
||||||
|
|
||||||
@ -30,8 +30,16 @@ public final class OverlayUniversalVideoNode: OverlayMediaItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private let defaultExpand: () -> Void
|
||||||
|
public var customExpand: (() -> Void)?
|
||||||
|
public var customClose: (() -> Void)?
|
||||||
|
|
||||||
public init(postbox: Postbox, audioSession: ManagedAudioSession, manager: UniversalVideoManager, content: UniversalVideoContent, expand: @escaping () -> Void, close: @escaping () -> Void) {
|
public init(postbox: Postbox, audioSession: ManagedAudioSession, manager: UniversalVideoManager, content: UniversalVideoContent, expand: @escaping () -> Void, close: @escaping () -> Void) {
|
||||||
self.content = content
|
self.content = content
|
||||||
|
self.defaultExpand = expand
|
||||||
|
|
||||||
|
var expandImpl: (() -> Void)?
|
||||||
|
|
||||||
var unminimizeImpl: (() -> Void)?
|
var unminimizeImpl: (() -> Void)?
|
||||||
var togglePlayPauseImpl: (() -> Void)?
|
var togglePlayPauseImpl: (() -> Void)?
|
||||||
var closeImpl: (() -> Void)?
|
var closeImpl: (() -> Void)?
|
||||||
@ -40,7 +48,7 @@ public final class OverlayUniversalVideoNode: OverlayMediaItemNode {
|
|||||||
}, togglePlayPause: {
|
}, togglePlayPause: {
|
||||||
togglePlayPauseImpl?()
|
togglePlayPauseImpl?()
|
||||||
}, expand: {
|
}, expand: {
|
||||||
expand()
|
expandImpl?()
|
||||||
}, close: {
|
}, close: {
|
||||||
closeImpl?()
|
closeImpl?()
|
||||||
})
|
})
|
||||||
@ -49,6 +57,17 @@ public final class OverlayUniversalVideoNode: OverlayMediaItemNode {
|
|||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
expandImpl = { [weak self] in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let customExpand = strongSelf.customExpand {
|
||||||
|
customExpand()
|
||||||
|
} else {
|
||||||
|
strongSelf.defaultExpand()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unminimizeImpl = { [weak self] in
|
unminimizeImpl = { [weak self] in
|
||||||
self?.unminimize?()
|
self?.unminimize?()
|
||||||
}
|
}
|
||||||
@ -57,6 +76,10 @@ public final class OverlayUniversalVideoNode: OverlayMediaItemNode {
|
|||||||
}
|
}
|
||||||
closeImpl = { [weak self] in
|
closeImpl = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
|
if let customClose = strongSelf.customClose {
|
||||||
|
customClose()
|
||||||
|
return
|
||||||
|
}
|
||||||
if strongSelf.videoNode.hasAttachedContext {
|
if strongSelf.videoNode.hasAttachedContext {
|
||||||
strongSelf.videoNode.continuePlayingWithoutSound()
|
strongSelf.videoNode.continuePlayingWithoutSound()
|
||||||
}
|
}
|
||||||
@ -104,18 +127,32 @@ public final class OverlayUniversalVideoNode: OverlayMediaItemNode {
|
|||||||
|
|
||||||
override public func updateLayout(_ size: CGSize) {
|
override public func updateLayout(_ size: CGSize) {
|
||||||
if size != self.validLayoutSize {
|
if size != self.validLayoutSize {
|
||||||
self.updateLayoutImpl(size)
|
self.updateLayoutImpl(size, transition: .immediate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateLayoutImpl(_ size: CGSize) {
|
public func updateLayout(_ size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||||
|
if size != self.validLayoutSize {
|
||||||
|
self.updateLayoutImpl(size, transition: transition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateLayoutImpl(_ size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||||
self.validLayoutSize = size
|
self.validLayoutSize = size
|
||||||
|
|
||||||
self.videoNode.frame = CGRect(origin: CGPoint(), size: size)
|
transition.updateFrame(node: self.videoNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
self.videoNode.updateLayout(size: size, transition: .immediate)
|
self.videoNode.updateLayout(size: size, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func updateMinimizedEdge(_ edge: OverlayMediaItemMinimizationEdge?, adjusting: Bool) {
|
override public func updateMinimizedEdge(_ edge: OverlayMediaItemMinimizationEdge?, adjusting: Bool) {
|
||||||
self.decoration.updateMinimizedEdge(edge, adjusting: adjusting)
|
self.decoration.updateMinimizedEdge(edge, adjusting: adjusting)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func updateRoundCorners(_ value: Bool, transition: ContainedViewLayoutTransition) {
|
||||||
|
transition.updateCornerRadius(node: self, cornerRadius: value ? 4.0 : 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func showControls() {
|
||||||
|
self.decoration.showControls()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,6 +148,13 @@ final class OverlayVideoDecoration: UniversalVideoDecoration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func showControls() {
|
||||||
|
if self.controlsNode.alpha.isZero {
|
||||||
|
self.controlsNode.alpha = 1.0
|
||||||
|
self.controlsNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func setStatus(_ status: Signal<MediaPlayerStatus?, NoError>) {
|
func setStatus(_ status: Signal<MediaPlayerStatus?, NoError>) {
|
||||||
self.controlsNode.status = status |> map { value -> MediaPlayerStatus in
|
self.controlsNode.status = status |> map { value -> MediaPlayerStatus in
|
||||||
if let value = value {
|
if let value = value {
|
||||||
|
@ -109,12 +109,12 @@ final class PictureInPictureVideoControlsNode: ASDisplayNode {
|
|||||||
|
|
||||||
let buttonSize = TGEmbedPIPButtonSize
|
let buttonSize = TGEmbedPIPButtonSize
|
||||||
|
|
||||||
self.leaveButton.frame = CGRect(origin: CGPoint(x: forth - floor(buttonSize.width / 2.0) - 10.0, y: size.height - buttonSize.height - 15.0), size: buttonSize)
|
transition.updateFrame(view: self.leaveButton, frame: CGRect(origin: CGPoint(x: forth - floor(buttonSize.width / 2.0) - 10.0, y: size.height - buttonSize.height - 15.0), size: buttonSize))
|
||||||
|
|
||||||
self.pauseButton.frame = CGRect(origin: CGPoint(x: floor((size.width - buttonSize.width) / 2.0), y: size.height - buttonSize.height - 15.0), size: buttonSize)
|
transition.updateFrame(view: self.pauseButton, frame: CGRect(origin: CGPoint(x: floor((size.width - buttonSize.width) / 2.0), y: size.height - buttonSize.height - 15.0), size: buttonSize))
|
||||||
self.playButton.frame = CGRect(origin: CGPoint(x: floor((size.width - buttonSize.width) / 2.0), y: size.height - buttonSize.height - 15.0), size: buttonSize)
|
transition.updateFrame(view: self.playButton, frame: CGRect(origin: CGPoint(x: floor((size.width - buttonSize.width) / 2.0), y: size.height - buttonSize.height - 15.0), size: buttonSize))
|
||||||
|
|
||||||
self.closeButton.frame = CGRect(origin: CGPoint(x: self.playButton.frame.origin.x + forth + 10.0, y: size.height - buttonSize.height - 15.0), size: buttonSize)
|
transition.updateFrame(view: self.closeButton, frame: CGRect(origin: CGPoint(x: self.playButton.frame.origin.x + forth + 10.0, y: size.height - buttonSize.height - 15.0), size: buttonSize))
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func leavePressed() {
|
@objc func leavePressed() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user