mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-01 16:06:59 +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 UIKit
|
||||
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 {
|
||||
var updatePossibleEmbeddingItem: ((OverlayMediaControllerEmbeddingItem?) -> Void)? { get set }
|
||||
var embedPossibleEmbeddingItem: ((OverlayMediaControllerEmbeddingItem) -> Bool)? { get set }
|
||||
|
||||
var hasNodes: Bool { get }
|
||||
func addNode(_ node: OverlayMediaItemNode, customTransition: Bool)
|
||||
func removeNode(_ node: OverlayMediaItemNode, customTransition: Bool)
|
||||
@ -10,10 +27,21 @@ public protocol OverlayMediaController: class {
|
||||
public final class OverlayMediaManager {
|
||||
public var controller: (OverlayMediaController & ViewController)?
|
||||
|
||||
public var updatePossibleEmbeddingItem: ((OverlayMediaControllerEmbeddingItem?) -> Void)?
|
||||
public var embedPossibleEmbeddingItem: ((OverlayMediaControllerEmbeddingItem) -> Bool)?
|
||||
|
||||
public init() {
|
||||
}
|
||||
|
||||
public func attachOverlayMediaController(_ controller: OverlayMediaController & ViewController) {
|
||||
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 {
|
||||
public var isOpaqueWhenInOverlay: 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) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ public final class NavigationBarBadgeNode: ASDisplayNode {
|
||||
self.textColor = textColor
|
||||
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.redrawIfPossible()
|
||||
}
|
||||
|
||||
override public func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
||||
|
@ -644,6 +644,13 @@ public enum TabBarItemContextActionType {
|
||||
|
||||
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 {
|
||||
|
@ -1287,7 +1287,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
mediaManager?.setOverlayVideoNode(nil)
|
||||
})
|
||||
expandImpl = { [weak overlayNode] in
|
||||
guard let contentInfo = item.contentInfo else {
|
||||
guard let contentInfo = item.contentInfo, let overlayNode = overlayNode else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -1302,7 +1302,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
|
||||
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 {
|
||||
return GalleryTransitionArguments(transitionNode: (overlayNode, overlayNode.bounds, { [weak overlayNode] in
|
||||
return (overlayNode?.view.snapshotContentTree(), nil)
|
||||
|
@ -42,6 +42,7 @@ private enum DebugControllerSection: Int32 {
|
||||
case logs
|
||||
case logging
|
||||
case experiments
|
||||
case videoExperiments
|
||||
case info
|
||||
}
|
||||
|
||||
@ -70,6 +71,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
case knockoutWallpaper(PresentationTheme, Bool)
|
||||
case alternativeFolderTabs(Bool)
|
||||
case videoCalls(Bool)
|
||||
case videoCallsInfo(PresentationTheme, String)
|
||||
case hostInfo(PresentationTheme, String)
|
||||
case versionInfo(PresentationTheme)
|
||||
|
||||
@ -83,8 +85,10 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return DebugControllerSection.logging.rawValue
|
||||
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
|
||||
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
|
||||
case .videoCalls, .videoCallsInfo:
|
||||
return DebugControllerSection.videoExperiments.rawValue
|
||||
case .hostInfo, .versionInfo:
|
||||
return DebugControllerSection.info.rawValue
|
||||
}
|
||||
@ -140,10 +144,12 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return 23
|
||||
case .videoCalls:
|
||||
return 24
|
||||
case .hostInfo:
|
||||
case .videoCallsInfo:
|
||||
return 25
|
||||
case .versionInfo:
|
||||
case .hostInfo:
|
||||
return 26
|
||||
case .versionInfo:
|
||||
return 27
|
||||
}
|
||||
}
|
||||
|
||||
@ -542,7 +548,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
}).start()
|
||||
})
|
||||
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
|
||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
||||
var settings = settings as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings
|
||||
@ -551,6 +557,8 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
})
|
||||
}).start()
|
||||
})
|
||||
case let .videoCallsInfo(_, text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||
case let .hostInfo(theme, string):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(string), sectionId: self.section)
|
||||
case let .versionInfo(theme):
|
||||
@ -595,6 +603,7 @@ private func debugControllerEntries(presentationData: PresentationData, loggingS
|
||||
entries.append(.knockoutWallpaper(presentationData.theme, experimentalSettings.knockoutWallpaper))
|
||||
entries.append(.alternativeFolderTabs(experimentalSettings.foldersTabAtBottom))
|
||||
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 {
|
||||
entries.append(.hostInfo(presentationData.theme, "Host: \(backupHostOverride)"))
|
||||
|
@ -45,9 +45,9 @@ public extension TabBarControllerTheme {
|
||||
}
|
||||
|
||||
public extension NavigationBarTheme {
|
||||
convenience init(rootControllerTheme: PresentationTheme) {
|
||||
convenience init(rootControllerTheme: PresentationTheme, hideBackground: Bool = false) {
|
||||
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))
|
||||
}
|
||||
|
||||
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) {
|
||||
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 peekTimerDisposable = MetaDisposable()
|
||||
|
||||
private var hasEmbeddedTitleContent = false
|
||||
|
||||
public override var customData: Any? {
|
||||
return self.chatLocation
|
||||
@ -373,7 +375,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
case .inline:
|
||||
navigationBarPresentationData = nil
|
||||
default:
|
||||
navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData)
|
||||
navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData, hideBackground: true)
|
||||
}
|
||||
super.init(context: context, navigationBarPresentationData: navigationBarPresentationData, mediaAccessoryPanelVisibility: mediaAccessoryPanelVisibility, locationBroadcastPanelSource: locationBroadcastPanelSource)
|
||||
|
||||
@ -2779,8 +2781,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
case .inline:
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
}
|
||||
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))
|
||||
self.chatTitleView?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings)
|
||||
self.updateNavigationBarPresentation()
|
||||
self.updateChatPresentationInterfaceState(animated: false, interactive: false, { state in
|
||||
var state = state
|
||||
state = state.updatedTheme(self.presentationData.theme)
|
||||
@ -2794,6 +2795,20 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
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() {
|
||||
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
|
||||
|
||||
if let search = self.focusOnSearchAfterAppearance {
|
||||
@ -5143,7 +5184,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
switch self.presentationInterfaceState.mode {
|
||||
case .standard, .inline:
|
||||
break
|
||||
break
|
||||
case .overlay:
|
||||
if case .Ignore = self.statusBar.statusBarStyle {
|
||||
} else if layout.safeInsets.top.isZero {
|
||||
@ -5503,7 +5544,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
switch updatedChatPresentationInterfaceState.mode {
|
||||
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 = []
|
||||
case .overlay:
|
||||
self.deferScreenEdgeGestures = [.top]
|
||||
@ -9256,6 +9301,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.focusOnSearchAfterAppearance = (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 {
|
||||
|
@ -12,6 +12,15 @@ import TextFormat
|
||||
import AccountContext
|
||||
import TelegramNotices
|
||||
import ReactionSelectionNode
|
||||
import TelegramUniversalVideoContent
|
||||
|
||||
final class VideoNavigationControllerDropContentItem: NavigationControllerDropContentItem {
|
||||
let itemNode: OverlayMediaItemNode
|
||||
|
||||
init(itemNode: OverlayMediaItemNode) {
|
||||
self.itemNode = itemNode
|
||||
}
|
||||
}
|
||||
|
||||
private final class ChatControllerNodeView: UITracingLayerView, WindowInputAccessoryHeightProvider, PreviewingHostView {
|
||||
var inputAccessoryHeight: (() -> CGFloat)?
|
||||
@ -56,6 +65,175 @@ private struct ChatControllerNodeDerivedLayoutState {
|
||||
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 {
|
||||
let context: AccountContext
|
||||
let chatLocation: ChatLocation
|
||||
@ -63,6 +241,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
private weak var controller: ChatControllerImpl?
|
||||
|
||||
let navigationBar: NavigationBar?
|
||||
private let navigationBarBackroundNode: ASDisplayNode
|
||||
private let navigationBarSeparatorNode: ASDisplayNode
|
||||
|
||||
private var backgroundEffectNode: ASDisplayNode?
|
||||
private var containerBackgroundNode: ASImageNode?
|
||||
@ -204,6 +384,10 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
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?) {
|
||||
self.context = context
|
||||
self.chatLocation = chatLocation
|
||||
@ -248,6 +432,12 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.navigateButtons = ChatHistoryNavigationButtons(theme: self.chatPresentationInterfaceState.theme)
|
||||
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()
|
||||
|
||||
self.controller?.presentationContext.topLevelSubview = { [weak self] in
|
||||
@ -329,6 +519,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
self.addSubnode(self.navigateButtons)
|
||||
|
||||
self.addSubnode(self.navigationBarBackroundNode)
|
||||
self.addSubnode(self.navigationBarSeparatorNode)
|
||||
|
||||
self.historyNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
|
||||
|
||||
self.textInputPanelNode = ChatTextInputPanelNode(presentationInterfaceState: chatPresentationInterfaceState, presentController: { [weak self] controller in
|
||||
@ -696,12 +889,63 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
} else {
|
||||
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 {
|
||||
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()
|
||||
if case .overlay = self.chatPresentationInterfaceState.mode {
|
||||
let containerWidth = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: 8.0 + layout.safeInsets.left)
|
||||
@ -1518,6 +1762,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
self.updatePlainInputSeparator(transition: .immediate)
|
||||
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
|
||||
@ -2384,4 +2631,58 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
func animateQuizCorrectOptionSelected() {
|
||||
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)
|
||||
}
|
||||
|
||||
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 {
|
||||
case none
|
||||
case lock
|
||||
@ -88,6 +33,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
private let account: Account
|
||||
|
||||
private var theme: PresentationTheme
|
||||
private var hasEmbeddedTitleContent: Bool = false
|
||||
private var strings: PresentationStrings
|
||||
private var dateTimeFormat: PresentationDateTimeFormat
|
||||
private var nameDisplayOrder: PresentationPersonNameOrder
|
||||
@ -120,43 +66,6 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
@ -183,6 +92,8 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
var titleContent: ChatTitleContent? {
|
||||
didSet {
|
||||
if let titleContent = self.titleContent {
|
||||
let titleTheme = self.hasEmbeddedTitleContent ? defaultDarkPresentationTheme : self.theme
|
||||
|
||||
var string: NSAttributedString?
|
||||
var titleLeftIcon: ChatTitleIcon = .none
|
||||
var titleRightIcon: ChatTitleIcon = .none
|
||||
@ -192,20 +103,20 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
case let .peer(peerView, _, isScheduledMessages):
|
||||
if isScheduledMessages {
|
||||
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 {
|
||||
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
|
||||
} else {
|
||||
if let peer = peerViewMainPeer(peerView) {
|
||||
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 {
|
||||
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 {
|
||||
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
|
||||
@ -220,9 +131,9 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
}
|
||||
}
|
||||
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):
|
||||
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) {
|
||||
@ -234,7 +145,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
self.titleLeftIcon = titleLeftIcon
|
||||
switch titleLeftIcon {
|
||||
case .lock:
|
||||
self.titleLeftIconNode.image = PresentationResourcesChat.chatTitleLockIcon(self.theme)
|
||||
self.titleLeftIconNode.image = PresentationResourcesChat.chatTitleLockIcon(titleTheme)
|
||||
default:
|
||||
self.titleLeftIconNode.image = nil
|
||||
}
|
||||
@ -243,7 +154,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
|
||||
if titleScamIcon != self.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()
|
||||
}
|
||||
|
||||
@ -251,7 +162,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
self.titleRightIcon = titleRightIcon
|
||||
switch titleRightIcon {
|
||||
case .mute:
|
||||
self.titleRightIconNode.image = PresentationResourcesChat.chatTitleMuteIcon(self.theme)
|
||||
self.titleRightIconNode.image = PresentationResourcesChat.chatTitleMuteIcon(titleTheme)
|
||||
default:
|
||||
self.titleRightIconNode.image = nil
|
||||
}
|
||||
@ -278,6 +189,8 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
}
|
||||
}
|
||||
|
||||
let titleTheme = self.hasEmbeddedTitleContent ? defaultDarkPresentationTheme : self.theme
|
||||
|
||||
var state = ChatTitleActivityNodeState.none
|
||||
switch self.networkState {
|
||||
case .waitingForNetwork, .connecting, .updating:
|
||||
@ -285,14 +198,14 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
switch self.networkState {
|
||||
case .waitingForNetwork:
|
||||
infoText = self.strings.ChatState_WaitingForNetwork
|
||||
case let .connecting(proxy):
|
||||
case .connecting:
|
||||
infoText = self.strings.ChatState_Connecting
|
||||
case .updating:
|
||||
infoText = self.strings.ChatState_Updating
|
||||
case .online:
|
||||
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:
|
||||
if let (peerId, inputActivities) = self.inputActivities, !inputActivities.isEmpty, inputActivitiesAllowed {
|
||||
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)
|
||||
switch mergedActivity {
|
||||
case .typingText:
|
||||
@ -357,21 +270,21 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
if let peer = peerViewMainPeer(peerView) {
|
||||
let servicePeer = isServicePeer(peer)
|
||||
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)
|
||||
} else if let user = peer as? TelegramUser {
|
||||
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)
|
||||
} else if user.flags.contains(.isSupport) {
|
||||
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)
|
||||
} else if let _ = user.botInfo {
|
||||
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)
|
||||
} else if let peer = peerViewMainPeer(peerView) {
|
||||
let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
|
||||
@ -383,10 +296,10 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
userPresence = TelegramUserPresence(status: .none, lastActivity: 0)
|
||||
}
|
||||
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)
|
||||
} 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)
|
||||
}
|
||||
} else if let group = peer as? TelegramGroup {
|
||||
@ -408,11 +321,11 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
if onlineCount > 1 {
|
||||
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_StatusOnline(Int32(onlineCount)), 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: titleTheme.rootController.navigationBar.secondaryTextColor))
|
||||
state = .info(string, .generic)
|
||||
} 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)
|
||||
}
|
||||
} else if let channel = peer as? TelegramChannel {
|
||||
@ -420,17 +333,17 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
if memberCount == 0 {
|
||||
let string: NSAttributedString
|
||||
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 {
|
||||
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)
|
||||
} else {
|
||||
if case .group = channel.info, let onlineMemberCount = onlineMemberCount, onlineMemberCount > 1 {
|
||||
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_StatusOnline(Int32(onlineMemberCount)), 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: titleTheme.rootController.navigationBar.secondaryTextColor))
|
||||
state = .info(string, .generic)
|
||||
} else {
|
||||
let membersString: String
|
||||
@ -439,17 +352,17 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch channel.info {
|
||||
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)
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -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.hasEmbeddedTitleContent = hasEmbeddedTitleContent
|
||||
self.strings = strings
|
||||
|
||||
//self.networkStatusNode?.updateTheme(theme: theme)
|
||||
let titleContent = self.titleContent
|
||||
self.titleContent = titleContent
|
||||
self.updateStatus()
|
||||
@ -568,8 +481,6 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (size, clearBounds)
|
||||
|
||||
let transition: ContainedViewLayoutTransition = .immediate
|
||||
|
||||
self.button.frame = clearBounds
|
||||
self.contentContainer.frame = clearBounds
|
||||
|
||||
|
@ -11,6 +11,9 @@ public final class OverlayMediaControllerImpl: ViewController, OverlayMediaContr
|
||||
return self.displayNode as! OverlayMediaControllerNode
|
||||
}
|
||||
|
||||
public var updatePossibleEmbeddingItem: ((OverlayMediaControllerEmbeddingItem?) -> Void)?
|
||||
public var embedPossibleEmbeddingItem: ((OverlayMediaControllerEmbeddingItem) -> Bool)?
|
||||
|
||||
public init() {
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
@ -22,7 +25,11 @@ public final class OverlayMediaControllerImpl: ViewController, OverlayMediaContr
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,12 @@ private final class OverlayMediaVideoNodeData {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
final class OverlayMediaControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
private let updatePossibleEmbeddingItem: (OverlayMediaControllerEmbeddingItem?) -> Void
|
||||
private let embedPossibleEmbeddingItem: (OverlayMediaControllerEmbeddingItem) -> Bool
|
||||
|
||||
private var videoNodes: [OverlayMediaVideoNodeData] = []
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
@ -40,7 +45,10 @@ final class OverlayMediaControllerNode: ASDisplayNode, UIGestureRecognizerDelega
|
||||
private var pinchingNode: OverlayMediaItemNode?
|
||||
private var pinchingNodeInitialSize: CGSize?
|
||||
|
||||
override init() {
|
||||
init(updatePossibleEmbeddingItem: @escaping (OverlayMediaControllerEmbeddingItem?) -> Void, embedPossibleEmbeddingItem: @escaping (OverlayMediaControllerEmbeddingItem) -> Bool) {
|
||||
self.updatePossibleEmbeddingItem = updatePossibleEmbeddingItem
|
||||
self.embedPossibleEmbeddingItem = embedPossibleEmbeddingItem
|
||||
|
||||
super.init()
|
||||
|
||||
self.setViewBlock({
|
||||
@ -329,34 +337,46 @@ final class OverlayMediaControllerNode: ASDisplayNode, UIGestureRecognizerDelega
|
||||
draggingNode.updateMinimizedEdge(nil, adjusting: true)
|
||||
}
|
||||
draggingNode.frame = nodeFrame
|
||||
self.updatePossibleEmbeddingItem(OverlayMediaControllerEmbeddingItem(
|
||||
position: nodeFrame.center,
|
||||
itemNode: draggingNode
|
||||
))
|
||||
}
|
||||
case .ended, .cancelled:
|
||||
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 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 shouldDismiss && draggingNode.isMinimizeable {
|
||||
draggingNode.updateMinimizedEdge(updatedLocation.x.isZero ? .left : .right, adjusting: false)
|
||||
self.videoNodes[index].isMinimized = true
|
||||
if self.embedPossibleEmbeddingItem(OverlayMediaControllerEmbeddingItem(
|
||||
position: previousFrame.center,
|
||||
itemNode: draggingNode
|
||||
)) {
|
||||
self.draggingNode = nil
|
||||
} else {
|
||||
draggingNode.updateMinimizedEdge(nil, adjusting: true)
|
||||
self.videoNodes[index].isMinimized = false
|
||||
}
|
||||
|
||||
if let group = draggingNode.group {
|
||||
self.locationByGroup[group] = updatedLocation
|
||||
}
|
||||
self.videoNodes[index].location = updatedLocation
|
||||
|
||||
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()
|
||||
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 shouldDismiss && draggingNode.isMinimizeable {
|
||||
draggingNode.updateMinimizedEdge(updatedLocation.x.isZero ? .left : .right, adjusting: false)
|
||||
self.videoNodes[index].isMinimized = true
|
||||
} else {
|
||||
draggingNode.updateMinimizedEdge(nil, adjusting: true)
|
||||
self.videoNodes[index].isMinimized = false
|
||||
}
|
||||
|
||||
if let group = draggingNode.group {
|
||||
self.locationByGroup[group] = updatedLocation
|
||||
}
|
||||
self.videoNodes[index].location = updatedLocation
|
||||
|
||||
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:
|
||||
break
|
||||
|
@ -2281,14 +2281,14 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
if buttonKeys.count > 3 {
|
||||
if self.isOpenedFromChat {
|
||||
switch buttonKey {
|
||||
case .message, .search:
|
||||
case .message, .search, .videoCall:
|
||||
hiddenWhileExpanded = true
|
||||
default:
|
||||
hiddenWhileExpanded = false
|
||||
}
|
||||
} else {
|
||||
switch buttonKey {
|
||||
case .mute, .search:
|
||||
case .mute, .search, .videoCall:
|
||||
hiddenWhileExpanded = true
|
||||
default:
|
||||
hiddenWhileExpanded = false
|
||||
|
@ -207,6 +207,43 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
|
||||
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)
|
||||
|> then(accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings])
|
||||
|> map { sharedData in
|
||||
|
@ -10,7 +10,7 @@ import TelegramAudio
|
||||
import AccountContext
|
||||
|
||||
public final class OverlayUniversalVideoNode: OverlayMediaItemNode {
|
||||
private let content: UniversalVideoContent
|
||||
public let content: UniversalVideoContent
|
||||
private let videoNode: UniversalVideoNode
|
||||
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) {
|
||||
self.content = content
|
||||
self.defaultExpand = expand
|
||||
|
||||
var expandImpl: (() -> Void)?
|
||||
|
||||
var unminimizeImpl: (() -> Void)?
|
||||
var togglePlayPauseImpl: (() -> Void)?
|
||||
var closeImpl: (() -> Void)?
|
||||
@ -40,7 +48,7 @@ public final class OverlayUniversalVideoNode: OverlayMediaItemNode {
|
||||
}, togglePlayPause: {
|
||||
togglePlayPauseImpl?()
|
||||
}, expand: {
|
||||
expand()
|
||||
expandImpl?()
|
||||
}, close: {
|
||||
closeImpl?()
|
||||
})
|
||||
@ -49,6 +57,17 @@ public final class OverlayUniversalVideoNode: OverlayMediaItemNode {
|
||||
|
||||
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
|
||||
self?.unminimize?()
|
||||
}
|
||||
@ -57,6 +76,10 @@ public final class OverlayUniversalVideoNode: OverlayMediaItemNode {
|
||||
}
|
||||
closeImpl = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
if let customClose = strongSelf.customClose {
|
||||
customClose()
|
||||
return
|
||||
}
|
||||
if strongSelf.videoNode.hasAttachedContext {
|
||||
strongSelf.videoNode.continuePlayingWithoutSound()
|
||||
}
|
||||
@ -104,18 +127,32 @@ public final class OverlayUniversalVideoNode: OverlayMediaItemNode {
|
||||
|
||||
override public func updateLayout(_ size: CGSize) {
|
||||
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.videoNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.videoNode.updateLayout(size: size, transition: .immediate)
|
||||
transition.updateFrame(node: self.videoNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
self.videoNode.updateLayout(size: size, transition: transition)
|
||||
}
|
||||
|
||||
override public func updateMinimizedEdge(_ edge: OverlayMediaItemMinimizationEdge?, adjusting: Bool) {
|
||||
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>) {
|
||||
self.controlsNode.status = status |> map { value -> MediaPlayerStatus in
|
||||
if let value = value {
|
||||
|
@ -109,12 +109,12 @@ final class PictureInPictureVideoControlsNode: ASDisplayNode {
|
||||
|
||||
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)
|
||||
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.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.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() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user