mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-15 18:59:54 +00:00
Enabled streaming for GIFs and round videos
Play video in fullscreen on switching to landscape Use autodownload settings in IV
This commit is contained in:
parent
df83998c74
commit
8c860dc9aa
@ -426,8 +426,8 @@ func autodownloadMediaCategoryController(context: AccountContext, connectionType
|
||||
}
|
||||
|
||||
let controller = ItemListController(context: context, state: signal)
|
||||
controller.willDisappear = { _ in
|
||||
let _ = (combineLatest(initialValuePromise.get(), currentAutodownloadSettings())
|
||||
controller.didDisappear = { _ in
|
||||
let _ = (combineLatest(initialValuePromise.get() |> take(1), currentAutodownloadSettings())
|
||||
|> mapToSignal { initialValue, currentValue -> Signal<Void, NoError> in
|
||||
let initialConnection = initialValue.connectionSettings(for: connectionType.automaticDownloadNetworkType)
|
||||
let currentConnection = currentValue.connectionSettings(for: connectionType.automaticDownloadNetworkType)
|
||||
|
||||
@ -147,3 +147,27 @@ final class CachedPatternWallpaperRepresentation: CachedMediaResourceRepresentat
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class CachedAlbumArtworkRepresentation: CachedMediaResourceRepresentation {
|
||||
let size: CGSize?
|
||||
|
||||
var uniqueId: String {
|
||||
if let size = self.size {
|
||||
return "album-artwork-\(Int(size.width))x\(Int(size.height))"
|
||||
} else {
|
||||
return "album-artwork"
|
||||
}
|
||||
}
|
||||
|
||||
init(size: CGSize) {
|
||||
self.size = size
|
||||
}
|
||||
|
||||
func isEqual(to: CachedMediaResourceRepresentation) -> Bool {
|
||||
if let to = to as? CachedAlbumArtworkRepresentation {
|
||||
return self.size == to.size
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -324,7 +324,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
}
|
||||
}
|
||||
|
||||
return openChatMessage(context: context, message: message, standalone: false, reverseMessageGalleryOrder: false, stream: mode == .stream, fromPlayingVideo: mode == .automaticPlayback, navigationController: strongSelf.navigationController as? NavigationController, dismissInput: {
|
||||
return openChatMessage(context: context, message: message, standalone: false, reverseMessageGalleryOrder: false, mode: mode, navigationController: strongSelf.navigationController as? NavigationController, dismissInput: {
|
||||
self?.chatDisplayNode.dismissInput()
|
||||
}, present: { c, a in
|
||||
self?.present(c, in: .window(.root), with: a, blockInteraction: true)
|
||||
@ -699,9 +699,9 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
}
|
||||
strongSelf.sendMessages([.message(text: command, attributes: attributes, mediaReference: nil, replyToMessageId: (postAsReply && messageId != nil) ? messageId! : nil, localGroupingKey: nil)])
|
||||
}
|
||||
}, openInstantPage: { [weak self] message in
|
||||
}, openInstantPage: { [weak self] message, associatedData in
|
||||
if let strongSelf = self, strongSelf.isNodeLoaded, let navigationController = strongSelf.navigationController as? NavigationController, let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) {
|
||||
openChatInstantPage(context: strongSelf.context, message: message, navigationController: navigationController)
|
||||
openChatInstantPage(context: strongSelf.context, message: message, sourcePeerType: associatedData?.automaticDownloadPeerType, navigationController: navigationController)
|
||||
}
|
||||
}, openWallpaper: { [weak self] message in
|
||||
if let strongSelf = self, strongSelf.isNodeLoaded, let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) {
|
||||
@ -3414,12 +3414,21 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
var shouldOpenCurrentlyActiveVideo = false
|
||||
if let previousLayout = self.validLayout, previousLayout.size.width < previousLayout.size.height && previousLayout.size.height == layout.size.width && self.traceVisibility() && isTopmostChatController(self) {
|
||||
shouldOpenCurrentlyActiveVideo = true
|
||||
}
|
||||
|
||||
self.validLayout = layout
|
||||
self.chatTitleView?.layout = layout
|
||||
|
||||
self.chatDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop in
|
||||
self.chatDisplayNode.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop)
|
||||
})
|
||||
|
||||
if shouldOpenCurrentlyActiveVideo {
|
||||
self.chatDisplayNode.openCurrentPlayingWithSoundMedia()
|
||||
}
|
||||
}
|
||||
|
||||
func updateChatPresentationInterfaceState(animated: Bool = true, interactive: Bool, saveInterfaceState: Bool = false, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState) {
|
||||
|
||||
@ -41,7 +41,7 @@ public enum ChatControllerInteractionOpenMessageMode {
|
||||
case `default`
|
||||
case stream
|
||||
case automaticPlayback
|
||||
case playWithSound
|
||||
case landscape
|
||||
}
|
||||
|
||||
struct ChatInterfacePollActionState: Equatable {
|
||||
@ -65,7 +65,7 @@ public final class ChatControllerInteraction {
|
||||
let shareCurrentLocation: () -> Void
|
||||
let shareAccountContact: () -> Void
|
||||
let sendBotCommand: (MessageId?, String) -> Void
|
||||
let openInstantPage: (Message) -> Void
|
||||
let openInstantPage: (Message, ChatMessageItemAssociatedData?) -> Void
|
||||
let openWallpaper: (Message) -> Void
|
||||
let openHashtag: (String?, String) -> Void
|
||||
let updateInputState: ((ChatTextInputState) -> ChatTextInputState) -> Void
|
||||
@ -98,7 +98,7 @@ public final class ChatControllerInteraction {
|
||||
var pollActionState: ChatInterfacePollActionState
|
||||
var searchTextHighightState: String?
|
||||
|
||||
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool) -> Void, sendGif: @escaping (FileMediaReference) -> Void, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message) -> Void, openWallpaper: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOption: @escaping (MessageId, Data) -> Void, openAppStorePage: @escaping () -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState) {
|
||||
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool) -> Void, sendGif: @escaping (FileMediaReference) -> Void, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOption: @escaping (MessageId, Data) -> Void, openAppStorePage: @escaping () -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState) {
|
||||
self.openMessage = openMessage
|
||||
self.openPeer = openPeer
|
||||
self.openPeerMention = openPeerMention
|
||||
@ -147,7 +147,7 @@ public final class ChatControllerInteraction {
|
||||
|
||||
static var `default`: ChatControllerInteraction {
|
||||
return ChatControllerInteraction(openMessage: { _, _ in
|
||||
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _ in }, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendMessage: { _ in }, sendSticker: { _, _ in }, sendGif: { _ in }, requestMessageActionCallback: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _ in }, openWallpaper: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _ in }, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendMessage: { _ in }, sendSticker: { _, _ in }, sendGif: { _ in }, requestMessageActionCallback: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||
}, presentController: { _, _ in }, navigationController: {
|
||||
return nil
|
||||
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _ in }, longTap: { _ in }, openCheckoutOrReceipt: { _ in }, openSearch: { }, setupReply: { _ in
|
||||
|
||||
@ -413,7 +413,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
if display {
|
||||
var nodes: [(CGFloat, ChatMessageItemView, ASDisplayNode)] = []
|
||||
strongSelf.historyNode.forEachVisibleItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView, let (_, isVideoMessage, _, badgeNode) = itemNode.playMediaWithSound(), let node = badgeNode {
|
||||
if let itemNode = itemNode as? ChatMessageItemView, let (_, _, isVideoMessage, _, badgeNode) = itemNode.playMediaWithSound(), let node = badgeNode {
|
||||
if !isVideoMessage, case let .visible(fraction) = itemNode.visibility {
|
||||
nodes.insert((fraction, itemNode, node), at: 0)
|
||||
}
|
||||
@ -566,6 +566,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.validLayout = (layout, navigationBarHeight)
|
||||
|
||||
let cleanInsets = layout.intrinsicInsets
|
||||
@ -1369,7 +1370,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
if let textInputPanelNode = self.textInputPanelNode, updateInputTextState {
|
||||
textInputPanelNode.updateInputTextState(chatPresentationInterfaceState.interfaceState.effectiveInputState, keepSendButtonEnabled: keepSendButtonEnabled, extendedSearchLayout: extendedSearchLayout, animated: transition.isAnimated)
|
||||
} else {
|
||||
textInputPanelNode?.updateKeepSendButtonEnabled(keepSendButtonEnabled: keepSendButtonEnabled, extendedSearchLayout: extendedSearchLayout, animated: transition.isAnimated)
|
||||
self.textInputPanelNode?.updateKeepSendButtonEnabled(keepSendButtonEnabled: keepSendButtonEnabled, extendedSearchLayout: extendedSearchLayout, animated: transition.isAnimated)
|
||||
}
|
||||
|
||||
var restrictionText: String?
|
||||
@ -1483,23 +1484,42 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
|
||||
func playFirstMediaWithSound() {
|
||||
var actions: [(CGFloat, () -> Void)] = []
|
||||
var actions: [(CGFloat, Bool, () -> Void)] = []
|
||||
var hasUnconsumed = false
|
||||
self.historyNode.forEachVisibleItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView, let (action, isVideoMessage, isUnconsumed, _) = itemNode.playMediaWithSound() {
|
||||
if let itemNode = itemNode as? ChatMessageItemView, let (action, _, _, isUnconsumed, _) = itemNode.playMediaWithSound() {
|
||||
if case let .visible(fraction) = itemNode.visibility {
|
||||
actions.insert((fraction, action), at: 0)
|
||||
hasUnconsumed = isUnconsumed
|
||||
actions.insert((fraction, isUnconsumed, action), at: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
for (fraction, action) in actions {
|
||||
if fraction > 0.7 {
|
||||
for (fraction, isUnconsumed, action) in actions {
|
||||
if fraction > 0.7 && (!hasUnconsumed || isUnconsumed) {
|
||||
action()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func openCurrentPlayingWithSoundMedia() {
|
||||
var result: (Message?, ListViewItemNode)?
|
||||
self.historyNode.forEachVisibleItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView, let (_, soundEnabled, _, _, _) = itemNode.playMediaWithSound(), soundEnabled {
|
||||
if case let .visible(fraction) = itemNode.visibility, fraction > 0.7 {
|
||||
result = (itemNode.item?.message, itemNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
if let (message, itemNode) = result {
|
||||
if let message = message {
|
||||
let _ = self.controllerInteraction.openMessage(message, .landscape)
|
||||
}
|
||||
|
||||
self.historyNode.ensureItemNodeVisibleAtTopInset(itemNode)
|
||||
}
|
||||
}
|
||||
|
||||
var isInputViewFocused: Bool {
|
||||
if let inputPanelNode = self.inputPanelNode as? ChatTextInputPanelNode {
|
||||
return inputPanelNode.isFocused
|
||||
|
||||
@ -1239,6 +1239,13 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
override func touchesToOtherItemsPrevented() {
|
||||
super.touchesToOtherItemsPrevented()
|
||||
if let item = self.item {
|
||||
item.interaction.setPeerIdWithRevealedOptions(nil, nil)
|
||||
}
|
||||
}
|
||||
|
||||
override func revealOptionsInteractivelyOpened() {
|
||||
if let item = self.item {
|
||||
item.interaction.setPeerIdWithRevealedOptions(item.index.messageIndex.id.peerId, nil)
|
||||
|
||||
@ -418,7 +418,7 @@ final class ChatListNode: ListView {
|
||||
}, setPeerIdWithRevealedOptions: { [weak self] peerId, fromPeerId in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateState { state in
|
||||
if (peerId == nil && fromPeerId == state.peerIdWithRevealedOptions) || (peerId != nil && fromPeerId == nil) {
|
||||
if (peerId == nil && fromPeerId == state.peerIdWithRevealedOptions) || (peerId != nil && fromPeerId == nil) || (peerId == nil && fromPeerId == nil) {
|
||||
var state = state
|
||||
state.peerIdWithRevealedOptions = peerId
|
||||
return state
|
||||
|
||||
@ -1013,7 +1013,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
func playMediaWithSound() -> (() -> Void, Bool, Bool, ASDisplayNode?)? {
|
||||
func playMediaWithSound() -> (() -> Void, Bool, Bool, Bool, ASDisplayNode?)? {
|
||||
return self.contentImageNode?.playMediaWithSound()
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,7 +147,7 @@ class ChatMessageBubbleContentNode: ASDisplayNode {
|
||||
func updateAutomaticMediaDownloadSettings(_ settings: MediaAutoDownloadSettings) {
|
||||
}
|
||||
|
||||
func playMediaWithSound() -> (() -> Void, Bool, Bool, ASDisplayNode?)? {
|
||||
func playMediaWithSound() -> (() -> Void, Bool, Bool, Bool, ASDisplayNode?)? {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -1622,7 +1622,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
case let .botCommand(command):
|
||||
foundTapAction = true
|
||||
if let item = self.item {
|
||||
item.controllerInteraction.sendBotCommand(item.message.id, command)
|
||||
item.controllerInteraction.sendBotCommand(item.message.id, command)
|
||||
}
|
||||
break loop
|
||||
case let .hashtag(peerName, hashtag):
|
||||
@ -1632,7 +1632,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
case .instantPage:
|
||||
foundTapAction = true
|
||||
if let item = self.item {
|
||||
item.controllerInteraction.openInstantPage(item.message)
|
||||
item.controllerInteraction.openInstantPage(item.message, item.associatedData)
|
||||
}
|
||||
break loop
|
||||
case .wallpaper:
|
||||
@ -1781,7 +1781,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
override func transitionNode(id: MessageId, media: Media) -> (ASDisplayNode, () -> (UIView?, UIView?))? {
|
||||
for contentNode in self.contentNodes {
|
||||
if let result = contentNode.transitionNode(messageId: id, media: media) {
|
||||
if self.contentNodes.count == 1 && self.nameNode == nil && self.adminBadgeNode == nil && self.forwardInfoNode == nil && self.replyInfoNode == nil {
|
||||
if self.contentNodes.count == 1 && self.contentNodes.first is ChatMessageMediaBubbleContentNode && self.nameNode == nil && self.adminBadgeNode == nil && self.forwardInfoNode == nil && self.replyInfoNode == nil {
|
||||
return (result.0, { [weak self] in
|
||||
guard let strongSelf = self, let resultView = result.1().0 else {
|
||||
return (nil, nil)
|
||||
@ -1856,7 +1856,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
}
|
||||
|
||||
|
||||
override func playMediaWithSound() -> (() -> Void, Bool, Bool, ASDisplayNode?)? {
|
||||
override func playMediaWithSound() -> (() -> Void, Bool, Bool, Bool, ASDisplayNode?)? {
|
||||
for contentNode in self.contentNodes {
|
||||
if let playMediaWithSound = contentNode.playMediaWithSound() {
|
||||
return playMediaWithSound
|
||||
|
||||
@ -695,7 +695,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
|
||||
override func playMediaWithSound() -> (() -> Void, Bool, Bool, ASDisplayNode?)? {
|
||||
override func playMediaWithSound() -> (() -> Void, Bool, Bool, Bool, ASDisplayNode?)? {
|
||||
return self.interactiveVideoNode.playMediaWithSound()
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,7 +370,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
}), content: NativeVideoContent(id: .message(item.message.id, item.message.stableId, telegramFile.fileId), fileReference: .message(message: MessageReference(item.message), media: telegramFile), streamVideo: false, enableSound: false, fetchAutomatically: false), priority: .embedded, autoplay: true)
|
||||
}), content: NativeVideoContent(id: .message(item.message.id, item.message.stableId, telegramFile.fileId), fileReference: .message(message: MessageReference(item.message), media: telegramFile), streamVideo: true, enableSound: false, fetchAutomatically: false), priority: .embedded, autoplay: true)
|
||||
let previousVideoNode = strongSelf.videoNode
|
||||
strongSelf.videoNode = videoNode
|
||||
strongSelf.insertSubnode(videoNode, belowSubnode: previousVideoNode ?? strongSelf.dateAndStatusNode)
|
||||
@ -453,7 +453,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
case .Local:
|
||||
displayMute = true
|
||||
default:
|
||||
displayMute = false
|
||||
displayMute = self.automaticDownload ?? false
|
||||
}
|
||||
case .playbackStatus:
|
||||
displayMute = false
|
||||
@ -483,6 +483,10 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
if self.automaticDownload ?? false {
|
||||
progressRequired = false
|
||||
}
|
||||
|
||||
if progressRequired {
|
||||
if self.statusNode == nil {
|
||||
let statusNode = RadialStatusNode(backgroundNodeColor: item.presentationData.theme.theme.chat.bubble.mediaOverlayControlBackgroundColor)
|
||||
@ -688,8 +692,18 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
func playMediaWithSound() -> (() -> Void, Bool, Bool, ASDisplayNode?)? {
|
||||
func playMediaWithSound() -> (action: () -> Void, soundEnabled: Bool, isVideoMessage: Bool, isUnread: Bool, badgeNode: ASDisplayNode?)? {
|
||||
if let item = self.item {
|
||||
var notConsumed = false
|
||||
for attribute in item.message.attributes {
|
||||
if let attribute = attribute as? ConsumableContentMessageAttribute {
|
||||
if !attribute.consumed {
|
||||
notConsumed = true
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return ({
|
||||
if !self.infoBackgroundNode.alpha.isZero {
|
||||
let _ = (item.context.sharedContext.mediaManager.globalMediaPlayerState
|
||||
@ -711,7 +725,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
}
|
||||
})
|
||||
}
|
||||
}, true, false, nil)
|
||||
}, false, true, !notConsumed, nil)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -594,7 +594,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
strongSelf.videoNodeDecoration = decoration
|
||||
let mediaManager = context.sharedContext.mediaManager
|
||||
|
||||
let streamVideo = !updatedVideoFile.isAnimated && isMediaStreamable(message: message, media: updatedVideoFile)
|
||||
let streamVideo = isMediaStreamable(message: message, media: updatedVideoFile)
|
||||
let videoNode = UniversalVideoNode(postbox: context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: decoration, content: NativeVideoContent(id: .message(message.id, message.stableId, updatedVideoFile.fileId), fileReference: .message(message: MessageReference(message), media: updatedVideoFile), streamVideo: streamVideo, enableSound: false, fetchAutomatically: false, onlyFullSizeThumbnail: (onlyFullSizeVideoThumbnail ?? false), continuePlayingWithoutSoundOnLostAudioSession: isInlinePlayableVideo, placeholderColor: emptyColor), priority: .embedded)
|
||||
videoNode.isUserInteractionEnabled = false
|
||||
videoNode.ownsContentNodeUpdated = { [weak self] owns in
|
||||
@ -897,6 +897,10 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
active = true
|
||||
}
|
||||
|
||||
if let file = self.media as? TelegramMediaFile, file.isAnimated {
|
||||
muted = false
|
||||
}
|
||||
|
||||
if message.flags.contains(.Unsent) {
|
||||
automaticPlayback = false
|
||||
}
|
||||
@ -918,11 +922,11 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
state = .progress(color: bubbleTheme.mediaOverlayControlForegroundColor, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true)
|
||||
}
|
||||
|
||||
if let file = self.media as? TelegramMediaFile, (!file.isAnimated || message.flags.contains(.Unsent)) {
|
||||
if let file = self.media as? TelegramMediaFile {
|
||||
if wideLayout {
|
||||
if let size = file.size {
|
||||
if let duration = file.duration, !message.flags.contains(.Unsent) {
|
||||
let durationString = stringForDuration(playerDuration > 0 ? playerDuration : duration, position: playerPosition)
|
||||
let durationString = file.isAnimated ? "GIF" : stringForDuration(playerDuration > 0 ? playerDuration : duration, position: playerPosition)
|
||||
let sizeString = "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true)) / \(dataSizeString(size, forceDecimal: true))"
|
||||
if isMediaStreamable(message: message, media: file) {
|
||||
badgeContent = .mediaDownload(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, duration: durationString, size: active ? sizeString : nil, muted: muted, active: active)
|
||||
@ -1016,14 +1020,14 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
state = .play(bubbleTheme.mediaOverlayControlForegroundColor)
|
||||
}
|
||||
}
|
||||
if let file = media as? TelegramMediaFile, let duration = file.duration, !file.isAnimated {
|
||||
let durationString = stringForDuration(playerDuration > 0 ? playerDuration : duration, position: playerPosition)
|
||||
if let file = media as? TelegramMediaFile, let duration = file.duration {
|
||||
let durationString = file.isAnimated ? "GIF" : stringForDuration(playerDuration > 0 ? playerDuration : duration, position: playerPosition)
|
||||
badgeContent = .mediaDownload(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, duration: durationString, size: nil, muted: muted, active: false)
|
||||
}
|
||||
case .Remote:
|
||||
state = .download(bubbleTheme.mediaOverlayControlForegroundColor)
|
||||
if let file = self.media as? TelegramMediaFile, !file.isAnimated {
|
||||
let durationString = stringForDuration(playerDuration > 0 ? playerDuration : (file.duration ?? 0), position: playerPosition)
|
||||
if let file = self.media as? TelegramMediaFile {
|
||||
let durationString = file.isAnimated ? "GIF" : stringForDuration(playerDuration > 0 ? playerDuration : (file.duration ?? 0), position: playerPosition)
|
||||
if wideLayout {
|
||||
if isMediaStreamable(message: message, media: file) {
|
||||
state = automaticPlayback ? .none : .play(bubbleTheme.mediaOverlayControlForegroundColor)
|
||||
@ -1201,12 +1205,17 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
})
|
||||
}
|
||||
|
||||
func playMediaWithSound() -> (() -> Void, Bool, Bool, ASDisplayNode?)? {
|
||||
func playMediaWithSound() -> (action: () -> Void, soundEnabled: Bool, isVideoMessage: Bool, isUnread: Bool, badgeNode: ASDisplayNode?)? {
|
||||
var isAnimated = false
|
||||
if let file = self.media as? TelegramMediaFile, file.isAnimated {
|
||||
isAnimated = true
|
||||
}
|
||||
|
||||
if let videoNode = self.videoNode, let context = self.context, (self.automaticPlayback ?? false) && !isAnimated {
|
||||
var isHorizontal = false
|
||||
if let file = self.media as? TelegramMediaFile, let dimensions = file.dimensions {
|
||||
isHorizontal = dimensions.width >= dimensions.height
|
||||
}
|
||||
return ({
|
||||
let _ = (context.sharedContext.mediaManager.globalMediaPlayerState
|
||||
|> take(1)
|
||||
@ -1226,7 +1235,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
videoNode.playOnceWithSound(playAndRecord: false, seekToStart: .none)
|
||||
}
|
||||
})
|
||||
}, false, false, self.badgeNode)
|
||||
}, (self.playerStatus?.soundEnabled ?? false) && isHorizontal, false, false, self.badgeNode)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -235,7 +235,7 @@ public class ChatMessageItemView: ListViewItemNode {
|
||||
func updateAutomaticMediaDownloadSettings() {
|
||||
}
|
||||
|
||||
func playMediaWithSound() -> (() -> Void, Bool, Bool, ASDisplayNode?)? {
|
||||
func playMediaWithSound() -> (() -> Void, Bool, Bool, Bool, ASDisplayNode?)? {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -316,7 +316,7 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
return mediaHidden
|
||||
}
|
||||
|
||||
override func playMediaWithSound() -> (() -> Void, Bool, Bool, ASDisplayNode?)? {
|
||||
override func playMediaWithSound() -> (() -> Void, Bool, Bool, Bool, ASDisplayNode?)? {
|
||||
return self.interactiveImageNode.playMediaWithSound()
|
||||
}
|
||||
|
||||
|
||||
@ -133,7 +133,7 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
break
|
||||
}
|
||||
if !isGallery {
|
||||
item.controllerInteraction.openInstantPage(item.message)
|
||||
item.controllerInteraction.openInstantPage(item.message, item.associatedData)
|
||||
return
|
||||
}
|
||||
} else if content.type == "telegram_background" {
|
||||
@ -369,7 +369,7 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||
}
|
||||
|
||||
override func playMediaWithSound() -> (() -> Void, Bool, Bool, ASDisplayNode?)? {
|
||||
override func playMediaWithSound() -> (() -> Void, Bool, Bool, Bool, ASDisplayNode?)? {
|
||||
return self.contentNode.playMediaWithSound()
|
||||
}
|
||||
|
||||
|
||||
@ -180,9 +180,9 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
self?.openMessageContextMenu(message: message, selectAll: selectAll, node: node, frame: frame)
|
||||
}, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendMessage: { _ in }, sendSticker: { _, _ in }, sendGif: { _ in }, requestMessageActionCallback: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { [weak self] url, _, _ in
|
||||
self?.openUrl(url)
|
||||
}, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { [weak self] message in
|
||||
}, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { [weak self] message, associatedData in
|
||||
if let strongSelf = self, let navigationController = strongSelf.getNavigationController() {
|
||||
openChatInstantPage(context: strongSelf.context, message: message, navigationController: navigationController)
|
||||
openChatInstantPage(context: strongSelf.context, message: message, sourcePeerType: associatedData?.automaticDownloadPeerType, navigationController: navigationController)
|
||||
}
|
||||
}, openWallpaper: { [weak self] message in
|
||||
if let strongSelf = self{
|
||||
@ -756,7 +756,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
case let .stickerPack(name):
|
||||
strongSelf.presentController(StickerPackPreviewController(context: strongSelf.context, stickerPack: .name(name), parentNavigationController: strongSelf.getNavigationController()), nil)
|
||||
case let .instantView(webpage, anchor):
|
||||
strongSelf.pushController(InstantPageController(context: strongSelf.context, webPage: webpage, anchor: anchor))
|
||||
strongSelf.pushController(InstantPageController(context: strongSelf.context, webPage: webpage, sourcePeerType: .channel, anchor: anchor))
|
||||
case let .join(link):
|
||||
strongSelf.presentController(JoinLinkPreviewController(context: strongSelf.context, link: link, navigateToPeer: { peerId in
|
||||
if let strongSelf = self {
|
||||
|
||||
@ -8,27 +8,6 @@ import Display
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
|
||||
private func videoFirstFrameData(account: Account, resource: MediaResource, chunkSize: Int) -> Signal<CachedMediaResourceRepresentationResult, NoError> {
|
||||
if let size = resource.size {
|
||||
return account.postbox.mediaBox.resourceData(resource, size: size, in: 0 ..< min(size, chunkSize))
|
||||
|> mapToSignal { _ -> Signal<CachedMediaResourceRepresentationResult, NoError> in
|
||||
return account.postbox.mediaBox.resourceData(resource, option: .incremental(waitUntilFetchStatus: false), attemptSynchronously: false)
|
||||
|> mapToSignal { data -> Signal<CachedMediaResourceRepresentationResult, NoError> in
|
||||
return fetchCachedVideoFirstFrameRepresentation(account: account, resource: resource, resourceData: data)
|
||||
|> `catch` { _ -> Signal<CachedMediaResourceRepresentationResult, NoError> in
|
||||
if chunkSize > size {
|
||||
return .complete()
|
||||
} else {
|
||||
return videoFirstFrameData(account: account, resource: resource, chunkSize: chunkSize + chunkSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|
||||
public func fetchCachedResourceRepresentation(account: Account, resource: MediaResource, representation: CachedMediaResourceRepresentation) -> Signal<CachedMediaResourceRepresentationResult, NoError> {
|
||||
if let representation = representation as? CachedStickerAJpegRepresentation {
|
||||
return account.postbox.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false))
|
||||
@ -96,6 +75,27 @@ public func fetchCachedResourceRepresentation(account: Account, resource: MediaR
|
||||
return .never()
|
||||
}
|
||||
|
||||
private func videoFirstFrameData(account: Account, resource: MediaResource, chunkSize: Int) -> Signal<CachedMediaResourceRepresentationResult, NoError> {
|
||||
if let size = resource.size {
|
||||
return account.postbox.mediaBox.resourceData(resource, size: size, in: 0 ..< min(size, chunkSize))
|
||||
|> mapToSignal { _ -> Signal<CachedMediaResourceRepresentationResult, NoError> in
|
||||
return account.postbox.mediaBox.resourceData(resource, option: .incremental(waitUntilFetchStatus: false), attemptSynchronously: false)
|
||||
|> mapToSignal { data -> Signal<CachedMediaResourceRepresentationResult, NoError> in
|
||||
return fetchCachedVideoFirstFrameRepresentation(account: account, resource: resource, resourceData: data)
|
||||
|> `catch` { _ -> Signal<CachedMediaResourceRepresentationResult, NoError> in
|
||||
if chunkSize > size {
|
||||
return .complete()
|
||||
} else {
|
||||
return videoFirstFrameData(account: account, resource: resource, chunkSize: chunkSize + chunkSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|
||||
private func fetchCachedStickerAJpegRepresentation(account: Account, resource: MediaResource, resourceData: MediaResourceData, representation: CachedStickerAJpegRepresentation) -> Signal<CachedMediaResourceRepresentationResult, NoError> {
|
||||
return Signal({ subscriber in
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) {
|
||||
@ -275,8 +275,6 @@ private func fetchCachedVideoFirstFrameRepresentation(account: Account, resource
|
||||
}
|
||||
|
||||
let _ = try? FileManager.default.removeItem(atPath: tempFilePath)
|
||||
subscriber.putNext(CachedMediaResourceRepresentationResult(temporaryPath: path))
|
||||
subscriber.putCompletion()
|
||||
} catch (let _) {
|
||||
let _ = try? FileManager.default.removeItem(atPath: tempFilePath)
|
||||
subscriber.putError(.generic)
|
||||
|
||||
@ -131,7 +131,7 @@ private func galleryMessageCaptionText(_ message: Message) -> String {
|
||||
return message.text
|
||||
}
|
||||
|
||||
func galleryItemForEntry(context: AccountContext, presentationData: PresentationData, entry: MessageHistoryEntry, isCentral: Bool = false, streamVideos: Bool, loopVideos: Bool = false, hideControls: Bool = false, fromPlayingVideo: Bool = false, tempFilePath: String? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }, openActionOptions: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }) -> GalleryItem? {
|
||||
func galleryItemForEntry(context: AccountContext, presentationData: PresentationData, entry: MessageHistoryEntry, isCentral: Bool = false, streamVideos: Bool, loopVideos: Bool = false, hideControls: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, tempFilePath: String? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }, openActionOptions: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }) -> GalleryItem? {
|
||||
switch entry {
|
||||
case let .MessageEntry(message, _, location, _, _):
|
||||
if let (media, mediaImage) = mediaForMessage(message: message) {
|
||||
@ -158,7 +158,7 @@ func galleryItemForEntry(context: AccountContext, presentationData: Presentation
|
||||
}
|
||||
}
|
||||
let caption = galleryCaptionStringWithAppliedEntities(galleryMessageCaptionText(message), entities: entities)
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: GalleryItemOriginData(title: message.author?.displayTitle, timestamp: message.timestamp), indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: caption, hideControls: hideControls, fromPlayingVideo: fromPlayingVideo, playbackCompleted: playbackCompleted, performAction: performAction, openActionOptions: openActionOptions)
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: GalleryItemOriginData(title: message.author?.displayTitle, timestamp: message.timestamp), indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: caption, hideControls: hideControls, fromPlayingVideo: fromPlayingVideo, landscape: landscape, playbackCompleted: playbackCompleted, performAction: performAction, openActionOptions: openActionOptions)
|
||||
} else {
|
||||
if file.mimeType.hasPrefix("image/") && file.mimeType != "image/gif" {
|
||||
var pixelsCount: Int = 0
|
||||
@ -179,12 +179,12 @@ func galleryItemForEntry(context: AccountContext, presentationData: Presentation
|
||||
} else if let webpage = media as? TelegramMediaWebpage, case let .Loaded(webpageContent) = webpage.content {
|
||||
switch websiteType(of: webpageContent) {
|
||||
case .instagram where webpageContent.file != nil && webpageContent.image != nil && webpageContent.file!.isVideo:
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: NativeVideoContent(id: .message(message.id, message.stableId, webpageContent.file?.id ?? webpage.webpageId), fileReference: .message(message: MessageReference(message), media: webpageContent.file!), imageReference: webpageContent.image.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: true, enableSound: true), originData: GalleryItemOriginData(title: message.author?.displayTitle, timestamp: message.timestamp), indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: NSAttributedString(string: ""), fromPlayingVideo: fromPlayingVideo, performAction: performAction, openActionOptions: openActionOptions)
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: NativeVideoContent(id: .message(message.id, message.stableId, webpageContent.file?.id ?? webpage.webpageId), fileReference: .message(message: MessageReference(message), media: webpageContent.file!), imageReference: webpageContent.image.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: true, enableSound: true), originData: GalleryItemOriginData(title: message.author?.displayTitle, timestamp: message.timestamp), indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: NSAttributedString(string: ""), fromPlayingVideo: fromPlayingVideo, landscape: landscape, performAction: performAction, openActionOptions: openActionOptions)
|
||||
default:
|
||||
if let embedUrl = webpageContent.embedUrl, let image = webpageContent.image, URL(string: embedUrl)?.pathExtension == "mp4" {
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: SystemVideoContent(url: embedUrl, imageReference: .webPage(webPage: WebpageReference(webpage), media: image), dimensions: webpageContent.embedSize ?? CGSize(width: 640.0, height: 640.0), duration: Int32(webpageContent.duration ?? 0)), originData: GalleryItemOriginData(title: message.author?.displayTitle, timestamp: message.timestamp), indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: NSAttributedString(string: ""), fromPlayingVideo: fromPlayingVideo, performAction: performAction, openActionOptions: openActionOptions)
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: SystemVideoContent(url: embedUrl, imageReference: .webPage(webPage: WebpageReference(webpage), media: image), dimensions: webpageContent.embedSize ?? CGSize(width: 640.0, height: 640.0), duration: Int32(webpageContent.duration ?? 0)), originData: GalleryItemOriginData(title: message.author?.displayTitle, timestamp: message.timestamp), indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: NSAttributedString(string: ""), fromPlayingVideo: fromPlayingVideo, landscape: landscape, performAction: performAction, openActionOptions: openActionOptions)
|
||||
} else if let content = WebEmbedVideoContent(webPage: webpage, webpageContent: webpageContent) {
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: GalleryItemOriginData(title: message.author?.displayTitle, timestamp: message.timestamp), indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: NSAttributedString(string: ""), fromPlayingVideo: fromPlayingVideo, performAction: performAction, openActionOptions: openActionOptions)
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: GalleryItemOriginData(title: message.author?.displayTitle, timestamp: message.timestamp), indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: NSAttributedString(string: ""), fromPlayingVideo: fromPlayingVideo, landscape: landscape, performAction: performAction, openActionOptions: openActionOptions)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -286,6 +286,7 @@ class GalleryController: ViewController {
|
||||
|
||||
var temporaryDoNotWaitForReady = false
|
||||
private let fromPlayingVideo: Bool
|
||||
private let landscape: Bool
|
||||
|
||||
private let accountInUseDisposable = MetaDisposable()
|
||||
private let disposable = MetaDisposable()
|
||||
@ -311,7 +312,7 @@ class GalleryController: ViewController {
|
||||
private var performAction: (GalleryControllerInteractionTapAction) -> Void
|
||||
private var openActionOptions: (GalleryControllerInteractionTapAction) -> Void
|
||||
|
||||
init(context: AccountContext, source: GalleryControllerItemSource, invertItemOrder: Bool = false, streamSingleVideo: Bool = false, fromPlayingVideo: Bool = false, synchronousLoad: Bool = false, replaceRootController: @escaping (ViewController, ValuePromise<Bool>?) -> Void, baseNavigationController: NavigationController?, actionInteraction: GalleryControllerActionInteraction? = nil) {
|
||||
init(context: AccountContext, source: GalleryControllerItemSource, invertItemOrder: Bool = false, streamSingleVideo: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, synchronousLoad: Bool = false, replaceRootController: @escaping (ViewController, ValuePromise<Bool>?) -> Void, baseNavigationController: NavigationController?, actionInteraction: GalleryControllerActionInteraction? = nil) {
|
||||
self.context = context
|
||||
self.source = source
|
||||
self.replaceRootController = replaceRootController
|
||||
@ -319,6 +320,7 @@ class GalleryController: ViewController {
|
||||
self.actionInteraction = actionInteraction
|
||||
self.streamVideos = streamSingleVideo
|
||||
self.fromPlayingVideo = fromPlayingVideo
|
||||
self.landscape = landscape
|
||||
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
@ -425,7 +427,7 @@ class GalleryController: ViewController {
|
||||
if case let .MessageEntry(message, _, _, _, _) = entry, message.stableId == strongSelf.centralEntryStableId {
|
||||
isCentral = true
|
||||
}
|
||||
if let item = galleryItemForEntry(context: context, presentationData: strongSelf.presentationData, entry: entry, isCentral: isCentral, streamVideos: streamSingleVideo, fromPlayingVideo: isCentral && fromPlayingVideo, performAction: strongSelf.performAction, openActionOptions: strongSelf.openActionOptions) {
|
||||
if let item = galleryItemForEntry(context: context, presentationData: strongSelf.presentationData, entry: entry, isCentral: isCentral, streamVideos: streamSingleVideo, fromPlayingVideo: isCentral && fromPlayingVideo, landscape: isCentral && landscape, performAction: strongSelf.performAction, openActionOptions: strongSelf.openActionOptions) {
|
||||
if isCentral {
|
||||
centralItemIndex = items.count
|
||||
}
|
||||
@ -804,7 +806,7 @@ class GalleryController: ViewController {
|
||||
var items: [GalleryItem] = []
|
||||
var centralItemIndex: Int?
|
||||
for entry in self.entries {
|
||||
if let item = galleryItemForEntry(context: context, presentationData: self.presentationData, entry: entry, streamVideos: self.streamVideos, fromPlayingVideo: self.fromPlayingVideo, performAction: self.performAction, openActionOptions: self.openActionOptions) {
|
||||
if let item = galleryItemForEntry(context: context, presentationData: self.presentationData, entry: entry, streamVideos: self.streamVideos, fromPlayingVideo: self.fromPlayingVideo, landscape: self.landscape, performAction: self.performAction, openActionOptions: self.openActionOptions) {
|
||||
if case let .MessageEntry(message, _, _, _, _) = entry, message.stableId == self.centralEntryStableId {
|
||||
centralItemIndex = items.count
|
||||
}
|
||||
|
||||
@ -59,6 +59,35 @@ class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGestureRecog
|
||||
}
|
||||
}
|
||||
|
||||
self.pager.dismiss = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
var interfaceAnimationCompleted = false
|
||||
var contentAnimationCompleted = true
|
||||
|
||||
strongSelf.scrollView.isScrollEnabled = false
|
||||
let completion = { [weak self] in
|
||||
if interfaceAnimationCompleted && contentAnimationCompleted {
|
||||
if let dismiss = self?.dismiss {
|
||||
self?.scrollView.isScrollEnabled = true
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let centralItemNode = strongSelf.pager.centralItemNode(), let (transitionNodeForCentralItem, addToTransitionSurface) = strongSelf.transitionDataForCentralItem?(), let node = transitionNodeForCentralItem {
|
||||
contentAnimationCompleted = false
|
||||
centralItemNode.animateOut(to: node, addToTransitionSurface: addToTransitionSurface, completion: {
|
||||
contentAnimationCompleted = true
|
||||
completion()
|
||||
})
|
||||
}
|
||||
strongSelf.animateOut(animateContent: false, completion: {
|
||||
interfaceAnimationCompleted = true
|
||||
completion()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
self.pager.beginCustomDismiss = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.beginCustomDismiss()
|
||||
|
||||
@ -20,6 +20,7 @@ open class GalleryItemNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
var toggleControlsVisibility: () -> Void = { }
|
||||
var dismiss: () -> Void = { }
|
||||
var beginCustomDismiss: () -> Void = { }
|
||||
var completeCustomDismiss: () -> Void = { }
|
||||
var baseNavigationController: () -> NavigationController? = { return nil }
|
||||
|
||||
@ -58,6 +58,7 @@ final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
private var invalidatedItems = false
|
||||
var centralItemIndexOffsetUpdated: (([GalleryItem]?, Int, CGFloat)?) -> Void = { _ in }
|
||||
var toggleControlsVisibility: () -> Void = { }
|
||||
var dismiss: () -> Void = { }
|
||||
var beginCustomDismiss: () -> Void = { }
|
||||
var completeCustomDismiss: () -> Void = { }
|
||||
var baseNavigationController: () -> NavigationController? = { return nil }
|
||||
@ -232,6 +233,7 @@ final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
private func makeNodeForItem(at index: Int) -> GalleryItemNode {
|
||||
let node = self.items[index].node()
|
||||
node.toggleControlsVisibility = self.toggleControlsVisibility
|
||||
node.dismiss = self.dismiss
|
||||
node.beginCustomDismiss = self.beginCustomDismiss
|
||||
node.completeCustomDismiss = self.completeCustomDismiss
|
||||
node.baseNavigationController = self.baseNavigationController
|
||||
|
||||
@ -2233,7 +2233,7 @@ func handlePeerInfoAboutTextAction(context: AccountContext, peerId: PeerId, navi
|
||||
case let .stickerPack(name):
|
||||
controller.present(StickerPackPreviewController(context: context, stickerPack: .name(name), parentNavigationController: controller.navigationController as? NavigationController), in: .window(.root))
|
||||
case let .instantView(webpage, anchor):
|
||||
(controller.navigationController as? NavigationController)?.pushViewController(InstantPageController(context: context, webPage: webpage, anchor: anchor))
|
||||
(controller.navigationController as? NavigationController)?.pushViewController(InstantPageController(context: context, webPage: webpage, sourcePeerType: .group, anchor: anchor))
|
||||
case let .join(link):
|
||||
controller.present(JoinLinkPreviewController(context: context, link: link, navigateToPeer: { peerId in
|
||||
openResolvedPeerImpl(peerId, .chat(textInputState: nil, messageId: nil))
|
||||
|
||||
@ -7,6 +7,7 @@ import Display
|
||||
final class InstantPageController: ViewController {
|
||||
private let context: AccountContext
|
||||
private var webPage: TelegramMediaWebpage
|
||||
private let sourcePeerType: MediaAutoDownloadPeerType
|
||||
private let anchor: String?
|
||||
|
||||
private var presentationData: PresentationData
|
||||
@ -26,12 +27,13 @@ final class InstantPageController: ViewController {
|
||||
private var settingsDisposable: Disposable?
|
||||
private var themeSettings: PresentationThemeSettings?
|
||||
|
||||
init(context: AccountContext, webPage: TelegramMediaWebpage, anchor: String? = nil) {
|
||||
init(context: AccountContext, webPage: TelegramMediaWebpage, sourcePeerType: MediaAutoDownloadPeerType, anchor: String? = nil) {
|
||||
self.context = context
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
self.webPage = webPage
|
||||
self.anchor = anchor
|
||||
self.sourcePeerType = sourcePeerType
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
@ -85,7 +87,7 @@ final class InstantPageController: ViewController {
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = InstantPageControllerNode(context: self.context, settings: self.settings, themeSettings: self.themeSettings, presentationTheme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, statusBar: self.statusBar, getNavigationController: { [weak self] in
|
||||
self.displayNode = InstantPageControllerNode(context: self.context, settings: self.settings, themeSettings: self.themeSettings, presentationTheme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, statusBar: self.statusBar, sourcePeerType: self.sourcePeerType, getNavigationController: { [weak self] in
|
||||
return self?.navigationController as? NavigationController
|
||||
}, present: { [weak self] c, a in
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
|
||||
@ -14,6 +14,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
private var strings: PresentationStrings
|
||||
private var dateTimeFormat: PresentationDateTimeFormat
|
||||
private var theme: InstantPageTheme?
|
||||
private let sourcePeerType: MediaAutoDownloadPeerType
|
||||
private var manualThemeOverride: InstantPageThemeType?
|
||||
private let getNavigationController: () -> NavigationController?
|
||||
private let present: (ViewController, Any?) -> Void
|
||||
@ -74,7 +75,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
return InstantPageStoredState(contentOffset: Double(self.scrollNode.view.contentOffset.y), details: details)
|
||||
}
|
||||
|
||||
init(context: AccountContext, settings: InstantPagePresentationSettings?, themeSettings: PresentationThemeSettings?, presentationTheme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, statusBar: StatusBar, getNavigationController: @escaping () -> NavigationController?, present: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, openPeer: @escaping (PeerId) -> Void, navigateBack: @escaping () -> Void) {
|
||||
init(context: AccountContext, settings: InstantPagePresentationSettings?, themeSettings: PresentationThemeSettings?, presentationTheme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, statusBar: StatusBar, sourcePeerType: MediaAutoDownloadPeerType, getNavigationController: @escaping () -> NavigationController?, present: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, openPeer: @escaping (PeerId) -> Void, navigateBack: @escaping () -> Void) {
|
||||
self.context = context
|
||||
self.presentationTheme = presentationTheme
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
@ -85,7 +86,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.theme = settings.flatMap { settings in
|
||||
return instantPageThemeForType(instantPageThemeTypeForSettingsAndTime(themeSettings: themeSettings, settings: settings, time: themeReferenceDate), settings: settings)
|
||||
}
|
||||
|
||||
self.sourcePeerType = sourcePeerType
|
||||
self.statusBar = statusBar
|
||||
self.getNavigationController = getNavigationController
|
||||
self.present = present
|
||||
@ -1122,7 +1123,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
case let .result(webpage):
|
||||
if let webpage = webpage, case .Loaded = webpage.content {
|
||||
strongSelf.loadProgress.set(1.0)
|
||||
strongSelf.pushController(InstantPageController(context: strongSelf.context, webPage: webpage, anchor: anchor))
|
||||
strongSelf.pushController(InstantPageController(context: strongSelf.context, webPage: webpage, sourcePeerType: strongSelf.sourcePeerType, anchor: anchor))
|
||||
}
|
||||
break
|
||||
case let .progress(progress):
|
||||
|
||||
@ -5,6 +5,11 @@ import Postbox
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
|
||||
private struct FetchControls {
|
||||
let fetch: (Bool) -> Void
|
||||
let cancel: () -> Void
|
||||
}
|
||||
|
||||
final class InstantPageImageNode: ASDisplayNode, InstantPageNode {
|
||||
private let context: AccountContext
|
||||
private let webPage: TelegramMediaWebpage
|
||||
@ -17,6 +22,8 @@ final class InstantPageImageNode: ASDisplayNode, InstantPageNode {
|
||||
private let openMedia: (InstantPageMedia) -> Void
|
||||
private let longPressMedia: (InstantPageMedia) -> Void
|
||||
|
||||
private var fetchControls: FetchControls?
|
||||
|
||||
private let imageNode: TransformImageNode
|
||||
private let statusNode: RadialStatusNode
|
||||
private let linkIconNode: ASImageNode
|
||||
@ -54,7 +61,18 @@ final class InstantPageImageNode: ASDisplayNode, InstantPageNode {
|
||||
if let image = media.media as? TelegramMediaImage, let largest = largestImageRepresentation(image.representations) {
|
||||
let imageReference = ImageMediaReference.webPage(webPage: WebpageReference(webPage), media: image)
|
||||
self.imageNode.setSignal(chatMessagePhoto(postbox: context.account.postbox, photoReference: imageReference))
|
||||
self.fetchedDisposable.set(chatMessagePhotoInteractiveFetched(context: context, photoReference: imageReference, storeToDownloadsPeerType: nil).start())
|
||||
|
||||
if false {
|
||||
self.fetchedDisposable.set(chatMessagePhotoInteractiveFetched(context: context, photoReference: imageReference, storeToDownloadsPeerType: nil).start())
|
||||
}
|
||||
|
||||
self.fetchControls = FetchControls(fetch: { [weak self] manual in
|
||||
if let strongSelf = self {
|
||||
strongSelf.fetchedDisposable.set(chatMessagePhotoInteractiveFetched(context: context, photoReference: imageReference, storeToDownloadsPeerType: nil).start())
|
||||
}
|
||||
}, cancel: {
|
||||
chatMessagePhotoCancelInteractiveFetch(account: context.account, photoReference: imageReference)
|
||||
})
|
||||
|
||||
if interactive {
|
||||
self.statusDisposable.set((context.account.postbox.mediaBox.resourceStatus(largest.resource) |> deliverOnMainQueue).start(next: { [weak self] status in
|
||||
@ -143,9 +161,11 @@ final class InstantPageImageNode: ASDisplayNode, InstantPageNode {
|
||||
var state: RadialStatusNodeState = .none
|
||||
if let fetchStatus = self.fetchStatus {
|
||||
switch fetchStatus {
|
||||
case let .Fetching(isActive, progress):
|
||||
case let .Fetching(_, progress):
|
||||
let adjustedProgress = max(progress, 0.027)
|
||||
state = .progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: false)
|
||||
state = .progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true)
|
||||
case .Remote:
|
||||
state = .download(.white)
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -236,17 +256,28 @@ final class InstantPageImageNode: ASDisplayNode, InstantPageNode {
|
||||
@objc private func tapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .ended:
|
||||
if let (gesture, _) = recognizer.lastRecognizedGestureAndLocation {
|
||||
switch gesture {
|
||||
case .tap:
|
||||
if self.media.media is TelegramMediaImage && self.media.index == -1 {
|
||||
return
|
||||
if let (gesture, _) = recognizer.lastRecognizedGestureAndLocation, let fetchStatus = self.fetchStatus {
|
||||
switch fetchStatus {
|
||||
case .Local:
|
||||
switch gesture {
|
||||
case .tap:
|
||||
if self.media.media is TelegramMediaImage && self.media.index == -1 {
|
||||
return
|
||||
}
|
||||
self.openMedia(self.media)
|
||||
case .longTap:
|
||||
self.longPressMedia(self.media)
|
||||
default:
|
||||
break
|
||||
}
|
||||
case .Remote:
|
||||
if case .tap = gesture {
|
||||
self.fetchControls?.fetch(true)
|
||||
}
|
||||
case .Fetching:
|
||||
if case .tap = gesture {
|
||||
self.fetchControls?.cancel()
|
||||
}
|
||||
self.openMedia(self.media)
|
||||
case .longTap:
|
||||
self.longPressMedia(self.media)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
|
||||
@ -5,21 +5,30 @@ import Postbox
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
|
||||
private struct FetchControls {
|
||||
let fetch: (Bool) -> Void
|
||||
let cancel: () -> Void
|
||||
}
|
||||
|
||||
final class InstantPagePlayableVideoNode: ASDisplayNode, InstantPageNode {
|
||||
private let context: AccountContext
|
||||
let media: InstantPageMedia
|
||||
private let interactive: Bool
|
||||
private let openMedia: (InstantPageMedia) -> Void
|
||||
private var fetchControls: FetchControls?
|
||||
|
||||
private let videoNode: UniversalVideoNode
|
||||
private let statusNode: RadialStatusNode
|
||||
|
||||
private var currentSize: CGSize?
|
||||
|
||||
private var fetchStatus: MediaResourceStatus?
|
||||
private var fetchedDisposable = MetaDisposable()
|
||||
private var statusDisposable = MetaDisposable()
|
||||
|
||||
private var localIsVisible = false
|
||||
|
||||
init(context: AccountContext, webPage: TelegramMediaWebpage, theme: InstantPageTheme, media: InstantPageMedia, interactive: Bool, openMedia: @escaping (InstantPageMedia) -> Void) {
|
||||
init(context: AccountContext, webPage: TelegramMediaWebpage, theme: InstantPageTheme, media: InstantPageMedia, interactive: Bool, openMedia: @escaping (InstantPageMedia) -> Void) {
|
||||
self.context = context
|
||||
self.media = media
|
||||
self.interactive = interactive
|
||||
@ -31,15 +40,31 @@ final class InstantPagePlayableVideoNode: ASDisplayNode, InstantPageNode {
|
||||
imageReference = ImageMediaReference.webPage(webPage: WebpageReference(webPage), media: image)
|
||||
}
|
||||
|
||||
self.videoNode = UniversalVideoNode(postbox: context.account.postbox, audioSession: context.sharedContext.mediaManager.audioSession, manager: context.sharedContext.mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: NativeVideoContent(id: .instantPage(webPage.webpageId, media.media.id!), fileReference: .webPage(webPage: WebpageReference(webPage), media: media.media as! TelegramMediaFile), imageReference: imageReference, loopVideo: true, enableSound: false, fetchAutomatically: true, placeholderColor: theme.pageBackgroundColor), priority: .embedded, autoplay: true)
|
||||
var streamVideo = false
|
||||
if let file = media.media as? TelegramMediaFile {
|
||||
streamVideo = isMediaStreamable(media: file)
|
||||
}
|
||||
|
||||
self.videoNode = UniversalVideoNode(postbox: context.account.postbox, audioSession: context.sharedContext.mediaManager.audioSession, manager: context.sharedContext.mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: NativeVideoContent(id: .instantPage(webPage.webpageId, media.media.id!), fileReference: .webPage(webPage: WebpageReference(webPage), media: media.media as! TelegramMediaFile), imageReference: imageReference, streamVideo: streamVideo, loopVideo: true, enableSound: false, fetchAutomatically: true, placeholderColor: theme.pageBackgroundColor), priority: .embedded, autoplay: true)
|
||||
self.videoNode.isUserInteractionEnabled = false
|
||||
|
||||
self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.6))
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.videoNode)
|
||||
|
||||
if let file = media.media as? TelegramMediaFile {
|
||||
self.fetchedDisposable.set(fetchedMediaResource(postbox: context.account.postbox, reference: AnyMediaReference.webPage(webPage: WebpageReference(webPage), media: file).resourceReference(file.resource)).start())
|
||||
|
||||
self.statusDisposable.set((context.account.postbox.mediaBox.resourceStatus(file.resource) |> deliverOnMainQueue).start(next: { [weak self] status in
|
||||
displayLinkDispatcher.dispatch {
|
||||
if let strongSelf = self {
|
||||
strongSelf.fetchStatus = status
|
||||
strongSelf.updateFetchStatus()
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,6 +94,26 @@ final class InstantPagePlayableVideoNode: ASDisplayNode, InstantPageNode {
|
||||
func update(strings: PresentationStrings, theme: InstantPageTheme) {
|
||||
}
|
||||
|
||||
private func updateFetchStatus() {
|
||||
var state: RadialStatusNodeState = .none
|
||||
if let fetchStatus = self.fetchStatus {
|
||||
switch fetchStatus {
|
||||
case let .Fetching(_, progress):
|
||||
let adjustedProgress = max(progress, 0.027)
|
||||
state = .progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true)
|
||||
case .Remote:
|
||||
state = .download(.white)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
self.statusNode.transitionToState(state, completion: { [weak statusNode] in
|
||||
if state == .none {
|
||||
statusNode?.removeFromSupernode()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override func layout() {
|
||||
super.layout()
|
||||
|
||||
@ -79,6 +124,9 @@ final class InstantPagePlayableVideoNode: ASDisplayNode, InstantPageNode {
|
||||
|
||||
self.videoNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.videoNode.updateLayout(size: size, transition: .immediate)
|
||||
|
||||
let radialStatusSize: CGFloat = 50.0
|
||||
self.statusNode.frame = CGRect(x: floorToScreenPixels((size.width - radialStatusSize) / 2.0), y: floorToScreenPixels((size.height - radialStatusSize) / 2.0), width: radialStatusSize, height: radialStatusSize)
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,8 +146,15 @@ final class InstantPagePlayableVideoNode: ASDisplayNode, InstantPageNode {
|
||||
}
|
||||
|
||||
@objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
self.openMedia(self.media)
|
||||
if case .ended = recognizer.state, let fetchStatus = self.fetchStatus {
|
||||
switch fetchStatus {
|
||||
case .Local:
|
||||
self.openMedia(self.media)
|
||||
case .Remote:
|
||||
self.fetchControls?.fetch(true)
|
||||
case .Fetching:
|
||||
self.fetchControls?.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,9 +15,6 @@ func isMediaStreamable(message: Message, media: TelegramMediaFile) -> Bool {
|
||||
if size < 1 * 1024 * 1024 {
|
||||
return false
|
||||
}
|
||||
// if media.isAnimated {
|
||||
// return false
|
||||
// }
|
||||
for attribute in media.attributes {
|
||||
if case let .Video(video) = attribute {
|
||||
if video.flags.contains(.supportsStreaming) {
|
||||
@ -41,9 +38,6 @@ func isMediaStreamable(media: TelegramMediaFile) -> Bool {
|
||||
if size < 1 * 1024 * 1024 {
|
||||
return false
|
||||
}
|
||||
// if media.isAnimated {
|
||||
// return false
|
||||
// }
|
||||
for attribute in media.attributes {
|
||||
if case let .Video(video) = attribute {
|
||||
if video.flags.contains(.supportsStreaming) {
|
||||
|
||||
@ -203,6 +203,7 @@ class ItemListController<Entry: ItemListNodeEntry>: ViewController {
|
||||
var commitPreview: ((UIViewController) -> Void)?
|
||||
|
||||
var willDisappear: ((Bool) -> Void)?
|
||||
var didDisappear: ((Bool) -> Void)?
|
||||
|
||||
convenience init(context: AccountContext, state: Signal<(ItemListControllerState, (ItemListNodeState<Entry>, Entry.ItemGenerationArguments)), NoError>, tabBarItem: Signal<ItemListControllerTabBarItem, NoError>? = nil) {
|
||||
self.init(sharedContext: context.sharedContext, state: state, tabBarItem: tabBarItem)
|
||||
@ -460,6 +461,12 @@ class ItemListController<Entry: ItemListNodeEntry>: ViewController {
|
||||
self.willDisappear?(animated)
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
|
||||
self.didDisappear?(animated)
|
||||
}
|
||||
|
||||
override func dismiss(completion: (() -> Void)? = nil) {
|
||||
(self.displayNode as! ItemListControllerNode<Entry>).animateOut(completion: completion)
|
||||
}
|
||||
|
||||
@ -484,10 +484,10 @@ final class ListMessageSnippetItemNode: ListMessageNode {
|
||||
if content.instantPage != nil {
|
||||
if websiteType(of: content) == .instagram {
|
||||
if !item.controllerInteraction.openMessage(item.message, .default) {
|
||||
item.controllerInteraction.openInstantPage(item.message)
|
||||
item.controllerInteraction.openInstantPage(item.message, nil)
|
||||
}
|
||||
} else {
|
||||
item.controllerInteraction.openInstantPage(item.message)
|
||||
item.controllerInteraction.openInstantPage(item.message, nil)
|
||||
}
|
||||
} else {
|
||||
if isTelegramMeLink(content.url) || !item.controllerInteraction.openMessage(item.message, .default) {
|
||||
|
||||
@ -322,7 +322,7 @@ func isAutodownloadEnabledForAnyPeerType(category: MediaAutoDownloadCategory) ->
|
||||
return category.contacts || category.otherPrivate || category.groups || category.channels
|
||||
}
|
||||
|
||||
public func shouldDownloadMediaAutomatically(settings: MediaAutoDownloadSettings, peerType: MediaAutoDownloadPeerType, networkType: MediaAutoDownloadNetworkType, authorPeerId: PeerId?, contactsPeerIds: Set<PeerId>, media: Media) -> Bool {
|
||||
public func shouldDownloadMediaAutomatically(settings: MediaAutoDownloadSettings, peerType: MediaAutoDownloadPeerType, networkType: MediaAutoDownloadNetworkType, authorPeerId: PeerId? = nil, contactsPeerIds: Set<PeerId> = Set(), media: Media) -> Bool {
|
||||
if (networkType == .cellular && !settings.cellular.enabled) || (networkType == .wifi && !settings.wifi.enabled) {
|
||||
return false
|
||||
}
|
||||
@ -340,7 +340,11 @@ public func shouldDownloadMediaAutomatically(settings: MediaAutoDownloadSettings
|
||||
return false
|
||||
}
|
||||
if let size = size {
|
||||
return size <= category.sizeLimit
|
||||
var sizeLimit = category.sizeLimit
|
||||
if let file = media as? TelegramMediaFile, file.isVoice {
|
||||
sizeLimit = max(2 * 1024 * 1024, sizeLimit)
|
||||
}
|
||||
return size <= sizeLimit
|
||||
} else if category.sizeLimit == Int32.max {
|
||||
return true
|
||||
} else {
|
||||
|
||||
@ -20,7 +20,7 @@ private enum ChatMessageGalleryControllerData {
|
||||
case chatAvatars(AvatarGalleryController, Media)
|
||||
}
|
||||
|
||||
private func chatMessageGalleryControllerData(context: AccountContext, message: Message, navigationController: NavigationController?, standalone: Bool, reverseMessageGalleryOrder: Bool, stream: Bool, fromPlayingVideo: Bool, synchronousLoad: Bool, actionInteraction: GalleryControllerActionInteraction?) -> ChatMessageGalleryControllerData? {
|
||||
private func chatMessageGalleryControllerData(context: AccountContext, message: Message, navigationController: NavigationController?, standalone: Bool, reverseMessageGalleryOrder: Bool, mode: ChatControllerInteractionOpenMessageMode, synchronousLoad: Bool, actionInteraction: GalleryControllerActionInteraction?) -> ChatMessageGalleryControllerData? {
|
||||
var galleryMedia: Media?
|
||||
var otherMedia: Media?
|
||||
var instantPageMedia: (TelegramMediaWebpage, [InstantPageGalleryEntry])?
|
||||
@ -69,6 +69,21 @@ private func chatMessageGalleryControllerData(context: AccountContext, message:
|
||||
}
|
||||
}
|
||||
|
||||
var stream = false
|
||||
var fromPlayingVideo = false
|
||||
var landscape = false
|
||||
|
||||
if case .stream = mode {
|
||||
stream = true
|
||||
}
|
||||
if case .automaticPlayback = mode {
|
||||
fromPlayingVideo = true
|
||||
}
|
||||
if case .landscape = mode {
|
||||
fromPlayingVideo = true
|
||||
landscape = true
|
||||
}
|
||||
|
||||
if let (webPage, instantPageMedia) = instantPageMedia, let galleryMedia = galleryMedia {
|
||||
var centralIndex: Int = 0
|
||||
for i in 0 ..< instantPageMedia.count {
|
||||
@ -109,7 +124,7 @@ private func chatMessageGalleryControllerData(context: AccountContext, message:
|
||||
}
|
||||
#if DEBUG
|
||||
if ext == "mkv" {
|
||||
let gallery = GalleryController(context: context, source: standalone ? .standaloneMessage(message) : .peerMessagesAtId(message.id), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: fromPlayingVideo, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in
|
||||
let gallery = GalleryController(context: context, source: standalone ? .standaloneMessage(message) : .peerMessagesAtId(message.id), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: fromPlayingVideo, landscape: landscape, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in
|
||||
navigationController?.replaceTopController(controller, animated: false, ready: ready)
|
||||
}, baseNavigationController: navigationController, actionInteraction: actionInteraction)
|
||||
return .gallery(gallery)
|
||||
@ -126,7 +141,7 @@ private func chatMessageGalleryControllerData(context: AccountContext, message:
|
||||
let gallery = SecretMediaPreviewController(context: context, messageId: message.id)
|
||||
return .secretGallery(gallery)
|
||||
} else {
|
||||
let gallery = GalleryController(context: context, source: standalone ? .standaloneMessage(message) : .peerMessagesAtId(message.id), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: fromPlayingVideo, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in
|
||||
let gallery = GalleryController(context: context, source: standalone ? .standaloneMessage(message) : .peerMessagesAtId(message.id), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: fromPlayingVideo, landscape: landscape, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in
|
||||
navigationController?.replaceTopController(controller, animated: false, ready: ready)
|
||||
}, baseNavigationController: navigationController, actionInteraction: actionInteraction)
|
||||
gallery.temporaryDoNotWaitForReady = fromPlayingVideo
|
||||
@ -147,7 +162,7 @@ enum ChatMessagePreviewControllerData {
|
||||
}
|
||||
|
||||
func chatMessagePreviewControllerData(context: AccountContext, message: Message, standalone: Bool, reverseMessageGalleryOrder: Bool, navigationController: NavigationController?) -> ChatMessagePreviewControllerData? {
|
||||
if let mediaData = chatMessageGalleryControllerData(context: context, message: message, navigationController: navigationController, standalone: standalone, reverseMessageGalleryOrder: reverseMessageGalleryOrder, stream: false, fromPlayingVideo: false, synchronousLoad: true, actionInteraction: nil) {
|
||||
if let mediaData = chatMessageGalleryControllerData(context: context, message: message, navigationController: navigationController, standalone: standalone, reverseMessageGalleryOrder: reverseMessageGalleryOrder, mode: .default, synchronousLoad: true, actionInteraction: nil) {
|
||||
switch mediaData {
|
||||
case let .gallery(gallery):
|
||||
return .gallery(gallery)
|
||||
@ -160,8 +175,8 @@ func chatMessagePreviewControllerData(context: AccountContext, message: Message,
|
||||
return nil
|
||||
}
|
||||
|
||||
func openChatMessage(context: AccountContext, message: Message, standalone: Bool, reverseMessageGalleryOrder: Bool, stream: Bool = false, fromPlayingVideo: Bool = false, navigationController: NavigationController?, modal: Bool = false, dismissInput: @escaping () -> Void, present: @escaping (ViewController, Any?) -> Void, transitionNode: @escaping (MessageId, Media) -> (ASDisplayNode, () -> (UIView?, UIView?))?, addToTransitionSurface: @escaping (UIView) -> Void, openUrl: @escaping (String) -> Void, openPeer: @escaping (Peer, ChatControllerInteractionNavigateToPeer) -> Void, callPeer: @escaping (PeerId) -> Void, enqueueMessage: @escaping (EnqueueMessage) -> Void, sendSticker: ((FileMediaReference) -> Void)?, setupTemporaryHiddenMedia: @escaping (Signal<InstantPageGalleryEntry?, NoError>, Int, Media) -> Void, chatAvatarHiddenMedia: @escaping (Signal<MessageId?, NoError>, Media) -> Void, actionInteraction: GalleryControllerActionInteraction? = nil) -> Bool {
|
||||
if let mediaData = chatMessageGalleryControllerData(context: context, message: message, navigationController: navigationController, standalone: standalone, reverseMessageGalleryOrder: reverseMessageGalleryOrder, stream: stream, fromPlayingVideo: fromPlayingVideo, synchronousLoad: false, actionInteraction: actionInteraction) {
|
||||
func openChatMessage(context: AccountContext, message: Message, standalone: Bool, reverseMessageGalleryOrder: Bool, mode: ChatControllerInteractionOpenMessageMode = .default, navigationController: NavigationController?, modal: Bool = false, dismissInput: @escaping () -> Void, present: @escaping (ViewController, Any?) -> Void, transitionNode: @escaping (MessageId, Media) -> (ASDisplayNode, () -> (UIView?, UIView?))?, addToTransitionSurface: @escaping (UIView) -> Void, openUrl: @escaping (String) -> Void, openPeer: @escaping (Peer, ChatControllerInteractionNavigateToPeer) -> Void, callPeer: @escaping (PeerId) -> Void, enqueueMessage: @escaping (EnqueueMessage) -> Void, sendSticker: ((FileMediaReference) -> Void)?, setupTemporaryHiddenMedia: @escaping (Signal<InstantPageGalleryEntry?, NoError>, Int, Media) -> Void, chatAvatarHiddenMedia: @escaping (Signal<MessageId?, NoError>, Media) -> Void, actionInteraction: GalleryControllerActionInteraction? = nil) -> Bool {
|
||||
if let mediaData = chatMessageGalleryControllerData(context: context, message: message, navigationController: navigationController, standalone: standalone, reverseMessageGalleryOrder: reverseMessageGalleryOrder, mode: mode, synchronousLoad: false, actionInteraction: actionInteraction) {
|
||||
switch mediaData {
|
||||
case let .url(url):
|
||||
openUrl(url)
|
||||
@ -360,7 +375,7 @@ func openChatMessage(context: AccountContext, message: Message, standalone: Bool
|
||||
return false
|
||||
}
|
||||
|
||||
func openChatInstantPage(context: AccountContext, message: Message, navigationController: NavigationController) {
|
||||
func openChatInstantPage(context: AccountContext, message: Message, sourcePeerType: MediaAutoDownloadPeerType?, navigationController: NavigationController) {
|
||||
for media in message.media {
|
||||
if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
|
||||
if let _ = content.instantPage {
|
||||
@ -402,7 +417,7 @@ func openChatInstantPage(context: AccountContext, message: Message, navigationCo
|
||||
anchor = String(textUrl[anchorRange.upperBound...])
|
||||
}
|
||||
|
||||
let pageController = InstantPageController(context: context, webPage: webpage, anchor: anchor)
|
||||
let pageController = InstantPageController(context: context, webPage: webpage, sourcePeerType: sourcePeerType ?? .channel, anchor: anchor)
|
||||
navigationController.pushViewController(pageController)
|
||||
}
|
||||
break
|
||||
|
||||
@ -77,7 +77,7 @@ func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlCon
|
||||
controller.sendSticker = sendFile
|
||||
present(controller, nil)
|
||||
case let .instantView(webpage, anchor):
|
||||
navigationController?.pushViewController(InstantPageController(context: context, webPage: webpage, anchor: anchor))
|
||||
navigationController?.pushViewController(InstantPageController(context: context, webPage: webpage, sourcePeerType: .channel, anchor: anchor))
|
||||
case let .join(link):
|
||||
dismissInput()
|
||||
present(JoinLinkPreviewController(context: context, link: link, navigateToPeer: { peerId in
|
||||
|
||||
@ -54,7 +54,7 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _ in }, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendMessage: { _ in }, sendSticker: { _, _ in }, sendGif: { _ in }, requestMessageActionCallback: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _ in }, openWallpaper: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||
}, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _ in }, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendMessage: { _ in }, sendSticker: { _, _ in }, sendGif: { _ in }, requestMessageActionCallback: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||
}, presentController: { _, _ in }, navigationController: {
|
||||
return nil
|
||||
}, presentGlobalOverlayController: { _, _ in
|
||||
|
||||
@ -187,9 +187,9 @@ public class PeerMediaCollectionController: TelegramController {
|
||||
}, shareCurrentLocation: {
|
||||
}, shareAccountContact: {
|
||||
}, sendBotCommand: { _, _ in
|
||||
}, openInstantPage: { [weak self] message in
|
||||
}, openInstantPage: { [weak self] message, associatedData in
|
||||
if let strongSelf = self, strongSelf.isNodeLoaded, let navigationController = strongSelf.navigationController as? NavigationController, let message = strongSelf.mediaCollectionDisplayNode.messageForGallery(message.id)?.message {
|
||||
openChatInstantPage(context: strongSelf.context, message: message, navigationController: navigationController)
|
||||
openChatInstantPage(context: strongSelf.context, message: message, sourcePeerType: associatedData?.automaticDownloadPeerType, navigationController: navigationController)
|
||||
}
|
||||
}, openWallpaper: { [weak self] message in
|
||||
if let strongSelf = self, strongSelf.isNodeLoaded, let message = strongSelf.mediaCollectionDisplayNode.messageForGallery(message.id)?.message {
|
||||
|
||||
@ -521,7 +521,11 @@ public final class PresentationCall {
|
||||
case .accepting, .active, .dropping, .requesting:
|
||||
switch state {
|
||||
case .connecting:
|
||||
tone = .connecting
|
||||
if case .requesting = previous.state {
|
||||
tone = .ringing
|
||||
} else {
|
||||
tone = .connecting
|
||||
}
|
||||
case .requesting(true):
|
||||
tone = .ringing
|
||||
case let .terminated(_, reason, _):
|
||||
@ -529,10 +533,10 @@ public final class PresentationCall {
|
||||
switch reason {
|
||||
case let .ended(type):
|
||||
switch type {
|
||||
case .busy:
|
||||
tone = .busy
|
||||
case .hungUp, .missed:
|
||||
tone = .ended
|
||||
case .busy:
|
||||
tone = .busy
|
||||
case .hungUp, .missed:
|
||||
tone = .ended
|
||||
}
|
||||
case .error:
|
||||
tone = .failed
|
||||
|
||||
@ -72,13 +72,18 @@ class SearchBarPlaceholderNode: ASDisplayNode {
|
||||
super.didLoad()
|
||||
|
||||
let gestureRecognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.backgroundTap(_:)))
|
||||
gestureRecognizer.highlight = { [weak self] _ in
|
||||
gestureRecognizer.highlight = { [weak self] point in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.backgroundNode.layer.animate(from: (strongSelf.backgroundNode.backgroundColor ?? strongSelf.foregroundColor).cgColor, to: strongSelf.foregroundColor.withMultipliedBrightnessBy(0.9).cgColor, keyPath: "backgroundColor", timingFunction: kCAMediaTimingFunctionEaseInEaseOut, duration: 0.3)
|
||||
strongSelf.backgroundNode.backgroundColor = strongSelf.foregroundColor.withMultipliedBrightnessBy(0.9)
|
||||
if let _ = point {
|
||||
strongSelf.backgroundNode.layer.animate(from: (strongSelf.backgroundNode.backgroundColor ?? strongSelf.foregroundColor).cgColor, to: strongSelf.foregroundColor.withMultipliedBrightnessBy(0.9).cgColor, keyPath: "backgroundColor", timingFunction: kCAMediaTimingFunctionEaseInEaseOut, duration: 0.3)
|
||||
strongSelf.backgroundNode.backgroundColor = strongSelf.foregroundColor.withMultipliedBrightnessBy(0.9)
|
||||
} else {
|
||||
strongSelf.backgroundNode.layer.animate(from: (strongSelf.backgroundNode.backgroundColor ?? strongSelf.foregroundColor).cgColor, to: strongSelf.foregroundColor.cgColor, keyPath: "backgroundColor", timingFunction: kCAMediaTimingFunctionEaseInEaseOut, duration: 0.2, completion: { _ in
|
||||
strongSelf.backgroundNode.backgroundColor = strongSelf.foregroundColor
|
||||
})
|
||||
}
|
||||
}
|
||||
gestureRecognizer.tapActionAtPoint = { _ in
|
||||
return .waitForSingleTap
|
||||
@ -161,13 +166,7 @@ class SearchBarPlaceholderNode: ASDisplayNode {
|
||||
|
||||
@objc private func backgroundTap(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
self.backgroundNode.layer.animate(from: (self.backgroundNode.backgroundColor ?? self.foregroundColor).cgColor, to: self.foregroundColor.cgColor, keyPath: "backgroundColor", timingFunction: kCAMediaTimingFunctionEaseInEaseOut, duration: 0.2, completion: { _ in
|
||||
self.backgroundNode.backgroundColor = self.foregroundColor
|
||||
})
|
||||
|
||||
if let activate = self.activate {
|
||||
activate()
|
||||
}
|
||||
self.activate?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,11 +22,12 @@ class UniversalVideoGalleryItem: GalleryItem {
|
||||
let credit: NSAttributedString?
|
||||
let hideControls: Bool
|
||||
let fromPlayingVideo: Bool
|
||||
let landscape: Bool
|
||||
let playbackCompleted: () -> Void
|
||||
let performAction: (GalleryControllerInteractionTapAction) -> Void
|
||||
let openActionOptions: (GalleryControllerInteractionTapAction) -> Void
|
||||
|
||||
init(context: AccountContext, presentationData: PresentationData, content: UniversalVideoContent, originData: GalleryItemOriginData?, indexData: GalleryItemIndexData?, contentInfo: UniversalVideoGalleryItemContentInfo?, caption: NSAttributedString, credit: NSAttributedString? = nil, hideControls: Bool = false, fromPlayingVideo: Bool = false, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void, openActionOptions: @escaping (GalleryControllerInteractionTapAction) -> Void) {
|
||||
init(context: AccountContext, presentationData: PresentationData, content: UniversalVideoContent, originData: GalleryItemOriginData?, indexData: GalleryItemIndexData?, contentInfo: UniversalVideoGalleryItemContentInfo?, caption: NSAttributedString, credit: NSAttributedString? = nil, hideControls: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void, openActionOptions: @escaping (GalleryControllerInteractionTapAction) -> Void) {
|
||||
self.context = context
|
||||
self.presentationData = presentationData
|
||||
self.content = content
|
||||
@ -37,6 +38,7 @@ class UniversalVideoGalleryItem: GalleryItem {
|
||||
self.credit = credit
|
||||
self.hideControls = hideControls
|
||||
self.fromPlayingVideo = fromPlayingVideo
|
||||
self.landscape = landscape
|
||||
self.playbackCompleted = playbackCompleted
|
||||
self.performAction = performAction
|
||||
self.openActionOptions = openActionOptions
|
||||
@ -159,6 +161,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
private var validLayout: (ContainerViewLayout, CGFloat)?
|
||||
private var didPause = false
|
||||
private var isPaused = true
|
||||
private var dismissOnOrientationChange = false
|
||||
private var keepSoundOnDismiss = false
|
||||
|
||||
private var requiresDownload = false
|
||||
|
||||
@ -260,6 +264,10 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
override func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)
|
||||
|
||||
var dismiss = false
|
||||
if let (previousLayout, _) = self.validLayout, self.dismissOnOrientationChange, previousLayout.size.width > previousLayout.size.height && previousLayout.size.height == layout.size.width {
|
||||
dismiss = true
|
||||
}
|
||||
self.validLayout = (layout, navigationBarHeight)
|
||||
|
||||
let statusDiameter: CGFloat = 50.0
|
||||
@ -274,6 +282,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
pictureInPictureNode.updateLayout(placeholderSize, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
if dismiss {
|
||||
self.keepSoundOnDismiss = true
|
||||
self.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
func setupItem(_ item: UniversalVideoGalleryItem) {
|
||||
@ -282,6 +295,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
self.statusButtonNode.isHidden = true
|
||||
}
|
||||
|
||||
self.dismissOnOrientationChange = item.landscape
|
||||
|
||||
var disablePlayerControls = false
|
||||
var isAnimated = false
|
||||
if let content = item.content as? NativeVideoContent {
|
||||
@ -548,8 +563,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
self.initiallyActivated = true
|
||||
videoNode.playOnceWithSound(playAndRecord: false, actionAtEnd: .stop)
|
||||
}
|
||||
} else if videoNode.ownsContentNode {
|
||||
videoNode.pause()
|
||||
} else {
|
||||
self.dismissOnOrientationChange = false
|
||||
if videoNode.ownsContentNode {
|
||||
videoNode.pause()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -623,8 +641,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
self.context.sharedContext.mediaManager.setOverlayVideoNode(nil)
|
||||
} else {
|
||||
var transformedFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view)
|
||||
let transformedSuperFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view.superview)
|
||||
let transformedSelfFrame = node.0.view.convert(node.0.view.bounds, to: self.view)
|
||||
var transformedSuperFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view.superview)
|
||||
var transformedSelfFrame = node.0.view.convert(node.0.view.bounds, to: self.view)
|
||||
let transformedCopyViewFinalFrame = videoNode.view.convert(videoNode.view.bounds, to: self.view)
|
||||
|
||||
let (maybeSurfaceCopyView, _) = node.1()
|
||||
@ -640,6 +658,18 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
if let contentSurface = surfaceCopyView.superview {
|
||||
transformedSurfaceFrame = node.0.view.convert(node.0.view.bounds, to: contentSurface)
|
||||
transformedSurfaceFinalFrame = videoNode.view.convert(videoNode.view.bounds, to: contentSurface)
|
||||
|
||||
if let frame = transformedSurfaceFrame, frame.minY < 0.0 {
|
||||
transformedSurfaceFrame = CGRect(x: frame.minX, y: 0.0, width: frame.width, height: frame.height)
|
||||
}
|
||||
}
|
||||
|
||||
if transformedSelfFrame.maxY < 0.0 {
|
||||
transformedSelfFrame = CGRect(x: transformedSelfFrame.minX, y: 0.0, width: transformedSelfFrame.width, height: transformedSelfFrame.height)
|
||||
}
|
||||
|
||||
if transformedSuperFrame.maxY < 0.0 {
|
||||
transformedSuperFrame = CGRect(x: transformedSuperFrame.minX, y: 0.0, width: transformedSuperFrame.width, height: transformedSuperFrame.height)
|
||||
}
|
||||
|
||||
if let transformedSurfaceFrame = transformedSurfaceFrame {
|
||||
@ -806,7 +836,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
toTransform = CATransform3DScale(videoNode.layer.transform, transformScale, transformScale, 1.0)
|
||||
|
||||
if videoNode.hasAttachedContext {
|
||||
videoNode.continuePlayingWithoutSound()
|
||||
if self.isPaused || !self.keepSoundOnDismiss {
|
||||
videoNode.continuePlayingWithoutSound()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
videoNode.allowsGroupOpacity = true
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user