mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Initial instant video expansion implementation
This commit is contained in:
parent
6b3b6ac9ab
commit
7a34781f6f
@ -24,8 +24,9 @@ public final class ChatMessageItemAssociatedData: Equatable {
|
||||
public let channelDiscussionGroup: ChannelDiscussionGroupStatus
|
||||
public let animatedEmojiStickers: [String: [StickerPackItem]]
|
||||
public let forcedResourceStatus: FileMediaResourceStatus?
|
||||
public let currentlyPlayingMessageId: MessageIndex?
|
||||
|
||||
public init(automaticDownloadPeerType: MediaAutoDownloadPeerType, automaticDownloadNetworkType: MediaAutoDownloadNetworkType, isRecentActions: Bool = false, subject: ChatControllerSubject? = nil, contactsPeerIds: Set<PeerId> = Set(), channelDiscussionGroup: ChannelDiscussionGroupStatus = .unknown, animatedEmojiStickers: [String: [StickerPackItem]] = [:], forcedResourceStatus: FileMediaResourceStatus? = nil) {
|
||||
public init(automaticDownloadPeerType: MediaAutoDownloadPeerType, automaticDownloadNetworkType: MediaAutoDownloadNetworkType, isRecentActions: Bool = false, subject: ChatControllerSubject? = nil, contactsPeerIds: Set<PeerId> = Set(), channelDiscussionGroup: ChannelDiscussionGroupStatus = .unknown, animatedEmojiStickers: [String: [StickerPackItem]] = [:], forcedResourceStatus: FileMediaResourceStatus? = nil, currentlyPlayingMessageId: MessageIndex? = nil) {
|
||||
self.automaticDownloadPeerType = automaticDownloadPeerType
|
||||
self.automaticDownloadNetworkType = automaticDownloadNetworkType
|
||||
self.isRecentActions = isRecentActions
|
||||
@ -34,6 +35,7 @@ public final class ChatMessageItemAssociatedData: Equatable {
|
||||
self.channelDiscussionGroup = channelDiscussionGroup
|
||||
self.animatedEmojiStickers = animatedEmojiStickers
|
||||
self.forcedResourceStatus = forcedResourceStatus
|
||||
self.currentlyPlayingMessageId = currentlyPlayingMessageId
|
||||
}
|
||||
|
||||
public static func == (lhs: ChatMessageItemAssociatedData, rhs: ChatMessageItemAssociatedData) -> Bool {
|
||||
|
@ -119,11 +119,11 @@ public func peerMessageMediaPlayerType(_ message: Message) -> MediaManagerPlayer
|
||||
|
||||
public func peerMessagesMediaPlaylistAndItemId(_ message: Message, isRecentActions: Bool, isGlobalSearch: Bool) -> (SharedMediaPlaylistId, SharedMediaPlaylistItemId)? {
|
||||
if isGlobalSearch {
|
||||
return (PeerMessagesMediaPlaylistId.custom, PeerMessagesMediaPlaylistItemId(messageId: message.id))
|
||||
return (PeerMessagesMediaPlaylistId.custom, PeerMessagesMediaPlaylistItemId(messageId: message.id, messageIndex: message.index))
|
||||
} else if isRecentActions {
|
||||
return (PeerMessagesMediaPlaylistId.recentActions(message.id.peerId), PeerMessagesMediaPlaylistItemId(messageId: message.id))
|
||||
return (PeerMessagesMediaPlaylistId.recentActions(message.id.peerId), PeerMessagesMediaPlaylistItemId(messageId: message.id, messageIndex: message.index))
|
||||
} else {
|
||||
return (PeerMessagesMediaPlaylistId.peer(message.id.peerId), PeerMessagesMediaPlaylistItemId(messageId: message.id))
|
||||
return (PeerMessagesMediaPlaylistId.peer(message.id.peerId), PeerMessagesMediaPlaylistItemId(messageId: message.id, messageIndex: message.index))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,14 +135,16 @@ public func areSharedMediaPlaylistItemIdsEqual(_ lhs: SharedMediaPlaylistItemId?
|
||||
|
||||
public struct PeerMessagesMediaPlaylistItemId: SharedMediaPlaylistItemId {
|
||||
public let messageId: MessageId
|
||||
public let messageIndex: MessageIndex
|
||||
|
||||
public init(messageId: MessageId) {
|
||||
public init(messageId: MessageId, messageIndex: MessageIndex) {
|
||||
self.messageId = messageId
|
||||
self.messageIndex = messageIndex
|
||||
}
|
||||
|
||||
public func isEqual(to: SharedMediaPlaylistItemId) -> Bool {
|
||||
if let to = to as? PeerMessagesMediaPlaylistItemId {
|
||||
if self.messageId != to.messageId {
|
||||
if self.messageId != to.messageId || self.messageIndex != to.messageIndex {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
@ -234,6 +234,7 @@ open class ListViewItemNode: ASDisplayNode, AccessibilityFocusableNode {
|
||||
}
|
||||
|
||||
var apparentHeight: CGFloat = 0.0
|
||||
public private(set) var apparentHeightTransition: (CGFloat, CGFloat)?
|
||||
private var _bounds: CGRect = CGRect()
|
||||
private var _position: CGPoint = CGPoint()
|
||||
|
||||
@ -478,6 +479,7 @@ open class ListViewItemNode: ASDisplayNode, AccessibilityFocusableNode {
|
||||
}
|
||||
|
||||
public func addApparentHeightAnimation(_ value: CGFloat, duration: Double, beginAt: Double, update: ((CGFloat, CGFloat) -> Void)? = nil) {
|
||||
self.apparentHeightTransition = (self.apparentHeight, value)
|
||||
let animation = ListViewAnimation(from: self.apparentHeight, to: value, duration: duration, curve: self.preferredAnimationCurve, beginAt: beginAt, update: { [weak self] progress, currentValue in
|
||||
if let strongSelf = self {
|
||||
strongSelf.apparentHeight = currentValue
|
||||
|
@ -1414,7 +1414,7 @@ static CGFloat progressOfSampleBufferInTimeRange(CMSampleBufferRef sampleBuffer,
|
||||
return 64;
|
||||
|
||||
case TGMediaVideoConversionPresetVideoMessage:
|
||||
return 32;
|
||||
return 64;
|
||||
|
||||
case TGMediaVideoConversionPresetAnimation:
|
||||
case TGMediaVideoConversionPresetProfile:
|
||||
|
@ -114,6 +114,18 @@ public enum PeerInfoAvatarListItem: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
var representations: [ImageRepresentationWithReference] {
|
||||
switch self {
|
||||
case .custom:
|
||||
return []
|
||||
case let .topImage(representations, _, _):
|
||||
return representations
|
||||
case let .image(_, representations, _, _):
|
||||
return representations
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var videoRepresentations: [VideoRepresentationWithReference] {
|
||||
switch self {
|
||||
case .custom:
|
||||
@ -1117,6 +1129,9 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
||||
if self.currentIndex >= 0 && self.currentIndex < self.items.count {
|
||||
let preloadSpan: Int = 2
|
||||
for i in max(0, self.currentIndex - preloadSpan) ... min(self.currentIndex + preloadSpan, self.items.count - 1) {
|
||||
if self.items[i].representations.isEmpty {
|
||||
continue
|
||||
}
|
||||
validIds.append(self.items[i].id)
|
||||
var itemNode: PeerInfoAvatarListItemNode?
|
||||
var wasAdded = false
|
||||
|
@ -148,12 +148,18 @@ public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, E>(queue: Que
|
||||
}, initialValues: [:], queue: queue)
|
||||
}
|
||||
|
||||
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11), E> {
|
||||
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11), E> {
|
||||
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11)], combine: { values in
|
||||
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11)
|
||||
}, initialValues: [:], queue: queue)
|
||||
}
|
||||
|
||||
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>, _ s12: Signal<T12, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12), E> {
|
||||
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11), signalOfAny(s12)], combine: { values in
|
||||
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11, values[11] as! T12)
|
||||
}, initialValues: [:], queue: queue)
|
||||
}
|
||||
|
||||
public func combineLatest<T, E>(queue: Queue? = nil, _ signals: [Signal<T, E>]) -> Signal<[T], E> {
|
||||
if signals.count == 0 {
|
||||
return single([T](), E.self)
|
||||
|
@ -77,6 +77,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
|
||||
|
||||
public var tempVoicePlaylistEnded: (() -> Void)?
|
||||
public var tempVoicePlaylistItemChanged: ((SharedMediaPlaylistItem?, SharedMediaPlaylistItem?) -> Void)?
|
||||
public var tempVoicePlaylistCurrentItem: SharedMediaPlaylistItem?
|
||||
|
||||
public var mediaAccessoryPanel: (MediaNavigationAccessoryPanel, MediaManagerPlayerType)?
|
||||
|
||||
@ -150,6 +151,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
|
||||
updatedVoiceItem = playlistStateAndType.1.item
|
||||
}
|
||||
|
||||
strongSelf.tempVoicePlaylistCurrentItem = updatedVoiceItem
|
||||
strongSelf.tempVoicePlaylistItemChanged?(previousVoiceItem, updatedVoiceItem)
|
||||
if let playlistStateAndType = playlistStateAndType {
|
||||
strongSelf.playlistStateAndType = (playlistStateAndType.1.item, playlistStateAndType.1.previousItem, playlistStateAndType.1.nextItem, playlistStateAndType.1.order, playlistStateAndType.2, playlistStateAndType.0)
|
||||
|
@ -663,7 +663,7 @@ public struct PresentationResourcesChat {
|
||||
return theme.image(PresentationResourceKey.chatInstantMessageInfoBackgroundImage.rawValue, { theme in
|
||||
return generateImage(CGSize(width: 24.0, height: 24.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(theme.chat.message.mediaDateAndStatusFillColor.cgColor)
|
||||
context.setFillColor(theme.chat.message.mediaDateAndStatusFillColor.withAlphaComponent(0.3).cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
|
||||
})?.stretchableImage(withLeftCapWidth: 12, topCapHeight: 12)
|
||||
})
|
||||
|
@ -566,6 +566,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
var openMessageByAction: Bool = false
|
||||
|
||||
for media in message.media {
|
||||
if let file = media as? TelegramMediaFile, file.isInstantVideo {
|
||||
if strongSelf.chatDisplayNode.isInputViewFocused {
|
||||
strongSelf.returnInputViewFocus = true
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
}
|
||||
}
|
||||
if let action = media as? TelegramMediaAction {
|
||||
switch action.action {
|
||||
case .pinnedMessageUpdated:
|
||||
@ -4192,6 +4198,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = ChatControllerNode(context: self.context, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, subject: self.subject, controllerInteraction: self.controllerInteraction!, chatPresentationInterfaceState: self.presentationInterfaceState, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, navigationBar: self.navigationBar, backgroundNode: self.chatBackgroundNode, controller: self)
|
||||
|
||||
if let currentItem = self.tempVoicePlaylistCurrentItem {
|
||||
self.chatDisplayNode.historyNode.voicePlaylistItemChanged(nil, currentItem)
|
||||
}
|
||||
|
||||
self.chatDisplayNode.historyNode.didScrollWithOffset = { [weak self] offset, transition, itemNode in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -7032,6 +7042,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
private var returnInputViewFocus = false
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
#if DEBUG
|
||||
if #available(iOSApplicationExtension 12.0, iOS 12.0, *) {
|
||||
@ -7110,16 +7122,26 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.voicePlaylistDidEndTimestamp = CACurrentMediaTime()
|
||||
raiseToListen.activateBasedOnProximity(delay: 0.0)
|
||||
}
|
||||
|
||||
if strongSelf.returnInputViewFocus {
|
||||
strongSelf.returnInputViewFocus = false
|
||||
strongSelf.chatDisplayNode.ensureInputViewFocused()
|
||||
}
|
||||
}
|
||||
self.tempVoicePlaylistItemChanged = { [weak self] previousItem, currentItem in
|
||||
guard let strongSelf = self, case let .peer(peerId) = strongSelf.chatLocation else {
|
||||
return
|
||||
}
|
||||
if let currentItem = currentItem?.id as? PeerMessagesMediaPlaylistItemId, let previousItem = previousItem?.id as? PeerMessagesMediaPlaylistItemId, previousItem.messageId.peerId == peerId, currentItem.messageId.peerId == peerId, currentItem.messageId != previousItem.messageId {
|
||||
if strongSelf.chatDisplayNode.historyNode.isMessageVisibleOnScreen(currentItem.messageId) {
|
||||
strongSelf.navigateToMessage(from: nil, to: .id(currentItem.messageId, nil), scrollPosition: .center(.bottom), rememberInStack: false, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.chatDisplayNode.historyNode.voicePlaylistItemChanged(previousItem, currentItem)
|
||||
// if let currentItem = currentItem?.id as? PeerMessagesMediaPlaylistItemId {
|
||||
// self.controllerInteraction?.currentlyPlayingMessageId = currentItem.messageId
|
||||
// if let previousItem = previousItem?.id as? PeerMessagesMediaPlaylistItemId, previousItem.messageId.peerId == peerId, currentItem.messageId.peerId == peerId, currentItem.messageId != previousItem.messageId {
|
||||
// if strongSelf.chatDisplayNode.historyNode.isMessageVisibleOnScreen(currentItem.messageId) {
|
||||
// strongSelf.navigateToMessage(from: nil, to: .id(currentItem.messageId, nil), scrollPosition: .center(.bottom), rememberInStack: false, animated: true, completion: nil)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
|
||||
} else {
|
||||
selection = .none
|
||||
}
|
||||
groupBucket.append((message, isRead, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id])))
|
||||
groupBucket.append((message, isRead, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: false)))
|
||||
} else {
|
||||
let selection: ChatHistoryMessageSelection
|
||||
if let selectedMessages = selectedMessages {
|
||||
@ -107,7 +107,7 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
|
||||
} else {
|
||||
selection = .none
|
||||
}
|
||||
entries.append(.MessageEntry(message, presentationData, isRead, entry.monthLocation, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id])))
|
||||
entries.append(.MessageEntry(message, presentationData, isRead, entry.monthLocation, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: message.index == associatedData.currentlyPlayingMessageId)))
|
||||
}
|
||||
} else {
|
||||
let selection: ChatHistoryMessageSelection
|
||||
@ -116,7 +116,7 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
|
||||
} else {
|
||||
selection = .none
|
||||
}
|
||||
entries.append(.MessageEntry(message, presentationData, isRead, entry.monthLocation, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id])))
|
||||
entries.append(.MessageEntry(message, presentationData, isRead, entry.monthLocation, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: message.index == associatedData.currentlyPlayingMessageId)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,11 +167,11 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
|
||||
if messages.count > 1, let groupInfo = messages[0].groupInfo {
|
||||
var groupMessages: [(Message, Bool, ChatHistoryMessageSelection, ChatMessageEntryAttributes)] = []
|
||||
for message in messages {
|
||||
groupMessages.append((message, false, .none, ChatMessageEntryAttributes(rank: adminRank, isContact: false, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id])))
|
||||
groupMessages.append((message, false, .none, ChatMessageEntryAttributes(rank: adminRank, isContact: false, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: false)))
|
||||
}
|
||||
entries.insert(.MessageGroupEntry(groupInfo, groupMessages, presentationData), at: 0)
|
||||
} else {
|
||||
entries.insert(.MessageEntry(messages[0], presentationData, false, nil, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: false, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[messages[0].id])), at: 0)
|
||||
entries.insert(.MessageEntry(messages[0], presentationData, false, nil, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: false, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[messages[0].id], isPlaying: false)), at: 0)
|
||||
}
|
||||
|
||||
let replyCount = view.entries.isEmpty ? 0 : 1
|
||||
|
@ -17,12 +17,14 @@ public struct ChatMessageEntryAttributes: Equatable {
|
||||
let isContact: Bool
|
||||
let contentTypeHint: ChatMessageEntryContentType
|
||||
let updatingMedia: ChatUpdatingMessageMedia?
|
||||
let isPlaying: Bool
|
||||
|
||||
init(rank: CachedChannelAdminRank?, isContact: Bool, contentTypeHint: ChatMessageEntryContentType, updatingMedia: ChatUpdatingMessageMedia?) {
|
||||
init(rank: CachedChannelAdminRank?, isContact: Bool, contentTypeHint: ChatMessageEntryContentType, updatingMedia: ChatUpdatingMessageMedia?, isPlaying: Bool) {
|
||||
self.rank = rank
|
||||
self.isContact = isContact
|
||||
self.contentTypeHint = contentTypeHint
|
||||
self.updatingMedia = updatingMedia
|
||||
self.isPlaying = isPlaying
|
||||
}
|
||||
|
||||
public init() {
|
||||
@ -30,6 +32,7 @@ public struct ChatMessageEntryAttributes: Equatable {
|
||||
self.isContact = false
|
||||
self.contentTypeHint = .generic
|
||||
self.updatingMedia = nil
|
||||
self.isPlaying = false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -311,7 +311,7 @@ private final class ChatHistoryTransactionOpaqueState {
|
||||
}
|
||||
}
|
||||
|
||||
private func extractAssociatedData(chatLocation: ChatLocation, view: MessageHistoryView, automaticDownloadNetworkType: MediaAutoDownloadNetworkType, animatedEmojiStickers: [String: [StickerPackItem]], subject: ChatControllerSubject?) -> ChatMessageItemAssociatedData {
|
||||
private func extractAssociatedData(chatLocation: ChatLocation, view: MessageHistoryView, automaticDownloadNetworkType: MediaAutoDownloadNetworkType, animatedEmojiStickers: [String: [StickerPackItem]], subject: ChatControllerSubject?, currentlyPlayingMessageId: MessageIndex?) -> ChatMessageItemAssociatedData {
|
||||
var automaticMediaDownloadPeerType: MediaAutoDownloadPeerType = .channel
|
||||
var contactsPeerIds: Set<PeerId> = Set()
|
||||
var channelDiscussionGroup: ChatMessageItemAssociatedData.ChannelDiscussionGroupStatus = .unknown
|
||||
@ -360,8 +360,7 @@ private func extractAssociatedData(chatLocation: ChatLocation, view: MessageHist
|
||||
}
|
||||
}
|
||||
|
||||
let associatedData = ChatMessageItemAssociatedData(automaticDownloadPeerType: automaticMediaDownloadPeerType, automaticDownloadNetworkType: automaticDownloadNetworkType, isRecentActions: false, subject: subject, contactsPeerIds: contactsPeerIds, channelDiscussionGroup: channelDiscussionGroup, animatedEmojiStickers: animatedEmojiStickers)
|
||||
return associatedData
|
||||
return ChatMessageItemAssociatedData(automaticDownloadPeerType: automaticMediaDownloadPeerType, automaticDownloadNetworkType: automaticDownloadNetworkType, isRecentActions: false, subject: subject, contactsPeerIds: contactsPeerIds, channelDiscussionGroup: channelDiscussionGroup, animatedEmojiStickers: animatedEmojiStickers, currentlyPlayingMessageId: currentlyPlayingMessageId)
|
||||
}
|
||||
|
||||
private extension ChatHistoryLocationInput {
|
||||
@ -528,7 +527,10 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private let currentlyPlayingMessageIdPromise = ValuePromise<MessageIndex?>(nil)
|
||||
private var appliedPlayingMessageId: MessageIndex? = nil
|
||||
|
||||
private(set) var isScrollAtBottomPosition = false
|
||||
public var isScrollAtBottomPositionUpdated: (() -> Void)?
|
||||
|
||||
@ -832,8 +834,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
self.pendingRemovedMessagesPromise.get(),
|
||||
animatedEmojiStickers,
|
||||
customChannelDiscussionReadState,
|
||||
customThreadOutgoingReadState
|
||||
).start(next: { [weak self] update, chatPresentationData, selectedMessages, updatingMedia, networkType, historyAppearsCleared, pendingUnpinnedAllMessages, pendingRemovedMessages, animatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState in
|
||||
customThreadOutgoingReadState,
|
||||
self.currentlyPlayingMessageIdPromise.get()
|
||||
).start(next: { [weak self] update, chatPresentationData, selectedMessages, updatingMedia, networkType, historyAppearsCleared, pendingUnpinnedAllMessages, pendingRemovedMessages, animatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState, currentlyPlayingMessageId in
|
||||
func applyHole() {
|
||||
Queue.mainQueue().async {
|
||||
if let strongSelf = self {
|
||||
@ -916,7 +919,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
reverse = reverseValue
|
||||
}
|
||||
|
||||
let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, animatedEmojiStickers: animatedEmojiStickers, subject: subject)
|
||||
let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, animatedEmojiStickers: animatedEmojiStickers, subject: subject, currentlyPlayingMessageId: currentlyPlayingMessageId)
|
||||
|
||||
let filteredEntries = chatHistoryEntriesForView(location: chatLocation, view: view, includeUnreadEntry: mode == .bubbles, includeEmptyEntry: mode == .bubbles && tagMask == nil, includeChatInfoEntry: mode == .bubbles, includeSearchEntry: includeSearchEntry && tagMask != nil, reverse: reverse, groupMessages: mode == .bubbles, selectedMessages: selectedMessages, presentationData: chatPresentationData, historyAppearsCleared: historyAppearsCleared, pendingUnpinnedAllMessages: pendingUnpinnedAllMessages, pendingRemovedMessages: pendingRemovedMessages, associatedData: associatedData, updatingMedia: updatingMedia, customChannelDiscussionReadState: customChannelDiscussionReadState, customThreadOutgoingReadState: customThreadOutgoingReadState)
|
||||
let lastHeaderId = filteredEntries.last.flatMap { listMessageDateHeaderId(timestamp: $0.index.timestamp) } ?? 0
|
||||
@ -931,7 +934,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
}
|
||||
assert(update.1 >= previousVersion)
|
||||
}
|
||||
|
||||
|
||||
if scrollPosition == nil, let originalScrollPosition = originalScrollPosition {
|
||||
switch originalScrollPosition {
|
||||
case let .index(index, position, _, _, highlight):
|
||||
@ -970,12 +973,22 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: reverse, chatLocation: chatLocation, controllerInteraction: controllerInteraction, scrollPosition: updatedScrollPosition, initialData: initialData?.initialData, keyboardButtonsMessage: view.topTaggedMessages.first, cachedData: initialData?.cachedData, cachedDataMessages: initialData?.cachedDataMessages, readStateData: initialData?.readStateData, flashIndicators: flashIndicators, updatedMessageSelection: previousSelectedMessages != selectedMessages, messageTransitionNode: messageTransitionNode())
|
||||
|
||||
var scrollAnimationCurve: ListViewAnimationCurve? = nil
|
||||
if let strongSelf = self, strongSelf.appliedPlayingMessageId != currentlyPlayingMessageId, let currentlyPlayingMessageId = currentlyPlayingMessageId {
|
||||
updatedScrollPosition = .index(index: .message(currentlyPlayingMessageId), position: .center(.bottom), directionHint: .Down, animated: true, highlight: true)
|
||||
scrollAnimationCurve = .Spring(duration: 0.4)
|
||||
}
|
||||
|
||||
let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: reverse, chatLocation: chatLocation, controllerInteraction: controllerInteraction, scrollPosition: updatedScrollPosition, scrollAnimationCurve: scrollAnimationCurve, initialData: initialData?.initialData, keyboardButtonsMessage: view.topTaggedMessages.first, cachedData: initialData?.cachedData, cachedDataMessages: initialData?.cachedDataMessages, readStateData: initialData?.readStateData, flashIndicators: flashIndicators, updatedMessageSelection: previousSelectedMessages != selectedMessages, messageTransitionNode: messageTransitionNode())
|
||||
let mappedTransition = mappedChatHistoryViewListTransition(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, transition: rawTransition)
|
||||
Queue.mainQueue().async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if strongSelf.appliedPlayingMessageId != currentlyPlayingMessageId {
|
||||
strongSelf.appliedPlayingMessageId = currentlyPlayingMessageId
|
||||
}
|
||||
strongSelf.enqueueHistoryViewTransition(mappedTransition)
|
||||
}
|
||||
}
|
||||
@ -2290,6 +2303,15 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
self.selectionScrollDisplayLink?.isPaused = false
|
||||
}
|
||||
|
||||
|
||||
func voicePlaylistItemChanged(_ previousItem: SharedMediaPlaylistItem?, _ currentItem: SharedMediaPlaylistItem?) -> Void {
|
||||
if let currentItem = currentItem?.id as? PeerMessagesMediaPlaylistItemId {
|
||||
self.currentlyPlayingMessageIdPromise.set(currentItem.messageIndex)
|
||||
} else {
|
||||
self.currentlyPlayingMessageIdPromise.set(nil)
|
||||
}
|
||||
}
|
||||
|
||||
private var currentSendAnimationCorrelationId: Int64?
|
||||
func setCurrentSendAnimationCorrelationId(_ value: Int64?) {
|
||||
self.currentSendAnimationCorrelationId = value
|
||||
|
@ -5,7 +5,7 @@ import SwiftSignalKit
|
||||
import Display
|
||||
import UniversalMediaPlayer
|
||||
|
||||
private let textFont = Font.regular(11.0)
|
||||
private let textFont = Font.with(size: 11.0, design: .regular, weight: .regular, traits: [.monospacedNumbers])
|
||||
|
||||
private struct ChatInstantVideoMessageDurationNodeState: Equatable {
|
||||
let hours: Int32?
|
||||
|
@ -520,8 +520,9 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
initialWidth = initialImageWidth + horizontalInsets.left + horizontalInsets.right
|
||||
refineContentImageLayout = refineLayout
|
||||
} else if file.isInstantVideo {
|
||||
let displaySize = CGSize(width: 212.0, height: 212.0)
|
||||
let automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, authorPeerId: message.author?.id, contactsPeerIds: associatedData.contactsPeerIds, media: file)
|
||||
let (videoLayout, apply) = contentInstantVideoLayout(ChatMessageBubbleContentItem(context: context, controllerInteraction: controllerInteraction, message: message, read: messageRead, chatLocation: chatLocation, presentationData: presentationData, associatedData: associatedData, attributes: attributes, isItemPinned: message.tags.contains(.pinned) && !isReplyThread, isItemEdited: false), constrainedSize.width - horizontalInsets.left - horizontalInsets.right, CGSize(width: 212.0, height: 212.0), .bubble, automaticDownload)
|
||||
let (videoLayout, apply) = contentInstantVideoLayout(ChatMessageBubbleContentItem(context: context, controllerInteraction: controllerInteraction, message: message, read: messageRead, chatLocation: chatLocation, presentationData: presentationData, associatedData: associatedData, attributes: attributes, isItemPinned: message.tags.contains(.pinned) && !isReplyThread, isItemEdited: false), constrainedSize.width - horizontalInsets.left - horizontalInsets.right, displaySize, displaySize, 0.0, .bubble, automaticDownload)
|
||||
initialWidth = videoLayout.contentSize.width + videoLayout.overflowLeft + videoLayout.overflowRight
|
||||
contentInstantVideoSizeAndApply = (videoLayout, apply)
|
||||
} else if file.isVideo {
|
||||
|
@ -31,8 +31,12 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
private var swipeToReplyNode: ChatMessageSwipeToReplyNode?
|
||||
private var swipeToReplyFeedback: HapticFeedback?
|
||||
|
||||
private var appliedParams: ListViewItemLayoutParams?
|
||||
private var appliedItem: ChatMessageItem?
|
||||
private var appliedForwardInfo: (Peer?, String?)?
|
||||
private var appliedHasAvatar = false
|
||||
private var appliedCurrentlyPlaying = false
|
||||
private var appliedAutomaticDownload = false
|
||||
|
||||
private var forwardInfoNode: ChatMessageForwardInfoNode?
|
||||
private var forwardBackgroundNode: ASImageNode?
|
||||
@ -217,6 +221,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
|
||||
let currentItem = self.appliedItem
|
||||
let currentForwardInfo = self.appliedForwardInfo
|
||||
let currentPlaying = self.appliedCurrentlyPlaying
|
||||
|
||||
return { item, params, mergedTop, mergedBottom, dateHeaderAtBottom in
|
||||
let accessibilityData = ChatMessageAccessibilityData(item: item, isSelected: nil)
|
||||
@ -318,7 +323,13 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
deliveryFailedInset += 24.0
|
||||
}
|
||||
|
||||
let displaySize = layoutConstants.instantVideo.dimensions
|
||||
var isPlaying = false
|
||||
var displaySize = layoutConstants.instantVideo.dimensions
|
||||
let maximumDisplaySize = CGSize(width: params.width - 20.0, height: params.width - 20.0)
|
||||
if item.associatedData.currentlyPlayingMessageId == item.message.index {
|
||||
isPlaying = true
|
||||
displaySize = maximumDisplaySize
|
||||
}
|
||||
|
||||
var automaticDownload = true
|
||||
for media in item.message.media {
|
||||
@ -332,7 +343,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
isReplyThread = true
|
||||
}
|
||||
|
||||
let (videoLayout, videoApply) = makeVideoLayout(ChatMessageBubbleContentItem(context: item.context, controllerInteraction: item.controllerInteraction, message: item.message, read: item.read, chatLocation: item.chatLocation, presentationData: item.presentationData, associatedData: item.associatedData, attributes: item.content.firstMessageAttributes, isItemPinned: item.message.tags.contains(.pinned) && !isReplyThread, isItemEdited: false), params.width - params.leftInset - params.rightInset - avatarInset, displaySize, .free, automaticDownload)
|
||||
let (videoLayout, videoApply) = makeVideoLayout(ChatMessageBubbleContentItem(context: item.context, controllerInteraction: item.controllerInteraction, message: item.message, read: item.read, chatLocation: item.chatLocation, presentationData: item.presentationData, associatedData: item.associatedData, attributes: item.content.firstMessageAttributes, isItemPinned: item.message.tags.contains(.pinned) && !isReplyThread, isItemEdited: false), params.width - params.leftInset - params.rightInset - avatarInset, displaySize, maximumDisplaySize, isPlaying ? 1.0 : 0.0, .free, automaticDownload)
|
||||
|
||||
let videoFrame = CGRect(origin: CGPoint(x: (incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + avatarInset + layoutConstants.bubble.contentInsets.left) : (params.width - params.rightInset - videoLayout.contentSize.width - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left - deliveryFailedInset)), y: 0.0), size: videoLayout.contentSize)
|
||||
|
||||
@ -495,30 +506,40 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
|
||||
return (ListViewItemNodeLayout(contentSize: layoutSize, insets: layoutInsets), { [weak self] animation, _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.contextSourceNode.frame = CGRect(origin: CGPoint(), size: layoutSize)
|
||||
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: layoutSize)
|
||||
strongSelf.contextSourceNode.contentNode.frame = CGRect(origin: CGPoint(), size: layoutSize)
|
||||
strongSelf.messageAccessibilityArea.frame = CGRect(origin: CGPoint(), size: layoutSize)
|
||||
|
||||
strongSelf.appliedItem = item
|
||||
strongSelf.appliedForwardInfo = (forwardSource, forwardAuthorSignature)
|
||||
|
||||
strongSelf.updateAccessibilityData(accessibilityData)
|
||||
|
||||
let transition: ContainedViewLayoutTransition
|
||||
if animation.isAnimated {
|
||||
transition = .animated(duration: 0.2, curve: .spring)
|
||||
} else {
|
||||
transition = .immediate
|
||||
}
|
||||
strongSelf.interactiveVideoNode.frame = videoFrame
|
||||
|
||||
strongSelf.contextSourceNode.frame = CGRect(origin: CGPoint(), size: layoutSize)
|
||||
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: layoutSize)
|
||||
strongSelf.contextSourceNode.contentNode.frame = CGRect(origin: CGPoint(), size: layoutSize)
|
||||
strongSelf.messageAccessibilityArea.frame = CGRect(origin: CGPoint(), size: layoutSize)
|
||||
|
||||
strongSelf.appliedParams = params
|
||||
strongSelf.appliedItem = item
|
||||
strongSelf.appliedHasAvatar = hasAvatar
|
||||
strongSelf.appliedForwardInfo = (forwardSource, forwardAuthorSignature)
|
||||
strongSelf.appliedCurrentlyPlaying = isPlaying
|
||||
|
||||
strongSelf.updateAccessibilityData(accessibilityData)
|
||||
|
||||
|
||||
|
||||
let videoLayoutData: ChatMessageInstantVideoItemLayoutData
|
||||
if incoming {
|
||||
videoLayoutData = .constrained(left: 0.0, right: max(0.0, availableContentWidth - videoFrame.width))
|
||||
} else {
|
||||
videoLayoutData = .constrained(left: max(0.0, availableContentWidth - videoFrame.width), right: 0.0)
|
||||
}
|
||||
videoApply(videoLayoutData, transition)
|
||||
|
||||
if currentItem != nil && currentPlaying != isPlaying {
|
||||
} else {
|
||||
strongSelf.interactiveVideoNode.frame = videoFrame
|
||||
videoApply(videoLayoutData, transition)
|
||||
}
|
||||
|
||||
strongSelf.contextSourceNode.contentRect = videoFrame
|
||||
strongSelf.containerNode.targetNodeForActivationProgressContentRect = strongSelf.contextSourceNode.contentRect
|
||||
@ -1024,4 +1045,75 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
override func addAccessoryItemNode(_ accessoryItemNode: ListViewAccessoryItemNode) {
|
||||
self.contextSourceNode.contentNode.addSubnode(accessoryItemNode)
|
||||
}
|
||||
|
||||
override func animateFrameTransition(_ progress: CGFloat, _ currentValue: CGFloat) {
|
||||
super.animateFrameTransition(progress, currentValue)
|
||||
|
||||
guard let item = self.appliedItem, let params = self.appliedParams, progress > 0.0, let (initialHeight, targetHeight) = self.apparentHeightTransition else {
|
||||
return
|
||||
}
|
||||
|
||||
let layoutConstants = chatMessageItemLayoutConstants(self.layoutConstants, params: params, presentationData: item.presentationData)
|
||||
let incoming = item.message.effectivelyIncoming(item.context.account.peerId)
|
||||
|
||||
var isReplyThread = false
|
||||
if case .replyThread = item.chatLocation {
|
||||
isReplyThread = true
|
||||
}
|
||||
|
||||
var isPlaying = false
|
||||
var displaySize = layoutConstants.instantVideo.dimensions
|
||||
let maximumDisplaySize = CGSize(width: params.width - 20.0, height: params.width - 20.0)
|
||||
if item.associatedData.currentlyPlayingMessageId == item.message.index {
|
||||
isPlaying = true
|
||||
}
|
||||
|
||||
let avatarInset: CGFloat
|
||||
if self.appliedHasAvatar {
|
||||
avatarInset = layoutConstants.avatarDiameter
|
||||
} else {
|
||||
avatarInset = 0.0
|
||||
}
|
||||
|
||||
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
|
||||
var deliveryFailedInset: CGFloat = 0.0
|
||||
if isFailed {
|
||||
deliveryFailedInset += 24.0
|
||||
}
|
||||
|
||||
let makeVideoLayout = self.interactiveVideoNode.asyncLayout()
|
||||
|
||||
let initialSize: CGSize
|
||||
let targetSize: CGSize
|
||||
let animationProgress: CGFloat = (currentValue - initialHeight) / (targetHeight - initialHeight)
|
||||
let scaleProgress: CGFloat
|
||||
if currentValue < targetHeight {
|
||||
initialSize = displaySize
|
||||
targetSize = maximumDisplaySize
|
||||
scaleProgress = animationProgress
|
||||
} else if currentValue > targetHeight {
|
||||
initialSize = maximumDisplaySize
|
||||
targetSize = displaySize
|
||||
scaleProgress = 1.0 - animationProgress
|
||||
} else {
|
||||
initialSize = isPlaying ? maximumDisplaySize : displaySize
|
||||
targetSize = initialSize
|
||||
scaleProgress = isPlaying ? 1.0 : 0.0
|
||||
}
|
||||
displaySize = CGSize(width: initialSize.width + (targetSize.width - initialSize.width) * animationProgress, height: initialSize.height + (targetSize.height - initialSize.height) * animationProgress)
|
||||
|
||||
let (videoLayout, videoApply) = makeVideoLayout(ChatMessageBubbleContentItem(context: item.context, controllerInteraction: item.controllerInteraction, message: item.message, read: item.read, chatLocation: item.chatLocation, presentationData: item.presentationData, associatedData: item.associatedData, attributes: item.content.firstMessageAttributes, isItemPinned: item.message.tags.contains(.pinned) && !isReplyThread, isItemEdited: false), params.width - params.leftInset - params.rightInset - avatarInset, displaySize, maximumDisplaySize, scaleProgress, .free, self.appliedAutomaticDownload)
|
||||
|
||||
let availableContentWidth = params.width - params.leftInset - params.rightInset - layoutConstants.bubble.edgeInset * 2.0 - avatarInset - layoutConstants.bubble.contentInsets.left
|
||||
let videoFrame = CGRect(origin: CGPoint(x: (incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + avatarInset + layoutConstants.bubble.contentInsets.left) : (params.width - params.rightInset - videoLayout.contentSize.width - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left - deliveryFailedInset)), y: 0.0), size: videoLayout.contentSize)
|
||||
self.interactiveVideoNode.frame = videoFrame
|
||||
|
||||
let videoLayoutData: ChatMessageInstantVideoItemLayoutData
|
||||
if incoming {
|
||||
videoLayoutData = .constrained(left: 0.0, right: max(0.0, availableContentWidth - videoFrame.width))
|
||||
} else {
|
||||
videoLayoutData = .constrained(left: max(0.0, availableContentWidth - videoFrame.width), right: 0.0)
|
||||
}
|
||||
videoApply(videoLayoutData, .immediate)
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,8 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
private var animating = false
|
||||
|
||||
override init() {
|
||||
self.secretVideoPlaceholderBackground = ASImageNode()
|
||||
self.secretVideoPlaceholderBackground.isLayerBacked = true
|
||||
@ -130,7 +132,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
self.view.addGestureRecognizer(recognizer)
|
||||
}
|
||||
|
||||
func asyncLayout() -> (_ item: ChatMessageBubbleContentItem, _ width: CGFloat, _ displaySize: CGSize, _ statusType: ChatMessageInteractiveInstantVideoNodeStatusType, _ automaticDownload: Bool) -> (ChatMessageInstantVideoItemLayoutResult, (ChatMessageInstantVideoItemLayoutData, ContainedViewLayoutTransition) -> Void) {
|
||||
func asyncLayout() -> (_ item: ChatMessageBubbleContentItem, _ width: CGFloat, _ displaySize: CGSize, _ maximumDisplaySize: CGSize, _ scaleProgress: CGFloat, _ statusType: ChatMessageInteractiveInstantVideoNodeStatusType, _ automaticDownload: Bool) -> (ChatMessageInstantVideoItemLayoutResult, (ChatMessageInstantVideoItemLayoutData, ContainedViewLayoutTransition) -> Void) {
|
||||
let previousFile = self.media
|
||||
|
||||
let currentItem = self.item
|
||||
@ -138,7 +140,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
|
||||
let makeDateAndStatusLayout = self.dateAndStatusNode.asyncLayout()
|
||||
|
||||
return { item, width, displaySize, statusDisplayType, automaticDownload in
|
||||
return { item, width, displaySize, maximumDisplaySize, scaleProgress, statusDisplayType, automaticDownload in
|
||||
var secretVideoPlaceholderBackgroundImage: UIImage?
|
||||
var updatedInfoBackgroundImage: UIImage?
|
||||
var updatedMuteIconImage: UIImage?
|
||||
@ -163,7 +165,8 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
secretVideoPlaceholderBackgroundImage = PresentationResourcesChat.chatInstantVideoBackgroundImage(theme.theme, wallpaper: !theme.wallpaper.isEmpty)
|
||||
}
|
||||
|
||||
let imageSize = displaySize
|
||||
let imageSize = maximumDisplaySize
|
||||
let imageScale = displaySize.width / maximumDisplaySize.width
|
||||
|
||||
let updatedMessageId = item.message.id != currentItem?.message.id
|
||||
|
||||
@ -294,20 +297,24 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
|
||||
let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.context, item.presentationData, edited && !sentViaBot, viewCount, dateText, statusType, CGSize(width: max(1.0, maxDateAndStatusWidth), height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, item.message.isSelfExpiring)
|
||||
|
||||
var contentSize = imageSize
|
||||
var displayVideoFrame = videoFrame
|
||||
displayVideoFrame.size.width *= imageScale
|
||||
displayVideoFrame.size.height *= imageScale
|
||||
|
||||
var contentSize = displayVideoFrame.size
|
||||
var dateAndStatusOverflow = false
|
||||
if case .bubble = statusDisplayType, videoFrame.maxX + dateAndStatusSize.width > width {
|
||||
if case .bubble = statusDisplayType, displayVideoFrame.maxX + dateAndStatusSize.width > width {
|
||||
contentSize.height += dateAndStatusSize.height + 2.0
|
||||
contentSize.width = max(contentSize.width, dateAndStatusSize.width)
|
||||
dateAndStatusOverflow = true
|
||||
}
|
||||
|
||||
let result = ChatMessageInstantVideoItemLayoutResult(contentSize: contentSize, overflowLeft: 0.0, overflowRight: dateAndStatusOverflow ? 0.0 : (max(0.0, floor(videoFrame.midX) + 55.0 + dateAndStatusSize.width - videoFrame.width)))
|
||||
let result = ChatMessageInstantVideoItemLayoutResult(contentSize: contentSize, overflowLeft: 0.0, overflowRight: dateAndStatusOverflow ? 0.0 : (max(0.0, floorToScreenPixels(videoFrame.midX) + 55.0 + dateAndStatusSize.width - videoFrame.width)))
|
||||
|
||||
return (result, { [weak self] layoutData, transition in
|
||||
if let strongSelf = self {
|
||||
strongSelf.item = item
|
||||
strongSelf.videoFrame = videoFrame
|
||||
strongSelf.videoFrame = displayVideoFrame
|
||||
strongSelf.secretProgressIcon = secretProgressIcon
|
||||
strongSelf.automaticDownload = automaticDownload
|
||||
|
||||
@ -327,10 +334,10 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
|
||||
if let infoBackgroundImage = strongSelf.infoBackgroundNode.image, let muteImage = strongSelf.muteIconNode.image {
|
||||
let infoWidth = muteImage.size.width
|
||||
let infoBackgroundFrame = CGRect(origin: CGPoint(x: floor(videoFrame.minX + (videoFrame.size.width - infoWidth) / 2.0), y: videoFrame.maxY - infoBackgroundImage.size.height - 8.0), size: CGSize(width: infoWidth, height: infoBackgroundImage.size.height))
|
||||
transition.updateFrame(node: strongSelf.infoBackgroundNode, frame: infoBackgroundFrame)
|
||||
let infoBackgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(displayVideoFrame.minX + (displayVideoFrame.size.width - infoWidth) / 2.0), y: displayVideoFrame.maxY - infoBackgroundImage.size.height - 8.0), size: CGSize(width: infoWidth, height: infoBackgroundImage.size.height))
|
||||
strongSelf.infoBackgroundNode.frame = infoBackgroundFrame
|
||||
let muteIconFrame = CGRect(origin: CGPoint(x: infoBackgroundFrame.width - muteImage.size.width, y: 0.0), size: muteImage.size)
|
||||
transition.updateFrame(node: strongSelf.muteIconNode, frame: muteIconFrame)
|
||||
strongSelf.muteIconNode.frame = muteIconFrame
|
||||
}
|
||||
|
||||
if let updatedFile = updatedFile, updatedMedia {
|
||||
@ -340,21 +347,21 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
strongSelf.fetchedThumbnailDisposable.set(nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dateAndStatusApply(false)
|
||||
switch layoutData {
|
||||
case let .unconstrained(width):
|
||||
let dateAndStatusOrigin: CGPoint
|
||||
if dateAndStatusOverflow {
|
||||
dateAndStatusOrigin = CGPoint(x: videoFrame.minX - 4.0, y: videoFrame.maxY + 2.0)
|
||||
dateAndStatusOrigin = CGPoint(x: displayVideoFrame.minX - 4.0, y: displayVideoFrame.maxY + 2.0)
|
||||
} else {
|
||||
dateAndStatusOrigin = CGPoint(x: min(floor(videoFrame.midX) + 55.0, width - dateAndStatusSize.width - 4.0), y: videoFrame.height - dateAndStatusSize.height)
|
||||
dateAndStatusOrigin = CGPoint(x: min(floorToScreenPixels(displayVideoFrame.midX) + 55.0 + 25.0 * scaleProgress, width - dateAndStatusSize.width - 4.0), y: displayVideoFrame.height - dateAndStatusSize.height)
|
||||
}
|
||||
strongSelf.dateAndStatusNode.frame = CGRect(origin: dateAndStatusOrigin, size: dateAndStatusSize)
|
||||
case let .constrained(_, right):
|
||||
strongSelf.dateAndStatusNode.frame = CGRect(origin: CGPoint(x: min(floor(videoFrame.midX) + 55.0, videoFrame.maxX + right - dateAndStatusSize.width - 4.0), y: videoFrame.maxY - dateAndStatusSize.height), size: dateAndStatusSize)
|
||||
strongSelf.dateAndStatusNode.frame = CGRect(origin: CGPoint(x: min(floorToScreenPixels(displayVideoFrame.midX) + 55.0 + 25.0 * scaleProgress, displayVideoFrame.maxX + right - dateAndStatusSize.width - 4.0), y: displayVideoFrame.maxY - dateAndStatusSize.height), size: dateAndStatusSize)
|
||||
}
|
||||
|
||||
|
||||
var updatedPlayerStatusSignal: Signal<MediaPlayerStatus?, NoError>?
|
||||
if let telegramFile = updatedFile {
|
||||
if updatedMedia {
|
||||
@ -471,7 +478,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
if let durationNode = strongSelf.durationNode {
|
||||
durationNode.frame = CGRect(origin: CGPoint(x: videoFrame.midX - 56.0, y: videoFrame.maxY - 18.0), size: CGSize(width: 1.0, height: 1.0))
|
||||
durationNode.frame = CGRect(origin: CGPoint(x: displayVideoFrame.midX - 56.0 - 25.0 * scaleProgress, y: displayVideoFrame.maxY - 18.0), size: CGSize(width: 1.0, height: 1.0))
|
||||
durationNode.isSeen = !notConsumed
|
||||
let size = durationNode.size
|
||||
if let durationBackgroundNode = strongSelf.durationBackgroundNode, size.width > 1.0 {
|
||||
@ -481,12 +488,14 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
if let videoNode = strongSelf.videoNode {
|
||||
videoNode.frame = videoFrame
|
||||
videoNode.bounds = CGRect(origin: CGPoint(), size: videoFrame.size)
|
||||
videoNode.transform = CATransform3DMakeScale(imageScale, imageScale, 1.0)
|
||||
videoNode.position = displayVideoFrame.center
|
||||
videoNode.updateLayout(size: arguments.boundingSize, transition: .immediate)
|
||||
}
|
||||
strongSelf.secretVideoPlaceholderBackground.frame = videoFrame
|
||||
strongSelf.secretVideoPlaceholderBackground.frame = displayVideoFrame
|
||||
|
||||
let placeholderFrame = videoFrame.insetBy(dx: 2.0, dy: 2.0)
|
||||
let placeholderFrame = displayVideoFrame.insetBy(dx: 2.0, dy: 2.0)
|
||||
strongSelf.secretVideoPlaceholder.frame = placeholderFrame
|
||||
let makeSecretPlaceholderLayout = strongSelf.secretVideoPlaceholder.asyncLayout()
|
||||
let arguments = TransformImageArguments(corners: ImageCorners(radius: placeholderFrame.size.width / 2.0), imageSize: placeholderFrame.size, boundingSize: placeholderFrame.size, intrinsicInsets: UIEdgeInsets())
|
||||
@ -599,7 +608,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
if self.statusNode == nil {
|
||||
let statusNode = RadialStatusNode(backgroundNodeColor: item.presentationData.theme.theme.chat.message.mediaOverlayControlColors.fillColor)
|
||||
self.isUserInteractionEnabled = false
|
||||
statusNode.frame = CGRect(origin: CGPoint(x: videoFrame.origin.x + floor((videoFrame.size.width - 50.0) / 2.0), y: videoFrame.origin.y + floor((videoFrame.size.height - 50.0) / 2.0)), size: CGSize(width: 50.0, height: 50.0))
|
||||
statusNode.frame = CGRect(origin: CGPoint(x: videoFrame.origin.x + floorToScreenPixels((videoFrame.size.width - 50.0) / 2.0), y: videoFrame.origin.y + floorToScreenPixels((videoFrame.size.height - 50.0) / 2.0)), size: CGSize(width: 50.0, height: 50.0))
|
||||
self.statusNode = statusNode
|
||||
self.addSubnode(statusNode)
|
||||
}
|
||||
@ -671,7 +680,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
if let current = self.playbackStatusNode {
|
||||
playbackStatusNode = current
|
||||
} else {
|
||||
playbackStatusNode = InstantVideoRadialStatusNode(color: UIColor(white: 1.0, alpha: 0.8))
|
||||
playbackStatusNode = InstantVideoRadialStatusNode(color: UIColor(white: 1.0, alpha: 0.6))
|
||||
self.addSubnode(playbackStatusNode)
|
||||
self.playbackStatusNode = playbackStatusNode
|
||||
}
|
||||
@ -800,16 +809,16 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
return nil
|
||||
}
|
||||
|
||||
static func asyncLayout(_ node: ChatMessageInteractiveInstantVideoNode?) -> (_ item: ChatMessageBubbleContentItem, _ width: CGFloat, _ displaySize: CGSize, _ statusType: ChatMessageInteractiveInstantVideoNodeStatusType, _ automaticDownload: Bool) -> (ChatMessageInstantVideoItemLayoutResult, (ChatMessageInstantVideoItemLayoutData, ContainedViewLayoutTransition) -> ChatMessageInteractiveInstantVideoNode) {
|
||||
static func asyncLayout(_ node: ChatMessageInteractiveInstantVideoNode?) -> (_ item: ChatMessageBubbleContentItem, _ width: CGFloat, _ displaySize: CGSize, _ maximumDisplaySize: CGSize, _ scaleProgress: CGFloat, _ statusType: ChatMessageInteractiveInstantVideoNodeStatusType, _ automaticDownload: Bool) -> (ChatMessageInstantVideoItemLayoutResult, (ChatMessageInstantVideoItemLayoutData, ContainedViewLayoutTransition) -> ChatMessageInteractiveInstantVideoNode) {
|
||||
let makeLayout = node?.asyncLayout()
|
||||
return { item, width, displaySize, statusType, automaticDownload in
|
||||
return { item, width, displaySize, maximumDisplaySize, scaleProgress, statusType, automaticDownload in
|
||||
var createdNode: ChatMessageInteractiveInstantVideoNode?
|
||||
let sizeAndApplyLayout: (ChatMessageInstantVideoItemLayoutResult, (ChatMessageInstantVideoItemLayoutData, ContainedViewLayoutTransition) -> Void)
|
||||
if let makeLayout = makeLayout {
|
||||
sizeAndApplyLayout = makeLayout(item, width, displaySize, statusType, automaticDownload)
|
||||
sizeAndApplyLayout = makeLayout(item, width, displaySize, maximumDisplaySize, scaleProgress, statusType, automaticDownload)
|
||||
} else {
|
||||
let node = ChatMessageInteractiveInstantVideoNode()
|
||||
sizeAndApplyLayout = node.asyncLayout()(item, width, displaySize, statusType, automaticDownload)
|
||||
sizeAndApplyLayout = node.asyncLayout()(item, width, displaySize, maximumDisplaySize, scaleProgress, statusType, automaticDownload)
|
||||
createdNode = node
|
||||
}
|
||||
return (sizeAndApplyLayout.0, { [weak node] layoutData, transition in
|
||||
|
@ -93,7 +93,7 @@ final class InstantVideoRadialStatusNode: ASDisplayNode {
|
||||
|
||||
let lineWidth: CGFloat = 4.0
|
||||
|
||||
let pathDiameter = bounds.size.width - lineWidth
|
||||
let pathDiameter = bounds.size.width - lineWidth - 8.0
|
||||
|
||||
let path = UIBezierPath(arcCenter: CGPoint(x: bounds.size.width / 2.0, y: bounds.size.height / 2.0), radius: pathDiameter / 2.0, startAngle: startAngle, endAngle: endAngle, clockwise:true)
|
||||
path.lineWidth = lineWidth
|
||||
|
@ -37,7 +37,7 @@ final class OverlayInstantVideoDecoration: UniversalVideoDecoration {
|
||||
self.contentContainerNode.clipsToBounds = true
|
||||
|
||||
self.foregroundContainerNode = ASDisplayNode()
|
||||
self.progressNode = InstantVideoRadialStatusNode(color: UIColor(white: 1.0, alpha: 0.8))
|
||||
self.progressNode = InstantVideoRadialStatusNode(color: UIColor(white: 1.0, alpha: 0.6))
|
||||
self.foregroundContainerNode.addSubnode(self.progressNode)
|
||||
self.foregroundNode = self.foregroundContainerNode
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ final class MessageMediaPlaylistItem: SharedMediaPlaylistItem {
|
||||
let message: Message
|
||||
|
||||
init(message: Message) {
|
||||
self.id = PeerMessagesMediaPlaylistItemId(messageId: message.id)
|
||||
self.id = PeerMessagesMediaPlaylistItemId(messageId: message.id, messageIndex: message.index)
|
||||
self.message = message
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ import Display
|
||||
import MergeLists
|
||||
import AccountContext
|
||||
|
||||
func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toView: ChatHistoryView, reason: ChatHistoryViewTransitionReason, reverse: Bool, chatLocation: ChatLocation, controllerInteraction: ChatControllerInteraction, scrollPosition: ChatHistoryViewScrollPosition?, initialData: InitialMessageHistoryData?, keyboardButtonsMessage: Message?, cachedData: CachedPeerData?, cachedDataMessages: [MessageId: Message]?, readStateData: [PeerId: ChatHistoryCombinedInitialReadStateData]?, flashIndicators: Bool, updatedMessageSelection: Bool, messageTransitionNode: ChatMessageTransitionNode?) -> ChatHistoryViewTransition {
|
||||
func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toView: ChatHistoryView, reason: ChatHistoryViewTransitionReason, reverse: Bool, chatLocation: ChatLocation, controllerInteraction: ChatControllerInteraction, scrollPosition: ChatHistoryViewScrollPosition?, scrollAnimationCurve: ListViewAnimationCurve?, initialData: InitialMessageHistoryData?, keyboardButtonsMessage: Message?, cachedData: CachedPeerData?, cachedDataMessages: [MessageId: Message]?, readStateData: [PeerId: ChatHistoryCombinedInitialReadStateData]?, flashIndicators: Bool, updatedMessageSelection: Bool, messageTransitionNode: ChatMessageTransitionNode?) -> ChatHistoryViewTransition {
|
||||
var mergeResult: (deleteIndices: [Int], indicesAndItems: [(Int, ChatHistoryEntry, Int?)], updateIndices: [(Int, ChatHistoryEntry, Int)])
|
||||
let allUpdated = fromView?.associatedData != toView.associatedData
|
||||
if reverse {
|
||||
@ -137,13 +137,15 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie
|
||||
var scrolledToIndex: MessageHistoryAnchorIndex?
|
||||
var scrolledToSomeIndex = false
|
||||
|
||||
let curve: ListViewAnimationCurve = scrollAnimationCurve ?? .Default(duration: nil)
|
||||
|
||||
if let scrollPosition = scrollPosition {
|
||||
switch scrollPosition {
|
||||
case let .unread(unreadIndex):
|
||||
var index = toView.filteredEntries.count - 1
|
||||
for entry in toView.filteredEntries {
|
||||
if case .UnreadEntry = entry {
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: .bottom(0.0), animated: false, curve: .Default(duration: nil), directionHint: .Down)
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: .bottom(0.0), animated: false, curve: curve, directionHint: .Down)
|
||||
break
|
||||
}
|
||||
index -= 1
|
||||
@ -153,7 +155,7 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie
|
||||
var index = toView.filteredEntries.count - 1
|
||||
for entry in toView.filteredEntries {
|
||||
if entry.index >= unreadIndex {
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: .bottom(0.0), animated: false, curve: .Default(duration: nil), directionHint: .Down)
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: .bottom(0.0), animated: false, curve: curve, directionHint: .Down)
|
||||
break
|
||||
}
|
||||
index -= 1
|
||||
@ -164,7 +166,7 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie
|
||||
var index = 0
|
||||
for entry in toView.filteredEntries.reversed() {
|
||||
if entry.index < unreadIndex {
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: .bottom(0.0), animated: false, curve: .Default(duration: nil), directionHint: .Down)
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: .bottom(0.0), animated: false, curve: curve, directionHint: .Down)
|
||||
break
|
||||
}
|
||||
index += 1
|
||||
@ -174,7 +176,7 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie
|
||||
var index = toView.filteredEntries.count - 1
|
||||
for entry in toView.filteredEntries {
|
||||
if entry.index >= scrollIndex {
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: .top(relativeOffset), animated: false, curve: .Default(duration: nil), directionHint: .Down)
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: .top(relativeOffset), animated: false, curve: curve, directionHint: .Down)
|
||||
break
|
||||
}
|
||||
index -= 1
|
||||
@ -184,7 +186,7 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie
|
||||
var index = 0
|
||||
for entry in toView.filteredEntries.reversed() {
|
||||
if entry.index < scrollIndex {
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: .top(0.0), animated: false, curve: .Default(duration: nil), directionHint: .Down)
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: .top(0.0), animated: false, curve: curve, directionHint: .Down)
|
||||
break
|
||||
}
|
||||
index += 1
|
||||
@ -197,7 +199,7 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie
|
||||
var index = toView.filteredEntries.count - 1
|
||||
for entry in toView.filteredEntries {
|
||||
if scrollIndex.isLessOrEqual(to: entry.index) {
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: .Default(duration: nil), directionHint: directionHint)
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: curve, directionHint: directionHint)
|
||||
break
|
||||
}
|
||||
index -= 1
|
||||
@ -208,7 +210,7 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie
|
||||
for entry in toView.filteredEntries.reversed() {
|
||||
if !scrollIndex.isLess(than: entry.index) {
|
||||
scrolledToSomeIndex = true
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: .Default(duration: nil), directionHint: directionHint)
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: curve, directionHint: directionHint)
|
||||
break
|
||||
}
|
||||
index += 1
|
||||
|
Loading…
x
Reference in New Issue
Block a user