mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-16 19:30:29 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
76baf58242
@ -5635,3 +5635,6 @@ Any member of this group will be able to see messages in the channel.";
|
|||||||
|
|
||||||
"Settings.SetProfilePhotoOrVideo" = "Set Profile Photo or Video";
|
"Settings.SetProfilePhotoOrVideo" = "Set Profile Photo or Video";
|
||||||
"Settings.SetNewProfilePhotoOrVideo" = "Set New Profile Photo or Video";
|
"Settings.SetNewProfilePhotoOrVideo" = "Set New Profile Photo or Video";
|
||||||
|
|
||||||
|
"Conversation.Unarchive" = "Unarhive";
|
||||||
|
"Conversation.UnarchiveDone" = "The chat was moved to your main list.";
|
||||||
|
|||||||
@ -29,6 +29,7 @@ public protocol MediaManager: class {
|
|||||||
func filteredPlaylistState(accountId: AccountRecordId, playlistId: SharedMediaPlaylistId, itemId: SharedMediaPlaylistItemId, type: MediaManagerPlayerType) -> Signal<SharedMediaPlayerItemPlaybackState?, NoError>
|
func filteredPlaylistState(accountId: AccountRecordId, playlistId: SharedMediaPlaylistId, itemId: SharedMediaPlaylistItemId, type: MediaManagerPlayerType) -> Signal<SharedMediaPlayerItemPlaybackState?, NoError>
|
||||||
|
|
||||||
func setOverlayVideoNode(_ node: OverlayMediaItemNode?)
|
func setOverlayVideoNode(_ node: OverlayMediaItemNode?)
|
||||||
|
func hasOverlayVideoNode(_ node: OverlayMediaItemNode) -> Bool
|
||||||
|
|
||||||
func audioRecorder(beginWithTone: Bool, applicationBindings: TelegramApplicationBindings, beganWithTone: @escaping (Bool) -> Void) -> Signal<ManagedAudioRecorder?, NoError>
|
func audioRecorder(beginWithTone: Bool, applicationBindings: TelegramApplicationBindings, beganWithTone: @escaping (Bool) -> Void) -> Signal<ManagedAudioRecorder?, NoError>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,9 @@ open class OverlayMediaItemNode: ASDisplayNode {
|
|||||||
|
|
||||||
open var unminimize: (() -> Void)?
|
open var unminimize: (() -> Void)?
|
||||||
|
|
||||||
|
public var manualExpandEmbed: (() -> Void)?
|
||||||
|
public var customUnembedWhenPortrait: ((OverlayMediaItemNode) -> Bool)?
|
||||||
|
|
||||||
open var group: OverlayMediaItemNodeGroup? {
|
open var group: OverlayMediaItemNodeGroup? {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -228,6 +228,7 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||||||
let velocity = recognizer.velocity(in: self.view).x
|
let velocity = recognizer.velocity(in: self.view).x
|
||||||
|
|
||||||
if velocity > 1000 || navigationTransitionCoordinator.progress > 0.2 {
|
if velocity > 1000 || navigationTransitionCoordinator.progress > 0.2 {
|
||||||
|
self.state.top?.value.viewWillLeaveNavigation()
|
||||||
navigationTransitionCoordinator.animateCompletion(velocity, completion: { [weak self] in
|
navigationTransitionCoordinator.animateCompletion(velocity, completion: { [weak self] in
|
||||||
guard let strongSelf = self, let _ = strongSelf.state.layout, let _ = strongSelf.state.transition, let top = strongSelf.state.top else {
|
guard let strongSelf = self, let _ = strongSelf.state.layout, let _ = strongSelf.state.transition, let top = strongSelf.state.top else {
|
||||||
return
|
return
|
||||||
@ -399,6 +400,7 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fromValue.value.viewWillLeaveNavigation()
|
||||||
fromValue.value.viewWillDisappear(true)
|
fromValue.value.viewWillDisappear(true)
|
||||||
toValue.value.viewWillAppear(true)
|
toValue.value.viewWillAppear(true)
|
||||||
toValue.value.setIgnoreAppearanceMethodInvocations(true)
|
toValue.value.setIgnoreAppearanceMethodInvocations(true)
|
||||||
@ -464,6 +466,7 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
if let fromValue = fromValue {
|
if let fromValue = fromValue {
|
||||||
|
fromValue.value.viewWillLeaveNavigation()
|
||||||
fromValue.value.viewWillDisappear(false)
|
fromValue.value.viewWillDisappear(false)
|
||||||
fromValue.value.setIgnoreAppearanceMethodInvocations(true)
|
fromValue.value.setIgnoreAppearanceMethodInvocations(true)
|
||||||
fromValue.value.displayNode.removeFromSupernode()
|
fromValue.value.displayNode.removeFromSupernode()
|
||||||
|
|||||||
@ -117,6 +117,7 @@ open class NavigationBar: ASDisplayNode {
|
|||||||
|
|
||||||
public var userInfo: Any?
|
public var userInfo: Any?
|
||||||
public var makeCustomTransitionNode: ((NavigationBar, Bool) -> CustomNavigationTransitionNode?)?
|
public var makeCustomTransitionNode: ((NavigationBar, Bool) -> CustomNavigationTransitionNode?)?
|
||||||
|
public var allowsCustomTransition: (() -> Bool)?
|
||||||
|
|
||||||
private var collapsed: Bool {
|
private var collapsed: Bool {
|
||||||
get {
|
get {
|
||||||
|
|||||||
@ -561,6 +561,9 @@ public enum TabBarItemContextActionType {
|
|||||||
super.viewDidDisappear(animated)
|
super.viewDidDisappear(animated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open func viewWillLeaveNavigation() {
|
||||||
|
}
|
||||||
|
|
||||||
open override func viewDidAppear(_ animated: Bool) {
|
open override func viewDidAppear(_ animated: Bool) {
|
||||||
self.activeInputView = nil
|
self.activeInputView = nil
|
||||||
|
|
||||||
|
|||||||
@ -290,6 +290,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
|
|
||||||
var playbackCompleted: (() -> Void)?
|
var playbackCompleted: (() -> Void)?
|
||||||
|
|
||||||
|
private var customUnembedWhenPortrait: ((OverlayMediaItemNode) -> Bool)?
|
||||||
|
|
||||||
init(context: AccountContext, presentationData: PresentationData, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void, openActionOptions: @escaping (GalleryControllerInteractionTapAction) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
init(context: AccountContext, presentationData: PresentationData, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void, openActionOptions: @escaping (GalleryControllerInteractionTapAction) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
@ -423,6 +425,10 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
override func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
|
if let _ = self.customUnembedWhenPortrait, layout.size.width < layout.size.height {
|
||||||
|
self.expandIntoCustomPiP()
|
||||||
|
}
|
||||||
|
|
||||||
super.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)
|
super.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)
|
||||||
|
|
||||||
var dismiss = false
|
var dismiss = false
|
||||||
@ -888,6 +894,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let node = node.0 as? OverlayMediaItemNode {
|
if let node = node.0 as? OverlayMediaItemNode {
|
||||||
|
self.customUnembedWhenPortrait = node.customUnembedWhenPortrait
|
||||||
|
node.customUnembedWhenPortrait = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if let node = node.0 as? OverlayMediaItemNode, self.context.sharedContext.mediaManager.hasOverlayVideoNode(node) {
|
||||||
var transformedFrame = node.view.convert(node.view.bounds, to: videoNode.view)
|
var transformedFrame = node.view.convert(node.view.bounds, to: videoNode.view)
|
||||||
let transformedSuperFrame = node.view.convert(node.view.bounds, to: videoNode.view.superview)
|
let transformedSuperFrame = node.view.convert(node.view.bounds, to: videoNode.view.superview)
|
||||||
|
|
||||||
@ -960,10 +971,12 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
surfaceCopyView.layer.animate(from: NSValue(caTransform3D: CATransform3DIdentity), to: NSValue(caTransform3D: CATransform3DMakeScale(scale.width, scale.height, 1.0)), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25, removeOnCompletion: false)
|
surfaceCopyView.layer.animate(from: NSValue(caTransform3D: CATransform3DIdentity), to: NSValue(caTransform3D: CATransform3DMakeScale(scale.width, scale.height, 1.0)), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25, removeOnCompletion: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if surfaceCopyView.superview != nil {
|
||||||
videoNode.allowsGroupOpacity = true
|
videoNode.allowsGroupOpacity = true
|
||||||
videoNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1, completion: { [weak videoNode] _ in
|
videoNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1, completion: { [weak videoNode] _ in
|
||||||
videoNode?.allowsGroupOpacity = false
|
videoNode?.allowsGroupOpacity = false
|
||||||
})
|
})
|
||||||
|
}
|
||||||
videoNode.layer.animatePosition(from: CGPoint(x: transformedSuperFrame.midX, y: transformedSuperFrame.midY), to: videoNode.layer.position, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring)
|
videoNode.layer.animatePosition(from: CGPoint(x: transformedSuperFrame.midX, y: transformedSuperFrame.midY), to: videoNode.layer.position, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
|
||||||
transformedFrame.origin = CGPoint()
|
transformedFrame.origin = CGPoint()
|
||||||
@ -1272,9 +1285,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func pictureInPictureButtonPressed() {
|
private func expandIntoCustomPiP() {
|
||||||
if let item = self.item, let videoNode = self.videoNode {
|
if let item = self.item, let videoNode = self.videoNode, let customUnembedWhenPortrait = customUnembedWhenPortrait {
|
||||||
|
self.customUnembedWhenPortrait = nil
|
||||||
videoNode.setContinuePlayingWithoutSoundOnLostAudioSession(false)
|
videoNode.setContinuePlayingWithoutSoundOnLostAudioSession(false)
|
||||||
|
|
||||||
let context = self.context
|
let context = self.context
|
||||||
@ -1306,9 +1319,77 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
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)
|
||||||
}), addToTransitionSurface: { [weak overlaySupernode, weak overlayNode] view in
|
}), addToTransitionSurface: { [weak context, weak overlaySupernode, weak overlayNode] view in
|
||||||
|
guard let context = context, let overlayNode = overlayNode else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if context.sharedContext.mediaManager.hasOverlayVideoNode(overlayNode) {
|
||||||
overlaySupernode?.view.addSubview(view)
|
overlaySupernode?.view.addSubview(view)
|
||||||
overlayNode?.canAttachContent = false
|
}
|
||||||
|
overlayNode.canAttachContent = false
|
||||||
|
})
|
||||||
|
} else if let info = context.sharedContext.mediaManager.galleryHiddenMediaManager.findTarget(messageId: id, media: media) {
|
||||||
|
return GalleryTransitionArguments(transitionNode: (info.1, info.1.bounds, {
|
||||||
|
return info.2()
|
||||||
|
}), addToTransitionSurface: info.0)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
case .webPage:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if customUnembedWhenPortrait(overlayNode) {
|
||||||
|
self.beginCustomDismiss()
|
||||||
|
self.statusNode.isHidden = true
|
||||||
|
self.animateOut(toOverlay: overlayNode, completion: { [weak self] in
|
||||||
|
self?.completeCustomDismiss()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func pictureInPictureButtonPressed() {
|
||||||
|
if let item = self.item, let videoNode = self.videoNode {
|
||||||
|
videoNode.setContinuePlayingWithoutSoundOnLostAudioSession(false)
|
||||||
|
|
||||||
|
let context = self.context
|
||||||
|
let baseNavigationController = self.baseNavigationController()
|
||||||
|
let mediaManager = self.context.sharedContext.mediaManager
|
||||||
|
var expandImpl: (() -> Void)?
|
||||||
|
let overlayNode = OverlayUniversalVideoNode(postbox: self.context.account.postbox, audioSession: context.sharedContext.mediaManager.audioSession, manager: context.sharedContext.mediaManager.universalVideoManager, content: item.content, expand: {
|
||||||
|
expandImpl?()
|
||||||
|
}, close: { [weak mediaManager] in
|
||||||
|
mediaManager?.setOverlayVideoNode(nil)
|
||||||
|
})
|
||||||
|
expandImpl = { [weak overlayNode] in
|
||||||
|
guard let contentInfo = item.contentInfo, let overlayNode = overlayNode else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch contentInfo {
|
||||||
|
case let .message(message):
|
||||||
|
let gallery = GalleryController(context: context, source: .peerMessagesAtId(message.id), replaceRootController: { controller, ready in
|
||||||
|
if let baseNavigationController = baseNavigationController {
|
||||||
|
baseNavigationController.replaceTopController(controller, animated: false, ready: ready)
|
||||||
|
}
|
||||||
|
}, baseNavigationController: baseNavigationController)
|
||||||
|
gallery.temporaryDoNotWaitForReady = true
|
||||||
|
|
||||||
|
baseNavigationController?.view.endEditing(true)
|
||||||
|
|
||||||
|
(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)
|
||||||
|
}), addToTransitionSurface: { [weak context, weak overlaySupernode, weak overlayNode] view in
|
||||||
|
guard let context = context, let overlayNode = overlayNode else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if context.sharedContext.mediaManager.hasOverlayVideoNode(overlayNode) {
|
||||||
|
overlaySupernode?.view.addSubview(view)
|
||||||
|
}
|
||||||
|
overlayNode.canAttachContent = false
|
||||||
})
|
})
|
||||||
} else if let info = context.sharedContext.mediaManager.galleryHiddenMediaManager.findTarget(messageId: id, media: media) {
|
} else if let info = context.sharedContext.mediaManager.galleryHiddenMediaManager.findTarget(messageId: id, media: media) {
|
||||||
return GalleryTransitionArguments(transitionNode: (info.1, info.1.bounds, {
|
return GalleryTransitionArguments(transitionNode: (info.1, info.1.bounds, {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Postbox
|
import Postbox
|
||||||
import TelegramApi
|
import TelegramApi
|
||||||
|
import SwiftSignalKit
|
||||||
import SyncCore
|
import SyncCore
|
||||||
|
|
||||||
extension PeerStatusSettings {
|
extension PeerStatusSettings {
|
||||||
@ -34,3 +34,22 @@ extension PeerStatusSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func unarchiveAutomaticallyArchivedPeer(account: Account, peerId: PeerId) {
|
||||||
|
let _ = (account.postbox.transaction { transaction -> Void in
|
||||||
|
updatePeerGroupIdInteractively(transaction: transaction, peerId: peerId, groupId: .root)
|
||||||
|
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
||||||
|
guard let currentData = current as? CachedUserData, let currentStatusSettings = currentData.peerStatusSettings else {
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
var statusSettings = currentStatusSettings
|
||||||
|
statusSettings.flags.remove(.canBlock)
|
||||||
|
statusSettings.flags.remove(.canReport)
|
||||||
|
statusSettings.flags.remove(.autoArchived)
|
||||||
|
return currentData.withUpdatedPeerStatusSettings(statusSettings)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|> deliverOnMainQueue).start()
|
||||||
|
|
||||||
|
let _ = updatePeerMuteSetting(account: account, peerId: peerId, muteInterval: nil).start()
|
||||||
|
}
|
||||||
|
|||||||
@ -45,9 +45,9 @@ public extension TabBarControllerTheme {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public extension NavigationBarTheme {
|
public extension NavigationBarTheme {
|
||||||
convenience init(rootControllerTheme: PresentationTheme, hideBackground: Bool = false) {
|
convenience init(rootControllerTheme: PresentationTheme, hideBackground: Bool = false, hideBadge: Bool = false) {
|
||||||
let theme = rootControllerTheme.rootController.navigationBar
|
let theme = rootControllerTheme.rootController.navigationBar
|
||||||
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)
|
self.init(buttonColor: theme.buttonColor, disabledButtonColor: theme.disabledButtonColor, primaryTextColor: theme.primaryTextColor, backgroundColor: hideBackground ? .clear : theme.backgroundColor, separatorColor: hideBackground ? .clear : theme.separatorColor, badgeBackgroundColor: hideBadge ? .clear : theme.badgeBackgroundColor, badgeStrokeColor: hideBadge ? .clear : theme.badgeStrokeColor, badgeTextColor: hideBadge ? .clear : theme.badgeTextColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,8 +62,8 @@ 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) {
|
convenience init(presentationData: PresentationData, hideBackground: Bool, hideBadge: Bool) {
|
||||||
self.init(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme, hideBackground: hideBackground), strings: NavigationBarStrings(presentationStrings: presentationData.strings))
|
self.init(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme, hideBackground: hideBackground, hideBadge: hideBadge), strings: NavigationBarStrings(presentationStrings: presentationData.strings))
|
||||||
}
|
}
|
||||||
|
|
||||||
convenience init(presentationTheme: PresentationTheme, presentationStrings: PresentationStrings) {
|
convenience init(presentationTheme: PresentationTheme, presentationStrings: PresentationStrings) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
1
submodules/TelegramUI/Resources/Animations/Bin.json
Normal file
1
submodules/TelegramUI/Resources/Animations/Bin.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -24,7 +24,7 @@ final class VoiceBlobView: UIView, TGModernConversationInputMicButtonDecoration
|
|||||||
pointsCount: 8,
|
pointsCount: 8,
|
||||||
minRandomness: 1,
|
minRandomness: 1,
|
||||||
maxRandomness: 1,
|
maxRandomness: 1,
|
||||||
minSpeed: 3,
|
minSpeed: 1,
|
||||||
maxSpeed: 7,
|
maxSpeed: 7,
|
||||||
minScale: 0.55,
|
minScale: 0.55,
|
||||||
maxScale: 0.9,
|
maxScale: 0.9,
|
||||||
@ -34,7 +34,7 @@ final class VoiceBlobView: UIView, TGModernConversationInputMicButtonDecoration
|
|||||||
pointsCount: 8,
|
pointsCount: 8,
|
||||||
minRandomness: 1,
|
minRandomness: 1,
|
||||||
maxRandomness: 1,
|
maxRandomness: 1,
|
||||||
minSpeed: 3,
|
minSpeed: 1,
|
||||||
maxSpeed: 7,
|
maxSpeed: 7,
|
||||||
minScale: 0.55,
|
minScale: 0.55,
|
||||||
maxScale: 1,
|
maxScale: 1,
|
||||||
|
|||||||
@ -322,6 +322,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
private let peekTimerDisposable = MetaDisposable()
|
private let peekTimerDisposable = MetaDisposable()
|
||||||
|
|
||||||
private var hasEmbeddedTitleContent = false
|
private var hasEmbeddedTitleContent = false
|
||||||
|
private var isEmbeddedTitleContentHidden = false
|
||||||
|
|
||||||
public override var customData: Any? {
|
public override var customData: Any? {
|
||||||
return self.chatLocation
|
return self.chatLocation
|
||||||
@ -375,7 +376,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
case .inline:
|
case .inline:
|
||||||
navigationBarPresentationData = nil
|
navigationBarPresentationData = nil
|
||||||
default:
|
default:
|
||||||
navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData, hideBackground: true)
|
navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData, hideBackground: true, hideBadge: false)
|
||||||
}
|
}
|
||||||
super.init(context: context, navigationBarPresentationData: navigationBarPresentationData, mediaAccessoryPanelVisibility: mediaAccessoryPanelVisibility, locationBroadcastPanelSource: locationBroadcastPanelSource)
|
super.init(context: context, navigationBarPresentationData: navigationBarPresentationData, mediaAccessoryPanelVisibility: mediaAccessoryPanelVisibility, locationBroadcastPanelSource: locationBroadcastPanelSource)
|
||||||
|
|
||||||
@ -2059,6 +2060,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
if case let .peer(peerId) = chatLocation, peerId != context.account.peerId, subject != .scheduledMessages {
|
if case let .peer(peerId) = chatLocation, peerId != context.account.peerId, subject != .scheduledMessages {
|
||||||
self.navigationBar?.userInfo = PeerInfoNavigationSourceTag(peerId: peerId)
|
self.navigationBar?.userInfo = PeerInfoNavigationSourceTag(peerId: peerId)
|
||||||
}
|
}
|
||||||
|
self.navigationBar?.allowsCustomTransition = { [weak self] in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return !strongSelf.chatDisplayNode.hasEmbeddedTitleContent
|
||||||
|
}
|
||||||
|
|
||||||
self.chatTitleView = ChatTitleView(account: self.context.account, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder)
|
self.chatTitleView = ChatTitleView(account: self.context.account, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder)
|
||||||
self.navigationItem.titleView = self.chatTitleView
|
self.navigationItem.titleView = self.chatTitleView
|
||||||
@ -2799,9 +2806,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
let navigationBarTheme: NavigationBarTheme
|
let navigationBarTheme: NavigationBarTheme
|
||||||
|
|
||||||
if self.hasEmbeddedTitleContent {
|
if self.hasEmbeddedTitleContent {
|
||||||
navigationBarTheme = NavigationBarTheme(rootControllerTheme: defaultDarkPresentationTheme, hideBackground: true)
|
navigationBarTheme = NavigationBarTheme(rootControllerTheme: defaultDarkPresentationTheme, hideBackground: true, hideBadge: true)
|
||||||
} else {
|
} else {
|
||||||
navigationBarTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme, hideBackground: true)
|
navigationBarTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme, hideBackground: true, hideBadge: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)))
|
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)))
|
||||||
@ -4610,6 +4617,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return nil
|
return nil
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
}, unarchivePeer: { [weak self] in
|
||||||
|
guard let strongSelf = self, case let .peer(peerId) = strongSelf.chatLocation else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
unarchiveAutomaticallyArchivedPeer(account: strongSelf.context.account, peerId: peerId)
|
||||||
|
|
||||||
|
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .succeed(text: strongSelf.presentationData.strings.Conversation_UnarchiveDone), elevatedLayout: false, action: { _ in return false }), in: .current)
|
||||||
}, statuses: ChatPanelInterfaceInteractionStatuses(editingMessage: self.editingMessage.get(), startingBot: self.startingBot.get(), unblockingPeer: self.unblockingPeer.get(), searching: self.searching.get(), loadingMessage: self.loadingMessage.get(), inlineSearch: self.performingInlineSearch.get()))
|
}, statuses: ChatPanelInterfaceInteractionStatuses(editingMessage: self.editingMessage.get(), startingBot: self.startingBot.get(), unblockingPeer: self.unblockingPeer.get(), searching: self.searching.get(), loadingMessage: self.loadingMessage.get(), inlineSearch: self.performingInlineSearch.get()))
|
||||||
|
|
||||||
switch self.chatLocation {
|
switch self.chatLocation {
|
||||||
@ -4756,10 +4770,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
self.chatDisplayNode.updateHasEmbeddedTitleContent = { [weak self] hasEmbeddedTitleContent in
|
self.chatDisplayNode.updateHasEmbeddedTitleContent = { [weak self] in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let hasEmbeddedTitleContent = strongSelf.chatDisplayNode.hasEmbeddedTitleContent
|
||||||
|
let isEmbeddedTitleContentHidden = strongSelf.chatDisplayNode.isEmbeddedTitleContentHidden
|
||||||
|
|
||||||
if strongSelf.hasEmbeddedTitleContent != hasEmbeddedTitleContent {
|
if strongSelf.hasEmbeddedTitleContent != hasEmbeddedTitleContent {
|
||||||
strongSelf.hasEmbeddedTitleContent = hasEmbeddedTitleContent
|
strongSelf.hasEmbeddedTitleContent = hasEmbeddedTitleContent
|
||||||
|
|
||||||
@ -4780,6 +4798,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
strongSelf.updateNavigationBarPresentation()
|
strongSelf.updateNavigationBarPresentation()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strongSelf.isEmbeddedTitleContentHidden != isEmbeddedTitleContentHidden {
|
||||||
|
strongSelf.isEmbeddedTitleContentHidden = isEmbeddedTitleContentHidden
|
||||||
|
|
||||||
|
if let navigationBar = strongSelf.navigationBar {
|
||||||
|
let transition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut)
|
||||||
|
transition.updateAlpha(node: navigationBar, alpha: isEmbeddedTitleContentHidden ? 0.0 : 1.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.interfaceInteraction = interfaceInteraction
|
self.interfaceInteraction = interfaceInteraction
|
||||||
@ -5174,6 +5201,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override public func viewWillLeaveNavigation() {
|
||||||
|
self.chatDisplayNode.willNavigateAway()
|
||||||
|
}
|
||||||
|
|
||||||
override public func inFocusUpdated(isInFocus: Bool) {
|
override public func inFocusUpdated(isInFocus: Bool) {
|
||||||
self.chatDisplayNode.inFocusUpdated(isInFocus: isInFocus)
|
self.chatDisplayNode.inFocusUpdated(isInFocus: isInFocus)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -69,6 +69,9 @@ private final class ChatEmbeddedTitleContentNode: ASDisplayNode {
|
|||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let backgroundNode: ASDisplayNode
|
private let backgroundNode: ASDisplayNode
|
||||||
private let videoNode: OverlayUniversalVideoNode
|
private let videoNode: OverlayUniversalVideoNode
|
||||||
|
private let disableInternalAnimationIn: Bool
|
||||||
|
private let isUIHiddenUpdated: () -> Void
|
||||||
|
private let unembedWhenPortrait: (OverlayMediaItemNode) -> Bool
|
||||||
|
|
||||||
private var validLayout: (CGSize, CGFloat, CGFloat)?
|
private var validLayout: (CGSize, CGFloat, CGFloat)?
|
||||||
|
|
||||||
@ -78,9 +81,14 @@ private final class ChatEmbeddedTitleContentNode: ASDisplayNode {
|
|||||||
private(set) var interactiveExtension: CGFloat = 0.0
|
private(set) var interactiveExtension: CGFloat = 0.0
|
||||||
private var freezeInteractiveExtension = false
|
private var freezeInteractiveExtension = false
|
||||||
|
|
||||||
init(context: AccountContext, videoNode: OverlayUniversalVideoNode, interactiveExtensionUpdated: @escaping (ContainedViewLayoutTransition) -> Void, dismissed: @escaping () -> Void) {
|
private(set) var isUIHidden: Bool = false
|
||||||
|
|
||||||
|
init(context: AccountContext, videoNode: OverlayUniversalVideoNode, disableInternalAnimationIn: Bool, interactiveExtensionUpdated: @escaping (ContainedViewLayoutTransition) -> Void, dismissed: @escaping () -> Void, isUIHiddenUpdated: @escaping () -> Void, unembedWhenPortrait: @escaping (OverlayMediaItemNode) -> Bool) {
|
||||||
self.dismissed = dismissed
|
self.dismissed = dismissed
|
||||||
self.interactiveExtensionUpdated = interactiveExtensionUpdated
|
self.interactiveExtensionUpdated = interactiveExtensionUpdated
|
||||||
|
self.isUIHiddenUpdated = isUIHiddenUpdated
|
||||||
|
self.unembedWhenPortrait = unembedWhenPortrait
|
||||||
|
self.disableInternalAnimationIn = disableInternalAnimationIn
|
||||||
|
|
||||||
self.context = context
|
self.context = context
|
||||||
|
|
||||||
@ -96,6 +104,14 @@ private final class ChatEmbeddedTitleContentNode: ASDisplayNode {
|
|||||||
self.addSubnode(self.backgroundNode)
|
self.addSubnode(self.backgroundNode)
|
||||||
|
|
||||||
self.view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))))
|
self.view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))))
|
||||||
|
|
||||||
|
self.videoNode.controlsAreShowingUpdated = { [weak self] value in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.isUIHidden = !value
|
||||||
|
strongSelf.isUIHiddenUpdated()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||||
@ -116,7 +132,7 @@ private final class ChatEmbeddedTitleContentNode: ASDisplayNode {
|
|||||||
|
|
||||||
if translation.y > 80.0 {
|
if translation.y > 80.0 {
|
||||||
self.freezeInteractiveExtension = true
|
self.freezeInteractiveExtension = true
|
||||||
self.videoNode.customExpand?()
|
self.expandIntoPiP()
|
||||||
} else {
|
} else {
|
||||||
self.interactiveExtension = max(0.0, offset)
|
self.interactiveExtension = max(0.0, offset)
|
||||||
self.interactiveExtensionUpdated(.immediate)
|
self.interactiveExtensionUpdated(.immediate)
|
||||||
@ -146,70 +162,61 @@ private final class ChatEmbeddedTitleContentNode: ASDisplayNode {
|
|||||||
let sourceFrame = self.videoNode.view.convert(self.videoNode.bounds, to: transitionSurface.view)
|
let sourceFrame = self.videoNode.view.convert(self.videoNode.bounds, to: transitionSurface.view)
|
||||||
let targetFrame = self.view.convert(videoFrame, to: transitionSurface.view)
|
let targetFrame = self.view.convert(videoFrame, to: transitionSurface.view)
|
||||||
|
|
||||||
|
var navigationBarCopy: UIView?
|
||||||
|
var navigationBarContainer: UIView?
|
||||||
|
var nodeTransition = transition
|
||||||
|
if self.disableInternalAnimationIn {
|
||||||
|
nodeTransition = .immediate
|
||||||
|
} else {
|
||||||
self.context.sharedContext.mediaManager.setOverlayVideoNode(nil)
|
self.context.sharedContext.mediaManager.setOverlayVideoNode(nil)
|
||||||
transitionSurface.addSubnode(self.videoNode)
|
transitionSurface.addSubnode(self.videoNode)
|
||||||
|
|
||||||
let navigationBarCopy = navigationBar?.view.snapshotView(afterScreenUpdates: true)
|
navigationBarCopy = navigationBar?.view.snapshotView(afterScreenUpdates: true)
|
||||||
let navigationBarContainer = UIView()
|
let navigationBarContainerValue = UIView()
|
||||||
navigationBarContainer.frame = targetFrame
|
navigationBarContainer = navigationBarContainerValue
|
||||||
navigationBarContainer.clipsToBounds = true
|
navigationBarContainerValue.frame = targetFrame
|
||||||
transitionSurface.view.addSubview(navigationBarContainer)
|
navigationBarContainerValue.clipsToBounds = true
|
||||||
|
transitionSurface.view.addSubview(navigationBarContainerValue)
|
||||||
|
}
|
||||||
|
|
||||||
navigationBarContainer.layer.animateFrame(from: sourceFrame, to: targetFrame, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring)
|
if !self.disableInternalAnimationIn {
|
||||||
|
navigationBarContainer?.layer.animateFrame(from: sourceFrame, to: targetFrame, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.disableInternalAnimationIn {
|
||||||
if let navigationBar = navigationBar, let navigationBarCopy = navigationBarCopy {
|
if let navigationBar = navigationBar, let navigationBarCopy = navigationBarCopy {
|
||||||
let navigationFrame = navigationBar.view.convert(navigationBar.bounds, to: transitionSurface.view)
|
let navigationFrame = navigationBar.view.convert(navigationBar.bounds, to: transitionSurface.view)
|
||||||
let navigationSourceFrame = navigationFrame.offsetBy(dx: -sourceFrame.minX, dy: -sourceFrame.minY)
|
let navigationSourceFrame = navigationFrame.offsetBy(dx: -sourceFrame.minX, dy: -sourceFrame.minY)
|
||||||
let navigationTargetFrame = navigationFrame.offsetBy(dx: -targetFrame.minX, dy: -targetFrame.minY)
|
let navigationTargetFrame = navigationFrame.offsetBy(dx: -targetFrame.minX, dy: -targetFrame.minY)
|
||||||
navigationBarCopy.frame = navigationTargetFrame
|
navigationBarCopy.frame = navigationTargetFrame
|
||||||
navigationBarContainer.addSubview(navigationBarCopy)
|
navigationBarContainer?.addSubview(navigationBarCopy)
|
||||||
|
|
||||||
navigationBarCopy.layer.animateFrame(from: navigationSourceFrame, to: navigationTargetFrame, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring)
|
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)
|
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.updateRoundCorners(false, transition: nodeTransition)
|
||||||
|
if !self.disableInternalAnimationIn {
|
||||||
self.videoNode.showControls()
|
self.videoNode.showControls()
|
||||||
|
}
|
||||||
|
|
||||||
self.videoNode.updateLayout(targetFrame.size, transition: .animated(duration: 0.25, curve: .spring))
|
self.videoNode.updateLayout(targetFrame.size, transition: nodeTransition)
|
||||||
self.videoNode.frame = targetFrame
|
self.videoNode.frame = targetFrame
|
||||||
|
if self.disableInternalAnimationIn {
|
||||||
|
self.addSubnode(self.videoNode)
|
||||||
|
} else {
|
||||||
self.videoNode.layer.animateFrame(from: sourceFrame, to: targetFrame, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak self] _ in
|
self.videoNode.layer.animateFrame(from: sourceFrame, to: targetFrame, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak self] _ in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
navigationBarContainer.removeFromSuperview()
|
navigationBarContainer?.removeFromSuperview()
|
||||||
strongSelf.addSubnode(strongSelf.videoNode)
|
strongSelf.addSubnode(strongSelf.videoNode)
|
||||||
if let (size, topInset, interactiveExtension) = strongSelf.validLayout {
|
if let (size, topInset, interactiveExtension) = strongSelf.validLayout {
|
||||||
strongSelf.updateLayout(size: size, topInset: topInset, interactiveExtension: interactiveExtension, transition: .immediate, transitionSurface: nil, navigationBar: nil)
|
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.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
|
self.videoNode.customClose = { [weak self] in
|
||||||
@ -227,6 +234,39 @@ private final class ChatEmbeddedTitleContentNode: ASDisplayNode {
|
|||||||
transition.updateFrame(node: self.videoNode, frame: videoFrame)
|
transition.updateFrame(node: self.videoNode, frame: videoFrame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func expand(intoLandscape: Bool) {
|
||||||
|
if intoLandscape {
|
||||||
|
let unembedWhenPortrait = self.unembedWhenPortrait
|
||||||
|
self.videoNode.customUnembedWhenPortrait = { videoNode in
|
||||||
|
unembedWhenPortrait(videoNode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.videoNode.expand()
|
||||||
|
}
|
||||||
|
|
||||||
|
func expandIntoPiP() {
|
||||||
|
let transition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .spring)
|
||||||
|
|
||||||
|
self.videoNode.customExpand = nil
|
||||||
|
self.videoNode.customClose = nil
|
||||||
|
|
||||||
|
let previousFrame = self.videoNode.frame
|
||||||
|
self.context.sharedContext.mediaManager.setOverlayVideoNode(self.videoNode)
|
||||||
|
self.videoNode.updateRoundCorners(true, transition: transition)
|
||||||
|
|
||||||
|
if let targetSuperview = self.videoNode.view.superview {
|
||||||
|
let sourceFrame = self.view.convert(previousFrame, to: targetSuperview)
|
||||||
|
let targetFrame = self.videoNode.frame
|
||||||
|
self.videoNode.frame = sourceFrame
|
||||||
|
self.videoNode.updateLayout(sourceFrame.size, transition: .immediate)
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.videoNode, frame: targetFrame)
|
||||||
|
self.videoNode.updateLayout(targetFrame.size, transition: transition)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.dismissed()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ChatEmbeddedTitlePeekContent: Equatable {
|
enum ChatEmbeddedTitlePeekContent: Equatable {
|
||||||
@ -387,6 +427,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
private var embeddedTitlePeekContent: ChatEmbeddedTitlePeekContent = .none
|
private var embeddedTitlePeekContent: ChatEmbeddedTitlePeekContent = .none
|
||||||
private var embeddedTitleContentNode: ChatEmbeddedTitleContentNode?
|
private var embeddedTitleContentNode: ChatEmbeddedTitleContentNode?
|
||||||
private var dismissedEmbeddedTitleContentNode: ChatEmbeddedTitleContentNode?
|
private var dismissedEmbeddedTitleContentNode: ChatEmbeddedTitleContentNode?
|
||||||
|
var hasEmbeddedTitleContent: Bool {
|
||||||
|
return self.embeddedTitleContentNode != nil
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
@ -892,6 +935,15 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
|
|
||||||
let statusBarHeight = layout.insets(options: [.statusBar]).top
|
let statusBarHeight = layout.insets(options: [.statusBar]).top
|
||||||
|
|
||||||
|
if let embeddedTitleContentNode = self.embeddedTitleContentNode, embeddedTitleContentNode.supernode != nil {
|
||||||
|
if layout.size.width > layout.size.height {
|
||||||
|
self.embeddedTitleContentNode = nil
|
||||||
|
self.dismissedEmbeddedTitleContentNode = embeddedTitleContentNode
|
||||||
|
embeddedTitleContentNode.expand(intoLandscape: true)
|
||||||
|
self.updateHasEmbeddedTitleContent?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let embeddedTitleContentNode = self.embeddedTitleContentNode {
|
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)
|
let embeddedSize = CGSize(width: layout.size.width, height: min(400.0, embeddedTitleContentNode.calculateHeight(width: layout.size.width)) + statusBarHeight + embeddedTitleContentNode.interactiveExtension)
|
||||||
if embeddedTitleContentNode.supernode == nil {
|
if embeddedTitleContentNode.supernode == nil {
|
||||||
@ -2648,7 +2700,15 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var updateHasEmbeddedTitleContent: ((Bool) -> Void)?
|
var isEmbeddedTitleContentHidden: Bool {
|
||||||
|
if let embeddedTitleContentNode = self.embeddedTitleContentNode {
|
||||||
|
return embeddedTitleContentNode.isUIHidden
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var updateHasEmbeddedTitleContent: (() -> Void)?
|
||||||
|
|
||||||
func acceptEmbeddedTitlePeekContent(content: NavigationControllerDropContent) -> Bool {
|
func acceptEmbeddedTitlePeekContent(content: NavigationControllerDropContent) -> Bool {
|
||||||
guard let (_, navigationHeight) = self.validLayout else {
|
guard let (_, navigationHeight) = self.validLayout else {
|
||||||
@ -2658,7 +2718,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if let item = content.item as? VideoNavigationControllerDropContentItem, let itemNode = item.itemNode as? OverlayUniversalVideoNode {
|
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
|
let embeddedTitleContentNode = ChatEmbeddedTitleContentNode(context: self.context, videoNode: itemNode, disableInternalAnimationIn: false, interactiveExtensionUpdated: { [weak self] transition in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -2671,12 +2731,20 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
strongSelf.embeddedTitleContentNode = nil
|
strongSelf.embeddedTitleContentNode = nil
|
||||||
strongSelf.dismissedEmbeddedTitleContentNode = embeddedTitleContentNode
|
strongSelf.dismissedEmbeddedTitleContentNode = embeddedTitleContentNode
|
||||||
strongSelf.requestLayout(.animated(duration: 0.25, curve: .spring))
|
strongSelf.requestLayout(.animated(duration: 0.25, curve: .spring))
|
||||||
strongSelf.updateHasEmbeddedTitleContent?(false)
|
strongSelf.updateHasEmbeddedTitleContent?()
|
||||||
}
|
}
|
||||||
|
}, isUIHiddenUpdated: { [weak self] in
|
||||||
|
self?.updateHasEmbeddedTitleContent?()
|
||||||
|
}, unembedWhenPortrait: { [weak self] itemNode in
|
||||||
|
guard let strongSelf = self, let itemNode = itemNode as? OverlayUniversalVideoNode else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
strongSelf.unembedWhenPortrait(contentNode: itemNode)
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
self.embeddedTitleContentNode = embeddedTitleContentNode
|
self.embeddedTitleContentNode = embeddedTitleContentNode
|
||||||
self.embeddedTitlePeekContent = .none
|
self.embeddedTitlePeekContent = .none
|
||||||
self.updateHasEmbeddedTitleContent?(true)
|
self.updateHasEmbeddedTitleContent?()
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.requestLayout(.animated(duration: 0.25, curve: .spring))
|
self.requestLayout(.animated(duration: 0.25, curve: .spring))
|
||||||
}
|
}
|
||||||
@ -2685,4 +2753,46 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func unembedWhenPortrait(contentNode: OverlayUniversalVideoNode) {
|
||||||
|
let embeddedTitleContentNode = ChatEmbeddedTitleContentNode(context: self.context, videoNode: contentNode, disableInternalAnimationIn: true, 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?()
|
||||||
|
}
|
||||||
|
}, isUIHiddenUpdated: { [weak self] in
|
||||||
|
self?.updateHasEmbeddedTitleContent?()
|
||||||
|
}, unembedWhenPortrait: { [weak self] itemNode in
|
||||||
|
guard let strongSelf = self, let itemNode = itemNode as? OverlayUniversalVideoNode else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
strongSelf.unembedWhenPortrait(contentNode: itemNode)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
self.embeddedTitleContentNode = embeddedTitleContentNode
|
||||||
|
self.embeddedTitlePeekContent = .none
|
||||||
|
self.updateHasEmbeddedTitleContent?()
|
||||||
|
self.requestLayout(.immediate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func willNavigateAway() {
|
||||||
|
if let embeddedTitleContentNode = self.embeddedTitleContentNode {
|
||||||
|
self.embeddedTitleContentNode = nil
|
||||||
|
self.dismissedEmbeddedTitleContentNode = embeddedTitleContentNode
|
||||||
|
embeddedTitleContentNode.expandIntoPiP()
|
||||||
|
self.requestLayout(.animated(duration: 0.25, curve: .spring))
|
||||||
|
self.updateHasEmbeddedTitleContent?()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,7 +35,7 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat
|
|||||||
if !peerStatusSettings.flags.isEmpty {
|
if !peerStatusSettings.flags.isEmpty {
|
||||||
if contactStatus.canAddContact && peerStatusSettings.contains(.canAddContact) {
|
if contactStatus.canAddContact && peerStatusSettings.contains(.canAddContact) {
|
||||||
displayActionsPanel = true
|
displayActionsPanel = true
|
||||||
} else if peerStatusSettings.contains(.canReport) || peerStatusSettings.contains(.canBlock) {
|
} else if peerStatusSettings.contains(.canReport) || peerStatusSettings.contains(.canBlock) || peerStatusSettings.contains(.autoArchived) {
|
||||||
displayActionsPanel = true
|
displayActionsPanel = true
|
||||||
} else if peerStatusSettings.contains(.canShareContact) {
|
} else if peerStatusSettings.contains(.canShareContact) {
|
||||||
displayActionsPanel = true
|
displayActionsPanel = true
|
||||||
|
|||||||
@ -119,9 +119,82 @@ final class ChatPanelInterfaceInteraction {
|
|||||||
let openScheduledMessages: () -> Void
|
let openScheduledMessages: () -> Void
|
||||||
let displaySearchResultsTooltip: (ASDisplayNode, CGRect) -> Void
|
let displaySearchResultsTooltip: (ASDisplayNode, CGRect) -> Void
|
||||||
let openPeersNearby: () -> Void
|
let openPeersNearby: () -> Void
|
||||||
|
let unarchivePeer: () -> Void
|
||||||
let statuses: ChatPanelInterfaceInteractionStatuses?
|
let statuses: ChatPanelInterfaceInteractionStatuses?
|
||||||
|
|
||||||
init(setupReplyMessage: @escaping (MessageId, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, setupEditMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, beginMessageSelection: @escaping ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void, deleteSelectedMessages: @escaping () -> Void, reportSelectedMessages: @escaping () -> Void, reportMessages: @escaping ([Message], ContextController?) -> Void, deleteMessages: @escaping ([Message], ContextController?, @escaping (ContextMenuActionResult) -> Void) -> Void, forwardSelectedMessages: @escaping () -> Void, forwardCurrentForwardMessages: @escaping () -> Void, forwardMessages: @escaping ([Message]) -> Void, shareSelectedMessages: @escaping () -> Void, updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void, updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void, openStickers: @escaping () -> Void, editMessage: @escaping () -> Void, beginMessageSearch: @escaping (ChatSearchDomain, String) -> Void, dismissMessageSearch: @escaping () -> Void, updateMessageSearch: @escaping (String) -> Void, openSearchResults: @escaping () -> Void, navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void, openCalendarSearch: @escaping () -> Void, toggleMembersSearch: @escaping (Bool) -> Void, navigateToMessage: @escaping (MessageId) -> Void, navigateToChat: @escaping (PeerId) -> Void, navigateToProfile: @escaping (PeerId) -> Void, openPeerInfo: @escaping () -> Void, togglePeerNotifications: @escaping () -> Void, sendContextResult: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, sendBotCommand: @escaping (Peer, String) -> Void, sendBotStart: @escaping (String?) -> Void, botSwitchChatWithPayload: @escaping (PeerId, String) -> Void, beginMediaRecording: @escaping (Bool) -> Void, finishMediaRecording: @escaping (ChatFinishMediaRecordingAction) -> Void, stopMediaRecording: @escaping () -> Void, lockMediaRecording: @escaping () -> Void, deleteRecordedMedia: @escaping () -> Void, sendRecordedMedia: @escaping () -> Void, displayRestrictedInfo: @escaping (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void, displayVideoUnmuteTip: @escaping (CGPoint?) -> Void, switchMediaRecordingMode: @escaping () -> Void, setupMessageAutoremoveTimeout: @escaping () -> Void, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, unblockPeer: @escaping () -> Void, pinMessage: @escaping (MessageId) -> Void, unpinMessage: @escaping () -> Void, shareAccountContact: @escaping () -> Void, reportPeer: @escaping () -> Void, presentPeerContact: @escaping () -> Void, dismissReportPeer: @escaping () -> Void, deleteChat: @escaping () -> Void, beginCall: @escaping (Bool) -> Void, toggleMessageStickerStarred: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, getNavigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, navigateFeed: @escaping () -> Void, openGrouping: @escaping () -> Void, toggleSilentPost: @escaping () -> Void, requestUnvoteInMessage: @escaping (MessageId) -> Void, requestStopPollInMessage: @escaping (MessageId) -> Void, updateInputLanguage: @escaping ((String?) -> String?) -> Void, unarchiveChat: @escaping () -> Void, openLinkEditing: @escaping () -> Void, reportPeerIrrelevantGeoLocation: @escaping () -> Void, displaySlowmodeTooltip: @escaping (ASDisplayNode, CGRect) -> Void, displaySendMessageOptions: @escaping (ASDisplayNode, ContextGesture) -> Void, openScheduledMessages: @escaping () -> Void, openPeersNearby: @escaping () -> Void, displaySearchResultsTooltip: @escaping (ASDisplayNode, CGRect) -> Void, statuses: ChatPanelInterfaceInteractionStatuses?) {
|
init(
|
||||||
|
setupReplyMessage: @escaping (MessageId, @escaping (ContainedViewLayoutTransition) -> Void) -> Void,
|
||||||
|
setupEditMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void,
|
||||||
|
beginMessageSelection: @escaping ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void,
|
||||||
|
deleteSelectedMessages: @escaping () -> Void,
|
||||||
|
reportSelectedMessages: @escaping () -> Void,
|
||||||
|
reportMessages: @escaping ([Message], ContextController?) -> Void,
|
||||||
|
deleteMessages: @escaping ([Message], ContextController?, @escaping (ContextMenuActionResult) -> Void) -> Void,
|
||||||
|
forwardSelectedMessages: @escaping () -> Void,
|
||||||
|
forwardCurrentForwardMessages: @escaping () -> Void,
|
||||||
|
forwardMessages: @escaping ([Message]) -> Void,
|
||||||
|
shareSelectedMessages: @escaping () -> Void,
|
||||||
|
updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void,
|
||||||
|
updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void,
|
||||||
|
openStickers: @escaping () -> Void,
|
||||||
|
editMessage: @escaping () -> Void,
|
||||||
|
beginMessageSearch: @escaping (ChatSearchDomain, String) -> Void,
|
||||||
|
dismissMessageSearch: @escaping () -> Void,
|
||||||
|
updateMessageSearch: @escaping (String) -> Void,
|
||||||
|
openSearchResults: @escaping () -> Void,
|
||||||
|
navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void,
|
||||||
|
openCalendarSearch: @escaping () -> Void,
|
||||||
|
toggleMembersSearch: @escaping (Bool) -> Void,
|
||||||
|
navigateToMessage: @escaping (MessageId) -> Void,
|
||||||
|
navigateToChat: @escaping (PeerId) -> Void,
|
||||||
|
navigateToProfile: @escaping (PeerId) -> Void,
|
||||||
|
openPeerInfo: @escaping () -> Void,
|
||||||
|
togglePeerNotifications: @escaping () -> Void,
|
||||||
|
sendContextResult: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool,
|
||||||
|
sendBotCommand: @escaping (Peer, String) -> Void,
|
||||||
|
sendBotStart: @escaping (String?) -> Void,
|
||||||
|
botSwitchChatWithPayload: @escaping (PeerId, String) -> Void,
|
||||||
|
beginMediaRecording: @escaping (Bool) -> Void,
|
||||||
|
finishMediaRecording: @escaping (ChatFinishMediaRecordingAction) -> Void,
|
||||||
|
stopMediaRecording: @escaping () -> Void,
|
||||||
|
lockMediaRecording: @escaping () -> Void,
|
||||||
|
deleteRecordedMedia: @escaping () -> Void,
|
||||||
|
sendRecordedMedia: @escaping () -> Void,
|
||||||
|
displayRestrictedInfo: @escaping (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void,
|
||||||
|
displayVideoUnmuteTip: @escaping (CGPoint?) -> Void,
|
||||||
|
switchMediaRecordingMode: @escaping () -> Void,
|
||||||
|
setupMessageAutoremoveTimeout: @escaping () -> Void,
|
||||||
|
sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool,
|
||||||
|
unblockPeer: @escaping () -> Void,
|
||||||
|
pinMessage: @escaping (MessageId) -> Void,
|
||||||
|
unpinMessage: @escaping () -> Void,
|
||||||
|
shareAccountContact: @escaping () -> Void,
|
||||||
|
reportPeer: @escaping () -> Void,
|
||||||
|
presentPeerContact: @escaping () -> Void,
|
||||||
|
dismissReportPeer: @escaping () -> Void,
|
||||||
|
deleteChat: @escaping () -> Void,
|
||||||
|
beginCall: @escaping (Bool) -> Void,
|
||||||
|
toggleMessageStickerStarred: @escaping (MessageId) -> Void,
|
||||||
|
presentController: @escaping (ViewController, Any?) -> Void,
|
||||||
|
getNavigationController: @escaping () -> NavigationController?,
|
||||||
|
presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void,
|
||||||
|
navigateFeed: @escaping () -> Void,
|
||||||
|
openGrouping: @escaping () -> Void,
|
||||||
|
toggleSilentPost: @escaping () -> Void,
|
||||||
|
requestUnvoteInMessage: @escaping (MessageId) -> Void,
|
||||||
|
requestStopPollInMessage: @escaping (MessageId) -> Void,
|
||||||
|
updateInputLanguage: @escaping ((String?) -> String?) -> Void,
|
||||||
|
unarchiveChat: @escaping () -> Void,
|
||||||
|
openLinkEditing: @escaping () -> Void,
|
||||||
|
reportPeerIrrelevantGeoLocation: @escaping () -> Void,
|
||||||
|
displaySlowmodeTooltip: @escaping (ASDisplayNode, CGRect) -> Void,
|
||||||
|
displaySendMessageOptions: @escaping (ASDisplayNode, ContextGesture) -> Void,
|
||||||
|
openScheduledMessages: @escaping () -> Void,
|
||||||
|
openPeersNearby: @escaping () -> Void,
|
||||||
|
displaySearchResultsTooltip: @escaping (ASDisplayNode, CGRect) -> Void,
|
||||||
|
unarchivePeer: @escaping () -> Void,
|
||||||
|
statuses: ChatPanelInterfaceInteractionStatuses?
|
||||||
|
) {
|
||||||
self.setupReplyMessage = setupReplyMessage
|
self.setupReplyMessage = setupReplyMessage
|
||||||
self.setupEditMessage = setupEditMessage
|
self.setupEditMessage = setupEditMessage
|
||||||
self.beginMessageSelection = beginMessageSelection
|
self.beginMessageSelection = beginMessageSelection
|
||||||
@ -191,6 +264,7 @@ final class ChatPanelInterfaceInteraction {
|
|||||||
self.openScheduledMessages = openScheduledMessages
|
self.openScheduledMessages = openScheduledMessages
|
||||||
self.openPeersNearby = openPeersNearby
|
self.openPeersNearby = openPeersNearby
|
||||||
self.displaySearchResultsTooltip = displaySearchResultsTooltip
|
self.displaySearchResultsTooltip = displaySearchResultsTooltip
|
||||||
|
self.unarchivePeer = unarchivePeer
|
||||||
self.statuses = statuses
|
self.statuses = statuses
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -123,7 +123,7 @@ final class ChatRecentActionsController: TelegramBaseController {
|
|||||||
}, openScheduledMessages: {
|
}, openScheduledMessages: {
|
||||||
}, openPeersNearby: {
|
}, openPeersNearby: {
|
||||||
}, displaySearchResultsTooltip: { _, _ in
|
}, displaySearchResultsTooltip: { _, _ in
|
||||||
}, statuses: nil)
|
}, unarchivePeer: {}, statuses: nil)
|
||||||
|
|
||||||
self.navigationItem.titleView = self.titleView
|
self.navigationItem.titleView = self.titleView
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,7 @@ private enum ChatReportPeerTitleButton: Equatable {
|
|||||||
case reportSpam
|
case reportSpam
|
||||||
case reportUserSpam
|
case reportUserSpam
|
||||||
case reportIrrelevantGeoLocation
|
case reportIrrelevantGeoLocation
|
||||||
|
case unarchive
|
||||||
|
|
||||||
func title(strings: PresentationStrings) -> String {
|
func title(strings: PresentationStrings) -> String {
|
||||||
switch self {
|
switch self {
|
||||||
@ -35,6 +36,8 @@ private enum ChatReportPeerTitleButton: Equatable {
|
|||||||
return strings.Conversation_ReportSpam
|
return strings.Conversation_ReportSpam
|
||||||
case .reportIrrelevantGeoLocation:
|
case .reportIrrelevantGeoLocation:
|
||||||
return strings.Conversation_ReportGroupLocation
|
return strings.Conversation_ReportGroupLocation
|
||||||
|
case .unarchive:
|
||||||
|
return strings.Conversation_Unarchive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -42,7 +45,19 @@ private enum ChatReportPeerTitleButton: Equatable {
|
|||||||
private func peerButtons(_ state: ChatPresentationInterfaceState) -> [ChatReportPeerTitleButton] {
|
private func peerButtons(_ state: ChatPresentationInterfaceState) -> [ChatReportPeerTitleButton] {
|
||||||
var buttons: [ChatReportPeerTitleButton] = []
|
var buttons: [ChatReportPeerTitleButton] = []
|
||||||
if let peer = state.renderedPeer?.chatMainPeer as? TelegramUser, let contactStatus = state.contactStatus, let peerStatusSettings = contactStatus.peerStatusSettings {
|
if let peer = state.renderedPeer?.chatMainPeer as? TelegramUser, let contactStatus = state.contactStatus, let peerStatusSettings = contactStatus.peerStatusSettings {
|
||||||
if contactStatus.canAddContact && peerStatusSettings.contains(.canAddContact) {
|
if peerStatusSettings.contains(.autoArchived) {
|
||||||
|
if peerStatusSettings.contains(.canBlock) || peerStatusSettings.contains(.canReport) {
|
||||||
|
if peer.isDeleted {
|
||||||
|
buttons.append(.reportUserSpam)
|
||||||
|
} else {
|
||||||
|
if !state.peerIsBlocked {
|
||||||
|
buttons.append(.block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buttons.append(.unarchive)
|
||||||
|
} else if contactStatus.canAddContact && peerStatusSettings.contains(.canAddContact) {
|
||||||
if peerStatusSettings.contains(.canBlock) || peerStatusSettings.contains(.canReport) {
|
if peerStatusSettings.contains(.canBlock) || peerStatusSettings.contains(.canReport) {
|
||||||
if !state.peerIsBlocked {
|
if !state.peerIsBlocked {
|
||||||
buttons.append(.block)
|
buttons.append(.block)
|
||||||
@ -375,12 +390,15 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let additionalRightInset: CGFloat = 36.0
|
let additionalRightInset: CGFloat = 36.0
|
||||||
let areaWidth = width - maxInset * 2.0 - additionalRightInset
|
var areaWidth = width - maxInset * 2.0 - additionalRightInset
|
||||||
let maxButtonWidth = floor(areaWidth / CGFloat(self.buttons.count))
|
let maxButtonWidth = floor(areaWidth / CGFloat(self.buttons.count))
|
||||||
let buttonSizes = self.buttons.map { button -> CGFloat in
|
let buttonSizes = self.buttons.map { button -> CGFloat in
|
||||||
return button.1.sizeThatFits(CGSize(width: maxButtonWidth, height: 100.0)).width
|
return button.1.sizeThatFits(CGSize(width: maxButtonWidth, height: 100.0)).width
|
||||||
}
|
}
|
||||||
let buttonsWidth = buttonSizes.reduce(0.0, +)
|
let buttonsWidth = buttonSizes.reduce(0.0, +)
|
||||||
|
if buttonsWidth < areaWidth - 20.0 {
|
||||||
|
areaWidth += additionalRightInset
|
||||||
|
}
|
||||||
let maxButtonSpacing = floor((areaWidth - buttonsWidth) / CGFloat(self.buttons.count - 1))
|
let maxButtonSpacing = floor((areaWidth - buttonsWidth) / CGFloat(self.buttons.count - 1))
|
||||||
let buttonSpacing = min(maxButtonSpacing, 110.0)
|
let buttonSpacing = min(maxButtonSpacing, 110.0)
|
||||||
let updatedButtonsWidth = buttonsWidth + CGFloat(self.buttons.count - 1) * buttonSpacing
|
let updatedButtonsWidth = buttonsWidth + CGFloat(self.buttons.count - 1) * buttonSpacing
|
||||||
@ -469,6 +487,8 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
self.interfaceInteraction?.shareAccountContact()
|
self.interfaceInteraction?.shareAccountContact()
|
||||||
case .block, .reportSpam, .reportUserSpam:
|
case .block, .reportSpam, .reportUserSpam:
|
||||||
self.interfaceInteraction?.reportPeer()
|
self.interfaceInteraction?.reportPeer()
|
||||||
|
case .unarchive:
|
||||||
|
self.interfaceInteraction?.unarchivePeer()
|
||||||
case .addContact:
|
case .addContact:
|
||||||
self.interfaceInteraction?.presentPeerContact()
|
self.interfaceInteraction?.presentPeerContact()
|
||||||
case .reportIrrelevantGeoLocation:
|
case .reportIrrelevantGeoLocation:
|
||||||
|
|||||||
@ -1044,12 +1044,12 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
|||||||
audioRecordingDotNode = currentAudioRecordingDotNode
|
audioRecordingDotNode = currentAudioRecordingDotNode
|
||||||
} else {
|
} else {
|
||||||
self.audioRecordingDotNode?.removeFromSupernode()
|
self.audioRecordingDotNode?.removeFromSupernode()
|
||||||
audioRecordingDotNode = AnimationNode(animation: "voicebin")
|
audioRecordingDotNode = AnimationNode(animation: "Bin")
|
||||||
self.audioRecordingDotNode = audioRecordingDotNode
|
self.audioRecordingDotNode = audioRecordingDotNode
|
||||||
self.addSubnode(audioRecordingDotNode)
|
self.addSubnode(audioRecordingDotNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
animateDotAppearing = transition.isAnimated && !hideInfo
|
animateDotAppearing = transition.isAnimated && !isLocked && !hideInfo
|
||||||
|
|
||||||
audioRecordingDotNode.frame = CGRect(origin: CGPoint(x: leftInset + 2.0 - UIScreenPixel, y: panelHeight - 44 + 1), size: CGSize(width: 40.0, height: 40))
|
audioRecordingDotNode.frame = CGRect(origin: CGPoint(x: leftInset + 2.0 - UIScreenPixel, y: panelHeight - 44 + 1), size: CGSize(width: 40.0, height: 40))
|
||||||
if animateDotAppearing {
|
if animateDotAppearing {
|
||||||
|
|||||||
@ -616,4 +616,8 @@ public final class MediaManagerImpl: NSObject, MediaManager {
|
|||||||
self.overlayMediaManager.controller?.addNode(node, customTransition: true)
|
self.overlayMediaManager.controller?.addNode(node, customTransition: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func hasOverlayVideoNode(_ node: OverlayMediaItemNode) -> Bool {
|
||||||
|
return self.currentOverlayVideoNode === node
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -416,7 +416,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
|
|||||||
}, openScheduledMessages: {
|
}, openScheduledMessages: {
|
||||||
}, openPeersNearby: {
|
}, openPeersNearby: {
|
||||||
}, displaySearchResultsTooltip: { _, _ in
|
}, displaySearchResultsTooltip: { _, _ in
|
||||||
}, statuses: nil)
|
}, unarchivePeer: {}, statuses: nil)
|
||||||
|
|
||||||
self.selectionPanel.interfaceInteraction = interfaceInteraction
|
self.selectionPanel.interfaceInteraction = interfaceInteraction
|
||||||
|
|
||||||
@ -4390,6 +4390,9 @@ public final class PeerInfoScreen: ViewController {
|
|||||||
if other.contentNode != nil {
|
if other.contentNode != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if let allowsCustomTransition = other.allowsCustomTransition, !allowsCustomTransition() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if let tag = other.userInfo as? PeerInfoNavigationSourceTag, tag.peerId == peerId {
|
if let tag = other.userInfo as? PeerInfoNavigationSourceTag, tag.peerId == peerId {
|
||||||
return PeerInfoNavigationTransitionNode(screenNode: strongSelf.controllerNode, presentationData: strongSelf.presentationData, headerNode: strongSelf.controllerNode.headerNode)
|
return PeerInfoNavigationTransitionNode(screenNode: strongSelf.controllerNode, presentationData: strongSelf.presentationData, headerNode: strongSelf.controllerNode.headerNode)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -550,7 +550,7 @@ public class PeerMediaCollectionController: TelegramBaseController {
|
|||||||
}, openScheduledMessages: {
|
}, openScheduledMessages: {
|
||||||
}, openPeersNearby: {
|
}, openPeersNearby: {
|
||||||
}, displaySearchResultsTooltip: { _, _ in
|
}, displaySearchResultsTooltip: { _, _ in
|
||||||
}, statuses: nil)
|
}, unarchivePeer: {}, statuses: nil)
|
||||||
|
|
||||||
self.updateInterfaceState(animated: false, { return $0 })
|
self.updateInterfaceState(animated: false, { return $0 })
|
||||||
|
|
||||||
|
|||||||
@ -289,10 +289,20 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
|
|||||||
applyLayout()
|
applyLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
self.imageNode.frame = CGRect(origin: CGPoint(), size: size)
|
transition.updateFrame(node: self.imageNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
self.playerNode.frame = CGRect(origin: CGPoint(), size: size).insetBy(dx: -1.0, dy: -1.0)
|
let fromFrame = self.playerNode.frame
|
||||||
|
let toFrame = CGRect(origin: CGPoint(), size: size).insetBy(dx: -1.0, dy: -1.0)
|
||||||
|
if case let .animated(duration, curve) = transition, fromFrame != toFrame, !fromFrame.width.isZero, !fromFrame.height.isZero, !toFrame.width.isZero, !toFrame.height.isZero {
|
||||||
|
self.playerNode.frame = toFrame
|
||||||
|
transition.animatePosition(node: self.playerNode, from: CGPoint(x: fromFrame.center.x - toFrame.center.x, y: fromFrame.center.y - toFrame.center.y))
|
||||||
|
|
||||||
|
let transform = CATransform3DScale(CATransform3DIdentity, fromFrame.width / toFrame.width, fromFrame.height / toFrame.height, 1.0)
|
||||||
|
self.playerNode.layer.animate(from: NSValue(caTransform3D: transform), to: NSValue(caTransform3D: CATransform3DIdentity), keyPath: "transform", timingFunction: curve.timingFunction, duration: duration)
|
||||||
|
} else {
|
||||||
|
transition.updateFrame(node: self.playerNode, frame: toFrame)
|
||||||
|
}
|
||||||
if let thumbnailNode = self.thumbnailNode {
|
if let thumbnailNode = self.thumbnailNode {
|
||||||
thumbnailNode.frame = CGRect(origin: CGPoint(), size: size).insetBy(dx: -1.0, dy: -1.0)
|
transition.updateFrame(node: thumbnailNode, frame: CGRect(origin: CGPoint(), size: size).insetBy(dx: -1.0, dy: -1.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -33,17 +33,19 @@ public final class OverlayUniversalVideoNode: OverlayMediaItemNode {
|
|||||||
private let defaultExpand: () -> Void
|
private let defaultExpand: () -> Void
|
||||||
public var customExpand: (() -> Void)?
|
public var customExpand: (() -> Void)?
|
||||||
public var customClose: (() -> Void)?
|
public var customClose: (() -> Void)?
|
||||||
|
public var controlsAreShowingUpdated: ((Bool) -> 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
|
self.defaultExpand = expand
|
||||||
|
|
||||||
var expandImpl: (() -> Void)?
|
var expandImpl: (() -> Void)?
|
||||||
|
var controlsAreShowingUpdatedImpl: ((Bool) -> Void)?
|
||||||
|
|
||||||
var unminimizeImpl: (() -> Void)?
|
var unminimizeImpl: (() -> Void)?
|
||||||
var togglePlayPauseImpl: (() -> Void)?
|
var togglePlayPauseImpl: (() -> Void)?
|
||||||
var closeImpl: (() -> Void)?
|
var closeImpl: (() -> Void)?
|
||||||
let decoration = OverlayVideoDecoration(unminimize: {
|
let decoration = OverlayVideoDecoration(contentDimensions: content.dimensions, unminimize: {
|
||||||
unminimizeImpl?()
|
unminimizeImpl?()
|
||||||
}, togglePlayPause: {
|
}, togglePlayPause: {
|
||||||
togglePlayPauseImpl?()
|
togglePlayPauseImpl?()
|
||||||
@ -51,6 +53,8 @@ public final class OverlayUniversalVideoNode: OverlayMediaItemNode {
|
|||||||
expandImpl?()
|
expandImpl?()
|
||||||
}, close: {
|
}, close: {
|
||||||
closeImpl?()
|
closeImpl?()
|
||||||
|
}, controlsAreShowingUpdated: { value in
|
||||||
|
controlsAreShowingUpdatedImpl?(value)
|
||||||
})
|
})
|
||||||
self.videoNode = UniversalVideoNode(postbox: postbox, audioSession: audioSession, manager: manager, decoration: decoration, content: content, priority: .overlay)
|
self.videoNode = UniversalVideoNode(postbox: postbox, audioSession: audioSession, manager: manager, decoration: decoration, content: content, priority: .overlay)
|
||||||
self.decoration = decoration
|
self.decoration = decoration
|
||||||
@ -58,14 +62,7 @@ public final class OverlayUniversalVideoNode: OverlayMediaItemNode {
|
|||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
expandImpl = { [weak self] in
|
expandImpl = { [weak self] in
|
||||||
guard let strongSelf = self else {
|
self?.expand()
|
||||||
return
|
|
||||||
}
|
|
||||||
if let customExpand = strongSelf.customExpand {
|
|
||||||
customExpand()
|
|
||||||
} else {
|
|
||||||
strongSelf.defaultExpand()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unminimizeImpl = { [weak self] in
|
unminimizeImpl = { [weak self] in
|
||||||
@ -91,6 +88,10 @@ public final class OverlayUniversalVideoNode: OverlayMediaItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
controlsAreShowingUpdatedImpl = { [weak self] value in
|
||||||
|
self?.controlsAreShowingUpdated?(value)
|
||||||
|
}
|
||||||
|
|
||||||
self.clipsToBounds = true
|
self.clipsToBounds = true
|
||||||
self.cornerRadius = 4.0
|
self.cornerRadius = 4.0
|
||||||
|
|
||||||
@ -104,7 +105,7 @@ public final class OverlayUniversalVideoNode: OverlayMediaItemNode {
|
|||||||
if previous != value {
|
if previous != value {
|
||||||
if !value {
|
if !value {
|
||||||
strongSelf.dismiss()
|
strongSelf.dismiss()
|
||||||
close()
|
closeImpl?()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,4 +156,12 @@ public final class OverlayUniversalVideoNode: OverlayMediaItemNode {
|
|||||||
public func showControls() {
|
public func showControls() {
|
||||||
self.decoration.showControls()
|
self.decoration.showControls()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func expand() {
|
||||||
|
if let customExpand = self.customExpand {
|
||||||
|
customExpand()
|
||||||
|
} else {
|
||||||
|
self.defaultExpand()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,11 +26,14 @@ private func setupArrowFrame(size: CGSize, edge: OverlayMediaItemMinimizationEdg
|
|||||||
private let backgroundImage = UIImage(bundleImageName: "Chat/Message/OverlayPlainVideoShadow")?.precomposed().resizableImage(withCapInsets: UIEdgeInsets(top: 22.0, left: 25.0, bottom: 26.0, right: 25.0), resizingMode: .stretch)
|
private let backgroundImage = UIImage(bundleImageName: "Chat/Message/OverlayPlainVideoShadow")?.precomposed().resizableImage(withCapInsets: UIEdgeInsets(top: 22.0, left: 25.0, bottom: 26.0, right: 25.0), resizingMode: .stretch)
|
||||||
|
|
||||||
final class OverlayVideoDecoration: UniversalVideoDecoration {
|
final class OverlayVideoDecoration: UniversalVideoDecoration {
|
||||||
|
private let contentDimensions: CGSize
|
||||||
|
|
||||||
let backgroundNode: ASDisplayNode?
|
let backgroundNode: ASDisplayNode?
|
||||||
let contentContainerNode: ASDisplayNode
|
let contentContainerNode: ASDisplayNode
|
||||||
let foregroundNode: ASDisplayNode?
|
let foregroundNode: ASDisplayNode?
|
||||||
|
|
||||||
private let unminimize: () -> Void
|
private let unminimize: () -> Void
|
||||||
|
private let controlsAreShowingUpdated: (Bool) -> Void
|
||||||
|
|
||||||
private let shadowNode: ASImageNode
|
private let shadowNode: ASImageNode
|
||||||
private let foregroundContainerNode: ASDisplayNode
|
private let foregroundContainerNode: ASDisplayNode
|
||||||
@ -46,8 +49,11 @@ final class OverlayVideoDecoration: UniversalVideoDecoration {
|
|||||||
|
|
||||||
private var validLayoutSize: CGSize?
|
private var validLayoutSize: CGSize?
|
||||||
|
|
||||||
init(unminimize: @escaping () -> Void, togglePlayPause: @escaping () -> Void, expand: @escaping () -> Void, close: @escaping () -> Void) {
|
init(contentDimensions: CGSize, unminimize: @escaping () -> Void, togglePlayPause: @escaping () -> Void, expand: @escaping () -> Void, close: @escaping () -> Void, controlsAreShowingUpdated: @escaping (Bool) -> Void) {
|
||||||
|
self.contentDimensions = contentDimensions
|
||||||
|
|
||||||
self.unminimize = unminimize
|
self.unminimize = unminimize
|
||||||
|
self.controlsAreShowingUpdated = controlsAreShowingUpdated
|
||||||
|
|
||||||
self.shadowNode = ASImageNode()
|
self.shadowNode = ASImageNode()
|
||||||
self.shadowNode.image = backgroundImage
|
self.shadowNode.image = backgroundImage
|
||||||
@ -78,6 +84,14 @@ final class OverlayVideoDecoration: UniversalVideoDecoration {
|
|||||||
self.statusDisposable.dispose()
|
self.statusDisposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func frameForContent(size: CGSize) -> CGRect {
|
||||||
|
if !self.contentDimensions.width.isZero && !self.contentDimensions.height.isZero {
|
||||||
|
let fittedSize = self.contentDimensions.aspectFittedWithOverflow(size, leeway: 10.0)
|
||||||
|
return CGRect(origin: CGPoint(x: floor(size.width - fittedSize.width) / 2.0, y: floor(size.height - fittedSize.height) / 2.0), size: fittedSize)
|
||||||
|
}
|
||||||
|
return CGRect(origin: CGPoint(), size: size)
|
||||||
|
}
|
||||||
|
|
||||||
func updateContentNode(_ contentNode: (UniversalVideoContentNode & ASDisplayNode)?) {
|
func updateContentNode(_ contentNode: (UniversalVideoContentNode & ASDisplayNode)?) {
|
||||||
if self.contentNode !== contentNode {
|
if self.contentNode !== contentNode {
|
||||||
let previous = self.contentNode
|
let previous = self.contentNode
|
||||||
@ -93,7 +107,7 @@ final class OverlayVideoDecoration: UniversalVideoDecoration {
|
|||||||
if contentNode.supernode !== self.contentContainerNode {
|
if contentNode.supernode !== self.contentContainerNode {
|
||||||
self.contentContainerNode.addSubnode(contentNode)
|
self.contentContainerNode.addSubnode(contentNode)
|
||||||
if let validLayoutSize = self.validLayoutSize {
|
if let validLayoutSize = self.validLayoutSize {
|
||||||
contentNode.frame = CGRect(origin: CGPoint(), size: validLayoutSize)
|
contentNode.frame = self.frameForContent(size: validLayoutSize)
|
||||||
contentNode.updateLayout(size: validLayoutSize, transition: .immediate)
|
contentNode.updateLayout(size: validLayoutSize, transition: .immediate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,6 +121,8 @@ final class OverlayVideoDecoration: UniversalVideoDecoration {
|
|||||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||||
self.validLayoutSize = size
|
self.validLayoutSize = size
|
||||||
|
|
||||||
|
let contentFrame = self.frameForContent(size: size)
|
||||||
|
|
||||||
let shadowInsets = UIEdgeInsets(top: 2.0, left: 3.0, bottom: 4.0, right: 3.0)
|
let shadowInsets = UIEdgeInsets(top: 2.0, left: 3.0, bottom: 4.0, right: 3.0)
|
||||||
transition.updateFrame(node: self.shadowNode, frame: CGRect(origin: CGPoint(x: -shadowInsets.left, y: -shadowInsets.top), size: CGSize(width: size.width + shadowInsets.left + shadowInsets.right, height: size.height + shadowInsets.top + shadowInsets.bottom)))
|
transition.updateFrame(node: self.shadowNode, frame: CGRect(origin: CGPoint(x: -shadowInsets.left, y: -shadowInsets.top), size: CGSize(width: size.width + shadowInsets.left + shadowInsets.right, height: size.height + shadowInsets.top + shadowInsets.bottom)))
|
||||||
|
|
||||||
@ -129,8 +145,8 @@ final class OverlayVideoDecoration: UniversalVideoDecoration {
|
|||||||
transition.updateFrame(node: self.statusNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - progressSize.width) / 2.0), y: floorToScreenPixels((size.height - progressSize.height) / 2.0)), size: progressSize))
|
transition.updateFrame(node: self.statusNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - progressSize.width) / 2.0), y: floorToScreenPixels((size.height - progressSize.height) / 2.0)), size: progressSize))
|
||||||
|
|
||||||
if let contentNode = self.contentNode {
|
if let contentNode = self.contentNode {
|
||||||
transition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(), size: size))
|
transition.updateFrame(node: contentNode, frame: contentFrame)
|
||||||
contentNode.updateLayout(size: size, transition: transition)
|
contentNode.updateLayout(size: contentFrame.size, transition: transition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,9 +157,11 @@ final class OverlayVideoDecoration: UniversalVideoDecoration {
|
|||||||
if self.controlsNode.alpha.isZero {
|
if self.controlsNode.alpha.isZero {
|
||||||
self.controlsNode.alpha = 1.0
|
self.controlsNode.alpha = 1.0
|
||||||
self.controlsNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
self.controlsNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
|
self.controlsAreShowingUpdated(true)
|
||||||
} else {
|
} else {
|
||||||
self.controlsNode.alpha = 0.0
|
self.controlsNode.alpha = 0.0
|
||||||
self.controlsNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
|
self.controlsNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
|
||||||
|
self.controlsAreShowingUpdated(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,6 +170,7 @@ final class OverlayVideoDecoration: UniversalVideoDecoration {
|
|||||||
if self.controlsNode.alpha.isZero {
|
if self.controlsNode.alpha.isZero {
|
||||||
self.controlsNode.alpha = 1.0
|
self.controlsNode.alpha = 1.0
|
||||||
self.controlsNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
self.controlsNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
|
self.controlsAreShowingUpdated(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@ -449,12 +449,12 @@ public final class WalletStrings: Equatable {
|
|||||||
public var Wallet_SecureStorageReset_Title: String { return self._s[219]! }
|
public var Wallet_SecureStorageReset_Title: String { return self._s[219]! }
|
||||||
public var Wallet_Receive_CommentHeader: String { return self._s[220]! }
|
public var Wallet_Receive_CommentHeader: String { return self._s[220]! }
|
||||||
public var Wallet_Info_ReceiveGrams: String { return self._s[221]! }
|
public var Wallet_Info_ReceiveGrams: String { return self._s[221]! }
|
||||||
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
|
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
|
||||||
let form = getPluralizationForm(self.lc, value)
|
let form = getPluralizationForm(self.lc, value)
|
||||||
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
||||||
return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, stringValue)
|
return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, stringValue)
|
||||||
}
|
}
|
||||||
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
|
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
|
||||||
let form = getPluralizationForm(self.lc, value)
|
let form = getPluralizationForm(self.lc, value)
|
||||||
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
||||||
return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue)
|
return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user