Video avatar fixes

This commit is contained in:
Ilya Laktyushin 2020-06-26 09:46:00 +03:00
parent 33538b46b9
commit cd9199578f
9 changed files with 128 additions and 84 deletions

View File

@ -317,7 +317,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
var deleteItems: [Int] = []
var insertItems: [GalleryPagerInsertItem] = []
var previousIndexById: [AnyHashable: Int] = [:]
var validIds = Set(items.map { $0.id })
let validIds = Set(items.map { $0.id })
for i in 0 ..< self.items.count {
previousIndexById[self.items[i].id] = i
@ -327,7 +327,11 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
}
for i in 0 ..< items.count {
insertItems.append(GalleryPagerInsertItem(index: i, item: items[i], previousIndex: previousIndexById[items[i].id]))
if i == previousIndexById[items[i].id] {
updateItems.append(GalleryPagerUpdateItem(index: i, previousIndex: i, item: items[i]))
} else {
insertItems.append(GalleryPagerInsertItem(index: i, item: items[i], previousIndex: previousIndexById[items[i].id]))
}
}
self.transaction(GalleryPagerTransaction(deleteItems: deleteItems, insertItems: insertItems, updateItems: updateItems, focusOnItem: centralItemIndex, synchronous: synchronous))

View File

@ -232,6 +232,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
_portraitToolsWrapperView.alpha = 0.0f;
_landscapeToolsWrapperView.alpha = 0.0f;
_videoAreaView.alpha = 0.0f;
_areaMaskView.alpha = 0.0f;
} completion:^(__unused BOOL finished)
{
if (completion != nil)
@ -318,6 +319,8 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
{
_portraitToolsWrapperView.alpha = 0.0f;
_landscapeToolsWrapperView.alpha = 0.0f;
_videoAreaView.alpha = 0.0f;
_areaMaskView.alpha = 0.0f;
} completion:nil];
}

View File

@ -650,6 +650,8 @@
[self setNeedsStatusBarAppearanceUpdate];
else
[_context forceSetStatusBarHidden:[self prefersStatusBarHidden] withAnimation:UIStatusBarAnimationNone];
self.navigationController.interactivePopGestureRecognizer.enabled = false;
}
[super viewDidAppear:animated];
@ -677,6 +679,8 @@
{
[_context setApplicationStatusBarAlpha:1.0f];
}
self.navigationController.interactivePopGestureRecognizer.enabled = true;
}
if ([self respondsToSelector:@selector(setNeedsUpdateOfScreenEdgesDeferringSystemGestures)])
@ -2278,8 +2282,11 @@
}
- (void)updateDotImage {
id<TGMediaEditAdjustments> adjustments = [_photoEditor exportAdjustments];
AVPlayer *player = _player;
if (player == nil) {
return;
}
id<TGMediaEditAdjustments> adjustments = [_photoEditor exportAdjustments];
[[SQueue concurrentDefaultQueue] dispatch:^{
AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:player.currentItem.asset];
generator.appliesPreferredTrackTransform = true;

View File

@ -14,26 +14,33 @@ import GalleryUI
public enum AvatarGalleryEntryId: Hashable {
case topImage
case image(MediaId)
case resource(String)
}
public enum AvatarGalleryEntry: Equatable {
case topImage([ImageRepresentationWithReference], GalleryItemIndexData?)
case image(MediaId, TelegramMediaImageReference?, [ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], Peer?, Int32, GalleryItemIndexData?, MessageId?)
case topImage([ImageRepresentationWithReference], GalleryItemIndexData?, Data?)
case image(MediaId, TelegramMediaImageReference?, [ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], Peer?, Int32, GalleryItemIndexData?, MessageId?, Data?)
public var id: AvatarGalleryEntryId {
switch self {
case .topImage:
case let .topImage(representations, _, _):
if let last = representations.last {
return .resource(last.representation.resource.id.uniqueId)
}
return .topImage
case let .image(id, _, _, _, _, _, indexData, _):
case let .image(id, _, representations, _, _, _, _, _, _):
if let last = representations.last {
return .resource(last.representation.resource.id.uniqueId)
}
return .image(id)
}
}
public var representations: [ImageRepresentationWithReference] {
switch self {
case let .topImage(representations, _):
case let .topImage(representations, _, _):
return representations
case let .image(_, _, representations, _, _, _, _, _):
case let .image(_, _, representations, _, _, _, _, _, _):
return representations
}
}
@ -42,30 +49,30 @@ public enum AvatarGalleryEntry: Equatable {
switch self {
case .topImage:
return []
case let .image(_, _, _, videoRepresentations, _, _, _, _):
case let .image(_, _, _, videoRepresentations, _, _, _, _, _):
return videoRepresentations
}
}
public var indexData: GalleryItemIndexData? {
switch self {
case let .topImage(_, indexData):
case let .topImage(_, indexData, _):
return indexData
case let .image(_, _, _, _, _, _, indexData, _):
case let .image(_, _, _, _, _, _, indexData, _, _):
return indexData
}
}
public static func ==(lhs: AvatarGalleryEntry, rhs: AvatarGalleryEntry) -> Bool {
switch lhs {
case let .topImage(lhsRepresentations, lhsIndexData):
if case let .topImage(rhsRepresentations, rhsIndexData) = rhs, lhsRepresentations == rhsRepresentations, lhsIndexData == rhsIndexData {
case let .topImage(lhsRepresentations, lhsIndexData, lhsImmediateThumbnailData):
if case let .topImage(rhsRepresentations, rhsIndexData, rhsImmediateThumbnailData) = rhs, lhsRepresentations == rhsRepresentations, lhsIndexData == rhsIndexData, lhsImmediateThumbnailData != rhsImmediateThumbnailData {
return true
} else {
return false
}
case let .image(lhsId, lhsImageReference, lhsRepresentations, lhsVideoRepresentations, lhsPeer, lhsDate, lhsIndexData, lhsMessageId):
if case let .image(rhsId, rhsImageReference, rhsRepresentations, rhsVideoRepresentations, rhsPeer, rhsDate, rhsIndexData, rhsMessageId) = rhs, lhsId == rhsId, lhsImageReference == rhsImageReference, lhsRepresentations == rhsRepresentations, lhsVideoRepresentations == rhsVideoRepresentations, arePeersEqual(lhsPeer, rhsPeer), lhsDate == rhsDate, lhsIndexData == rhsIndexData, lhsMessageId == rhsMessageId {
case let .image(lhsId, lhsImageReference, lhsRepresentations, lhsVideoRepresentations, lhsPeer, lhsDate, lhsIndexData, lhsMessageId, lhsImmediateThumbnailData):
if case let .image(rhsId, rhsImageReference, rhsRepresentations, rhsVideoRepresentations, rhsPeer, rhsDate, rhsIndexData, rhsMessageId, rhsImmediateThumbnailData) = rhs, lhsId == rhsId, lhsImageReference == rhsImageReference, lhsRepresentations == rhsRepresentations, lhsVideoRepresentations == rhsVideoRepresentations, arePeersEqual(lhsPeer, rhsPeer), lhsDate == rhsDate, lhsIndexData == rhsIndexData, lhsMessageId == rhsMessageId, lhsImmediateThumbnailData != rhsImmediateThumbnailData {
return true
} else {
return false
@ -87,7 +94,7 @@ public final class AvatarGalleryControllerPresentationArguments {
public func initialAvatarGalleryEntries(peer: Peer) -> [AvatarGalleryEntry] {
var initialEntries: [AvatarGalleryEntry] = []
if !peer.profileImageRepresentations.isEmpty, let peerReference = PeerReference(peer) {
initialEntries.append(.topImage(peer.profileImageRepresentations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), nil))
initialEntries.append(.topImage(peer.profileImageRepresentations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), nil, nil))
}
return initialEntries
}
@ -107,9 +114,9 @@ public func fetchedAvatarGalleryEntries(account: Account, peer: Peer) -> Signal<
for photo in photos {
let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photos.count))
if result.isEmpty, let first = initialEntries.first, photo.image.videoRepresentations.isEmpty {
result.append(.image(photo.image.imageId, photo.image.reference, first.representations, first.videoRepresentations, peer, photo.date, indexData, photo.messageId))
result.append(.image(photo.image.imageId, photo.image.reference, first.representations, first.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData))
} else {
result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.standalone(resource: $0.resource)) }), photo.image.videoRepresentations, peer, photo.date, indexData, photo.messageId))
result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.standalone(resource: $0.resource)) }), photo.image.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData))
}
index += 1
}
@ -134,9 +141,9 @@ public func fetchedAvatarGalleryEntries(account: Account, peer: Peer, firstEntry
for photo in photos {
let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photos.count))
if result.isEmpty, let first = initialEntries.first, photo.image.videoRepresentations.isEmpty {
result.append(.image(photo.image.imageId, photo.image.reference, first.representations, first.videoRepresentations, peer, photo.date, indexData, photo.messageId))
result.append(.image(photo.image.imageId, photo.image.reference, first.representations, first.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData))
} else {
result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.standalone(resource: $0.resource)) }), photo.image.videoRepresentations, peer, photo.date, indexData, photo.messageId))
result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.standalone(resource: $0.resource)) }), photo.image.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData))
}
index += 1
}
@ -224,6 +231,14 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
let f: () -> Void = {
if let strongSelf = self, animatedIn {
let isFirstTime = strongSelf.entries.isEmpty
var entries = entries
if !isFirstTime, let updated = entries.first, case let .image(image) = updated, !image.3.isEmpty, let previous = strongSelf.entries.first, case let .topImage(topImage) = previous {
let firstEntry = AvatarGalleryEntry.image(image.0, image.1, topImage.0, image.3, image.4, image.5, image.6, image.7, image.8)
entries.remove(at: 0)
entries.insert(firstEntry, at: 0)
}
strongSelf.entries = entries
if strongSelf.centralEntryIndex == nil {
strongSelf.centralEntryIndex = 0
@ -244,6 +259,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
} else {
canDelete = false
}
strongSelf.galleryNode.pager.replaceItems(strongSelf.entries.map({ entry in PeerAvatarImageGalleryItem(context: context, peer: peer, presentationData: presentationData, entry: entry, sourceHasRoundCorners: sourceHasRoundCorners, delete: canDelete ? {
self?.deleteEntry(entry)
} : nil, setMain: { [weak self] in
@ -501,10 +517,10 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
var index: Int32 = 0
for entry in entries {
let indexData = GalleryItemIndexData(position: index, totalCount: count)
if case let .topImage(representations, _) = entry {
updatedEntries.append(.topImage(representations, indexData))
} else if case let .image(id, reference, representations, videoRepresentations, peer, date, _, messageId) = entry {
updatedEntries.append(.image(id, reference, representations, videoRepresentations, peer, date, indexData, messageId))
if case let .topImage(representations, _, immediateThumbnailData) = entry {
updatedEntries.append(.topImage(representations, indexData, immediateThumbnailData))
} else if case let .image(id, reference, representations, videoRepresentations, peer, date, _, messageId, immediateThumbnailData) = entry {
updatedEntries.append(.image(id, reference, representations, videoRepresentations, peer, date, indexData, messageId, immediateThumbnailData))
}
index += 1
}
@ -522,7 +538,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
if self.peer.id == self.context.account.peerId {
} else {
}
case let .image(_, reference, _, _, _, _, _, messageId):
case let .image(_, reference, _, _, _, _, _, messageId, _):
if self.peer.id == self.context.account.peerId {
if let reference = reference {
let _ = updatePeerPhotoExisting(network: self.context.account.network, reference: reference).start()
@ -602,7 +618,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
}
}
}
case let .image(_, reference, _, _, _, _, _, messageId):
case let .image(_, reference, _, _, _, _, _, messageId, _):
if self.peer.id == self.context.account.peerId {
if let reference = reference {
let _ = removeAccountPhoto(network: self.context.account.network, reference: reference).start()

View File

@ -107,7 +107,7 @@ final class AvatarGalleryItemFooterContentNode: GalleryFooterContentNode {
var typeText: String?
var buttonText: String?
switch entry {
case let .image(_, _, _, videoRepresentations, peer, date, _, _):
case let .image(_, _, _, videoRepresentations, peer, date, _, _, _):
nameText = peer?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""
dateText = humanReadableStringForTimestamp(strings: self.strings, dateTimeFormat: self.dateTimeFormat, timestamp: date)

View File

@ -94,9 +94,9 @@ class PeerAvatarImageGalleryItem: GalleryItem {
func thumbnailItem() -> (Int64, GalleryThumbnailItem)? {
let content: [ImageRepresentationWithReference]
switch self.entry {
case let .topImage(representations, _):
case let .topImage(representations, _, _):
content = representations
case let .image(_, _, representations, _, _, _, _, _):
case let .image(_, _, representations, _, _, _, _, _, _):
content = representations
}
@ -154,11 +154,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
super.init()
self.contentNode.addSubnode(self.imageNode)
self.imageNode.imageUpdated = { [weak self] _ in
self?._ready.set(.single(Void()))
}
self.imageNode.contentAnimations = .subsequentUpdates
self.imageNode.view.contentMode = .scaleAspectFill
self.imageNode.clipsToBounds = true
@ -202,6 +198,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
}
fileprivate func setEntry(_ entry: AvatarGalleryEntry, synchronous: Bool) {
let previousRepresentations = self.entry?.representations
if self.entry != entry {
self.entry = entry
@ -217,14 +214,16 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
if let largestSize = largestImageRepresentation(entry.representations.map({ $0.representation })) {
let displaySize = largestSize.dimensions.cgSize.fitted(CGSize(width: 1280.0, height: 1280.0)).dividedByScreenScale().integralFloor
self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: displaySize, boundingSize: displaySize, intrinsicInsets: UIEdgeInsets()))()
let representations: [ImageRepresentationWithReference]
switch entry {
case let .topImage(topRepresentations, _):
representations = topRepresentations
case let .image(_, _, imageRepresentations, _, _, _, _, _):
representations = imageRepresentations
let representations = entry.representations
if representations.last != previousRepresentations?.last {
self.imageNode.setSignal(chatAvatarGalleryPhoto(account: self.context.account, representations: representations, attemptSynchronously: synchronous), attemptSynchronously: synchronous, dispatchOnDisplayLink: false)
if entry.videoRepresentations.isEmpty {
self.imageNode.imageUpdated = { [weak self] _ in
self?._ready.set(.single(Void()))
}
}
}
self.imageNode.setSignal(chatAvatarGalleryPhoto(account: self.context.account, representations: representations, attemptSynchronously: synchronous), attemptSynchronously: synchronous, dispatchOnDisplayLink: false)
self.zoomableContent = (largestSize.dimensions.cgSize, self.contentNode)
if let largestIndex = representations.firstIndex(where: { $0.representation == largestSize }) {
@ -278,8 +277,8 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
}
if let video = entry.videoRepresentations.last, let id = id {
let mediaManager = self.context.sharedContext.mediaManager
let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: true, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded)
videoNode.isUserInteractionEnabled = false
videoNode.ownsContentNodeUpdated = { [weak self] owns in
@ -298,6 +297,8 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
videoNode.updateLayout(size: largestSize.dimensions.cgSize, transition: .immediate)
self.contentNode.addSubnode(videoNode)
self._ready.set(videoNode.ready)
} else if let videoNode = self.videoNode {
self.videoContent = nil
self.videoNode = nil
@ -493,9 +494,9 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
case .Remote:
let representations: [ImageRepresentationWithReference]
switch entry {
case let .topImage(topRepresentations, _):
case let .topImage(topRepresentations, _, _):
representations = topRepresentations
case let .image(_, _, imageRepresentations, _, _, _, _, _):
case let .image(_, _, imageRepresentations, _, _, _, _, _, _):
representations = imageRepresentations
}

View File

@ -45,7 +45,6 @@ public func requestPeerPhotos(account: Account, peerId: PeerId) -> Signal<[Teleg
for i in 0 ..< photos.count {
if let image = telegramMediaImageFromApiPhoto(photos[i]), let reference = image.reference {
var date: Int32 = 0
var file: TelegramMediaFile?
switch photos[i] {
case let .photo(_, _, _, _, apiDate, _, _, _):
date = apiDate

View File

@ -46,7 +46,7 @@ private func chatMessageGalleryControllerData(context: AccountContext, message:
switch action.action {
case let .photoUpdated(image):
if let peer = messageMainPeer(message), let image = image {
let promise: Promise<[AvatarGalleryEntry]> = Promise([AvatarGalleryEntry.image(image.imageId, image.reference, image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: .media(media: .message(message: MessageReference(message), media: media), resource: $0.resource)) }), image.videoRepresentations, peer, message.timestamp, nil, message.id)])
let promise: Promise<[AvatarGalleryEntry]> = Promise([AvatarGalleryEntry.image(image.imageId, image.reference, image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: .media(media: .message(message: MessageReference(message), media: media), resource: $0.resource)) }), image.videoRepresentations, peer, message.timestamp, nil, message.id, image.immediateThumbnailData)])
let galleryController = AvatarGalleryController(context: context, peer: peer, remoteEntries: promise, replaceRootController: { controller, ready in
})

View File

@ -155,15 +155,15 @@ final class PeerInfoHeaderNavigationTransition {
}
enum PeerInfoAvatarListItem: Equatable {
case topImage([ImageRepresentationWithReference])
case image(TelegramMediaImageReference?, [ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation])
case topImage([ImageRepresentationWithReference], Data?)
case image(TelegramMediaImageReference?, [ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], Data?)
var id: WrappedMediaResourceId {
switch self {
case let .topImage(representations):
case let .topImage(representations, _):
let representation = largestImageRepresentation(representations.map { $0.representation }) ?? representations[representations.count - 1].representation
return WrappedMediaResourceId(representation.resource.id)
case let .image(_, representations, _):
case let .image(_, representations, _, _):
let representation = largestImageRepresentation(representations.map { $0.representation }) ?? representations[representations.count - 1].representation
return WrappedMediaResourceId(representation.resource.id)
}
@ -179,16 +179,14 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
let isReady = Promise<Bool>()
private var didSetReady: Bool = false
private var statusPromise = Promise<MediaPlayerStatus?>()
var mediaStatus: Signal<MediaPlayerStatus?, NoError> {
get {
return self.statusPromise.get()
}
}
var isExpanded: Bool = false {
didSet {
self.videoNode?.canAttachContent = self.isExpanded
if let videoNode = self.videoNode, videoNode.canAttachContent != self.isExpanded {
videoNode.canAttachContent = self.isExpanded
if videoNode.canAttachContent {
videoNode.play()
}
}
}
}
@ -217,14 +215,17 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
func setup(item: PeerInfoAvatarListItem, synchronous: Bool) {
let representations: [ImageRepresentationWithReference]
let videoRepresentations: [TelegramMediaImage.VideoRepresentation]
let immediateThumbnailData: Data?
var id: Int64?
switch item {
case let .topImage(topRepresentations):
case let .topImage(topRepresentations, immediateThumbnail):
representations = topRepresentations
videoRepresentations = []
case let .image(reference, imageRepresentations, videoRepresentationsValue):
immediateThumbnailData = immediateThumbnail
case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail):
representations = imageRepresentations
videoRepresentations = videoRepresentationsValue
immediateThumbnailData = immediateThumbnail
if case let .cloud(imageId, _, _) = reference {
id = imageId
@ -234,8 +235,8 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
if let video = videoRepresentations.last, let id = id {
let mediaManager = self.context.sharedContext.mediaManager
let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: true, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded)
videoNode.isUserInteractionEnabled = false
videoNode.ownsContentNodeUpdated = { [weak self] owns in
@ -243,19 +244,16 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
strongSelf.videoNode?.isHidden = !owns
}
}
self.videoContent = videoContent
self.videoNode = videoNode
self.addSubnode(videoNode)
self.statusPromise.set(videoNode.status)
} else if let videoNode = self.videoNode {
self.videoContent = nil
self.videoNode = nil
videoNode.removeFromSupernode()
self.statusPromise.set(.single(nil))
}
}
@ -270,10 +268,11 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
if let videoNode = self.videoNode {
videoNode.updateLayout(size: imageSize, transition: .immediate)
videoNode.frame = imageFrame
videoNode.canAttachContent = self.isExpanded
if videoNode.hasAttachedContext {
videoNode.play()
if videoNode.canAttachContent != self.isExpanded {
videoNode.canAttachContent = self.isExpanded
if videoNode.canAttachContent {
videoNode.play()
}
}
}
}
@ -604,20 +603,30 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
guard let strongSelf = self else {
return
}
var entries = entries
var synchronous = false
if !strongSelf.galleryEntries.isEmpty, let updated = entries.first, case let .image(image) = updated, !image.3.isEmpty, let previous = strongSelf.galleryEntries.first, case let .topImage(topImage) = previous {
let firstEntry = AvatarGalleryEntry.image(image.0, image.1, topImage.0, image.3, image.4, image.5, image.6, image.7, image.8)
entries.remove(at: 0)
entries.insert(firstEntry, at: 0)
synchronous = true
}
var items: [PeerInfoAvatarListItem] = []
for entry in entries {
switch entry {
case let .topImage(representations, _):
items.append(.topImage(representations))
case let .image(_, reference, representations, videoRepresentations, _, _, _, _):
items.append(.image(reference, representations, videoRepresentations))
case let .topImage(representations, _, immediateThumbnailData):
items.append(.topImage(representations, immediateThumbnailData))
case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData):
items.append(.image(reference, representations, videoRepresentations, immediateThumbnailData))
}
}
strongSelf.galleryEntries = entries
strongSelf.items = items
strongSelf.itemsUpdated?(items)
if let size = strongSelf.validLayout {
strongSelf.updateItems(size: size, transition: .immediate, stripTransition: .immediate)
strongSelf.updateItems(size: size, update: true, transition: .immediate, stripTransition: .immediate, synchronous: true)
}
if items.isEmpty {
if !strongSelf.didSetReady {
@ -630,7 +639,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
self.updateItems(size: size, transition: transition, stripTransition: transition)
}
private func updateItems(size: CGSize, transition: ContainedViewLayoutTransition, stripTransition: ContainedViewLayoutTransition, synchronous: Bool = false) {
private func updateItems(size: CGSize, update: Bool = false, transition: ContainedViewLayoutTransition, stripTransition: ContainedViewLayoutTransition, synchronous: Bool = false) {
var validIds: [WrappedMediaResourceId] = []
var addedItemNodesForAdditiveTransition: [PeerInfoAvatarListItemNode] = []
var additiveTransitionOffset: CGFloat = 0.0
@ -642,6 +651,9 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
if let current = self.itemNodes[self.items[i].id] {
itemNode = current
itemNode.isExpanded = self.isExpanded
if update {
itemNode.setup(item: self.items[i], synchronous: synchronous && i == self.currentIndex)
}
} else {
wasAdded = true
itemNode = PeerInfoAvatarListItemNode(context: self.context)
@ -788,9 +800,9 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
self.avatarNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize))
self.avatarNode.font = avatarPlaceholderFont(size: floor(avatarSize * 16.0 / 37.0))
if let item = item, case let .image(reference, _, videoRepresentations) = item, let video = videoRepresentations.last, case let .cloud(imageId, _, _) = reference {
if let item = item, case let .image(reference, representations, videoRepresentations, immediateThumbnailData) = item, let video = videoRepresentations.last, case let .cloud(imageId, _, _) = reference {
let id = imageId
let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
if videoContent.id != self.videoContent?.id {
let mediaManager = self.context.sharedContext.mediaManager
@ -808,7 +820,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
let shape = CAShapeLayer()
shape.path = maskPath.cgPath
videoNode.layer.mask = shape
self.addSubnode(videoNode)
}
} else if let videoNode = self.videoNode {
@ -822,9 +834,11 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
videoNode.updateLayout(size: self.avatarNode.frame.size, transition: .immediate)
videoNode.frame = self.avatarNode.frame
videoNode.canAttachContent = !isExpanded
if videoNode.hasAttachedContext {
videoNode.play()
if isExpanded == videoNode.canAttachContent {
videoNode.canAttachContent = !isExpanded
if videoNode.canAttachContent {
videoNode.play()
}
}
}
}