From 218ee8629a9b1c014a14dd43a284ef5fd6b35903 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 15 Jul 2020 12:59:15 +0300 Subject: [PATCH] Video avatar fixes --- .../Sources/TGMediaAssetsController.m | 5 +- .../Sources/AvatarGalleryController.swift | 65 +++++--- .../Sources/PeerAvatarImageGalleryItem.swift | 20 +-- .../SyncCore/Sources/MediaReference.swift | 40 +++++ .../Sources/FetchedMediaResource.swift | 47 +++++- .../ImageRepresentationWithReference.swift | 11 ++ .../Sources/RequestUserPhotos.swift | 10 +- .../TelegramUI/Sources/OpenChatMessage.swift | 2 +- .../Sources/PeerInfo/PeerInfoHeaderNode.swift | 157 ++++++++++-------- .../Sources/PeerInfo/PeerInfoScreen.swift | 2 +- 10 files changed, 247 insertions(+), 112 deletions(-) diff --git a/submodules/LegacyComponents/Sources/TGMediaAssetsController.m b/submodules/LegacyComponents/Sources/TGMediaAssetsController.m index b2950865b3..56cbb162f0 100644 --- a/submodules/LegacyComponents/Sources/TGMediaAssetsController.m +++ b/submodules/LegacyComponents/Sources/TGMediaAssetsController.m @@ -1050,6 +1050,9 @@ case TGMediaAssetGifType: { TGCameraCapturedVideo *video = (TGCameraCapturedVideo *)item; + if ([video isKindOfClass:[TGMediaAsset class]]) { + video = [[TGCameraCapturedVideo alloc] initWithAsset:(TGMediaAsset *)video livePhoto:false]; + } TGVideoEditAdjustments *adjustments = (TGVideoEditAdjustments *)[editingContext adjustmentsForItem:video]; NSString *caption = [editingContext captionForItem:video]; @@ -1102,7 +1105,7 @@ { NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; dict[@"type"] = @"cameraVideo"; - dict[@"url"] = ((TGCameraCapturedVideo *)item).immediateAVAsset.URL; + dict[@"url"] = video.immediateAVAsset.URL; dict[@"previewImage"] = image; dict[@"adjustments"] = adjustments; dict[@"dimensions"] = [NSValue valueWithCGSize:dimensions]; diff --git a/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift b/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift index 45dbfacb8d..0e260d08a6 100644 --- a/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift +++ b/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift @@ -23,12 +23,12 @@ public enum AvatarGalleryEntryId: Hashable { } public enum AvatarGalleryEntry: Equatable { - case topImage([ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], GalleryItemIndexData?, Data?, String?) - case image(MediaId, TelegramMediaImageReference?, [ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], Peer?, Int32, GalleryItemIndexData?, MessageId?, Data?, String?) + case topImage([ImageRepresentationWithReference], [VideoRepresentationWithReference], Peer?, GalleryItemIndexData?, Data?, String?) + case image(MediaId, TelegramMediaImageReference?, [ImageRepresentationWithReference], [VideoRepresentationWithReference], Peer?, Int32, GalleryItemIndexData?, MessageId?, Data?, String?) public var id: AvatarGalleryEntryId { switch self { - case let .topImage(representations, _, _, _, _): + case let .topImage(representations, _, _, _, _, _): if let last = representations.last { return .resource(last.representation.resource.id.uniqueId) } @@ -41,9 +41,18 @@ public enum AvatarGalleryEntry: Equatable { } } + public var peer: Peer? { + switch self { + case let .topImage(_, _, peer, _, _, _): + return peer + case let .image(_, _, _, _, peer, _, _, _, _, _): + return peer + } + } + public var representations: [ImageRepresentationWithReference] { switch self { - case let .topImage(representations, _, _, _, _): + case let .topImage(representations, _, _, _, _, _): return representations case let .image(_, _, representations, _, _, _, _, _, _, _): return representations @@ -52,16 +61,16 @@ public enum AvatarGalleryEntry: Equatable { public var immediateThumbnailData: Data? { switch self { - case let .topImage(_, _, _, immediateThumbnailData, _): + case let .topImage(_, _, _, _, immediateThumbnailData, _): return immediateThumbnailData case let .image(_, _, _, _, _, _, _, _, immediateThumbnailData, _): return immediateThumbnailData } } - public var videoRepresentations: [TelegramMediaImage.VideoRepresentation] { + public var videoRepresentations: [VideoRepresentationWithReference] { switch self { - case let .topImage(_, videoRepresentations, _, _, _): + case let .topImage(_, videoRepresentations, _, _, _, _): return videoRepresentations case let .image(_, _, _, videoRepresentations, _, _, _, _, _, _): return videoRepresentations @@ -70,7 +79,7 @@ public enum AvatarGalleryEntry: Equatable { public var indexData: GalleryItemIndexData? { switch self { - case let .topImage(_, _, indexData, _, _): + case let .topImage(_, _, _, indexData, _, _): return indexData case let .image(_, _, _, _, _, _, indexData, _, _, _): return indexData @@ -79,8 +88,8 @@ public enum AvatarGalleryEntry: Equatable { public static func ==(lhs: AvatarGalleryEntry, rhs: AvatarGalleryEntry) -> Bool { switch lhs { - case let .topImage(lhsRepresentations, lhsVideoRepresentations, lhsIndexData, lhsImmediateThumbnailData, lhsCategory): - if case let .topImage(rhsRepresentations, rhsVideoRepresentations, rhsIndexData, rhsImmediateThumbnailData, rhsCategory) = rhs, lhsRepresentations == rhsRepresentations, lhsVideoRepresentations == rhsVideoRepresentations, lhsIndexData == rhsIndexData, lhsImmediateThumbnailData == rhsImmediateThumbnailData, lhsCategory == rhsCategory { + case let .topImage(lhsRepresentations, lhsVideoRepresentations, lhsPeer, lhsIndexData, lhsImmediateThumbnailData, lhsCategory): + if case let .topImage(rhsRepresentations, rhsVideoRepresentations, rhsPeer, rhsIndexData, rhsImmediateThumbnailData, rhsCategory) = rhs, lhsRepresentations == rhsRepresentations, lhsVideoRepresentations == rhsVideoRepresentations, arePeersEqual(lhsPeer, rhsPeer), lhsIndexData == rhsIndexData, lhsImmediateThumbnailData == rhsImmediateThumbnailData, lhsCategory == rhsCategory { return true } else { return false @@ -111,8 +120,8 @@ public func normalizeEntries(_ entries: [AvatarGalleryEntry]) -> [AvatarGalleryE var index: Int32 = 0 for entry in entries { let indexData = GalleryItemIndexData(position: index, totalCount: count) - if case let .topImage(representations, videoRepresentations, _, immediateThumbnailData, category) = entry { - updatedEntries.append(.topImage(representations, videoRepresentations, indexData, immediateThumbnailData, category)) + if case let .topImage(representations, videoRepresentations, peer, _, immediateThumbnailData, category) = entry { + updatedEntries.append(.topImage(representations, videoRepresentations, peer, indexData, immediateThumbnailData, category)) } else if case let .image(id, reference, representations, videoRepresentations, peer, date, _, messageId, immediateThumbnailData, category) = entry { updatedEntries.append(.image(id, reference, representations, videoRepresentations, peer, date, indexData, messageId, immediateThumbnailData, category)) } @@ -124,7 +133,7 @@ public func normalizeEntries(_ entries: [AvatarGalleryEntry]) -> [AvatarGalleryE public func initialAvatarGalleryEntries(account: Account, peer: Peer) -> Signal<[AvatarGalleryEntry], NoError> { 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, nil, nil)) + initialEntries.append(.topImage(peer.profileImageRepresentations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), [], peer, nil, nil, nil)) } if peer is TelegramChannel || peer is TelegramGroup { @@ -139,8 +148,8 @@ public func initialAvatarGalleryEntries(account: Account, peer: Peer) -> Signal< initialPhoto = photo } - if let photo = initialPhoto, !photo.videoRepresentations.isEmpty { - return [.topImage(photo.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.standalone(resource: $0.resource)) }), photo.videoRepresentations, nil, nil, nil)] + if let photo = initialPhoto, !photo.videoRepresentations.isEmpty, let peerReference = PeerReference(peer) { + return [.topImage(photo.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), photo.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), peer, nil, nil, nil)] } else { return initialEntries } @@ -155,19 +164,19 @@ public func fetchedAvatarGalleryEntries(account: Account, peer: Peer) -> Signal< |> mapToSignal { initialEntries in return .single(initialEntries) |> then( - requestPeerPhotos(account: account, peerId: peer.id) + requestPeerPhotos(postbox: account.postbox, network: account.network, peerId: peer.id) |> map { photos -> [AvatarGalleryEntry] in var result: [AvatarGalleryEntry] = [] if photos.isEmpty { result = initialEntries - } else { + } else if let peerReference = PeerReference(peer) { var index: Int32 = 0 for photo in photos { let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photos.count)) if result.isEmpty, let first = initialEntries.first { - result.append(.image(photo.image.imageId, photo.image.reference, first.representations, photo.image.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil)) + result.append(.image(photo.image.imageId, photo.image.reference, first.representations, photo.image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil)) } 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, photo.image.immediateThumbnailData, nil)) + 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.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil)) } index += 1 } @@ -182,20 +191,20 @@ public func fetchedAvatarGalleryEntries(account: Account, peer: Peer, firstEntry let initialEntries = [firstEntry] return Signal<[AvatarGalleryEntry], NoError>.single(initialEntries) |> then( - requestPeerPhotos(account: account, peerId: peer.id) + requestPeerPhotos(postbox: account.postbox, network: account.network, peerId: peer.id) |> map { photos -> [AvatarGalleryEntry] in var result: [AvatarGalleryEntry] = [] let initialEntries = [firstEntry] if photos.isEmpty { result = initialEntries - } else { + } else if let peerReference = PeerReference(peer) { var index: Int32 = 0 for photo in photos { let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photos.count)) if result.isEmpty, let first = initialEntries.first { - result.append(.image(photo.image.imageId, photo.image.reference, first.representations, photo.image.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil)) + result.append(.image(photo.image.imageId, photo.image.reference, first.representations, photo.image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil)) } 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, photo.image.immediateThumbnailData, nil)) + 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.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil)) } index += 1 } @@ -676,7 +685,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr private func openEntryEdit(_ rawEntry: AvatarGalleryEntry) { let mediaReference: AnyMediaReference - if let video = rawEntry.videoRepresentations.last { + if let video = rawEntry.videoRepresentations.last?.representation { mediaReference = .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: [])])) } else { let media = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: rawEntry.representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: []) @@ -770,7 +779,13 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr // self?.openEntryEdit(rawEntry) // })) - items.append(ActionSheetButtonItem(title: self.presentationData.strings.GroupInfo_SetGroupPhotoDelete, color: .destructive, action: { [weak self] in + let deleteTitle: String + if let _ = rawEntry.videoRepresentations.last { + deleteTitle = self.presentationData.strings.Settings_RemoveVideo + } else { + deleteTitle = self.presentationData.strings.GroupInfo_SetGroupPhotoDelete + } + items.append(ActionSheetButtonItem(title: deleteTitle, color: .destructive, action: { [weak self] in dismissAction() self?.deleteEntry(rawEntry) })) diff --git a/submodules/PeerAvatarGalleryUI/Sources/PeerAvatarImageGalleryItem.swift b/submodules/PeerAvatarGalleryUI/Sources/PeerAvatarImageGalleryItem.swift index ea9fe74891..b588d924ac 100644 --- a/submodules/PeerAvatarGalleryUI/Sources/PeerAvatarImageGalleryItem.swift +++ b/submodules/PeerAvatarGalleryUI/Sources/PeerAvatarImageGalleryItem.swift @@ -106,7 +106,7 @@ 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, _, _, _, _, _, _, _): content = representations @@ -188,8 +188,8 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode { self.footerContentNode.share = { [weak self] interaction in if let strongSelf = self, let entry = strongSelf.entry, !entry.representations.isEmpty { let subject: ShareControllerSubject - if let video = entry.videoRepresentations.last { - 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: [])])) + if let video = entry.videoRepresentations.last, let peerReference = PeerReference(peer) { + let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [])])) subject = .media(videoFileReference.abstract) } else { subject = .image(entry.representations) @@ -253,23 +253,23 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode { self.fetchDisposable.set(fetchedMediaResource(mediaBox: self.context.account.postbox.mediaBox, reference: representations[largestIndex].reference).start()) } - var id: Int64? + var id: Int64 var category: String? if case let .image(image) = entry { id = image.0.id category = image.9 } else { - id = 1 + id = Int64(entry.peer?.id.id ?? 1) } - if let video = entry.videoRepresentations.last, let id = id { + if let video = entry.videoRepresentations.last, let peerReference = PeerReference(self.peer) { if video != previousVideoRepresentations?.last { 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: 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, category), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: true, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear) + let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: entry.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [])])) + let videoContent = NativeVideoContent(id: .profileVideo(id, category), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.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.isHidden = true - self.videoStartTimestamp = video.startTimestamp + self.videoStartTimestamp = video.representation.startTimestamp self.videoContent = videoContent self.videoNode = videoNode @@ -574,7 +574,7 @@ 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, _, _, _, _, _, _, _): representations = imageRepresentations diff --git a/submodules/SyncCore/Sources/MediaReference.swift b/submodules/SyncCore/Sources/MediaReference.swift index 0a8876b606..089f3a275b 100644 --- a/submodules/SyncCore/Sources/MediaReference.swift +++ b/submodules/SyncCore/Sources/MediaReference.swift @@ -214,6 +214,7 @@ public enum AnyMediaReference: Equatable { case webPage(webPage: WebpageReference, media: Media) case stickerPack(stickerPack: StickerPackReference, media: Media) case savedGif(media: Media) + case avatarList(peer: PeerReference, media: Media) public static func ==(lhs: AnyMediaReference, rhs: AnyMediaReference) -> Bool { switch lhs { @@ -247,6 +248,12 @@ public enum AnyMediaReference: Equatable { } else { return false } + case let .avatarList(lhsPeer, lhsMedia): + if case let .avatarList(rhsPeer, rhsMedia) = rhs, lhsPeer == rhsPeer, lhsMedia.isEqual(to: rhsMedia) { + return true + } else { + return false + } } } @@ -262,6 +269,8 @@ public enum AnyMediaReference: Equatable { return .stickerPack(stickerPack: stickerPack) case .savedGif: return .savedGif + case let .avatarList(peer, media): + return nil } } @@ -287,6 +296,10 @@ public enum AnyMediaReference: Equatable { if let media = media as? T { return .savedGif(media: media) } + case let .avatarList(peer, media): + if let media = media as? T { + return .avatarList(peer: peer, media: media) + } } return nil } @@ -303,6 +316,8 @@ public enum AnyMediaReference: Equatable { return media case let .savedGif(media): return media + case let .avatarList(_, media): + return media } } @@ -380,6 +395,7 @@ public enum MediaReference { case webPage case stickerPack case savedGif + case avatarList } case standalone(media: T) @@ -387,6 +403,7 @@ public enum MediaReference { case webPage(webPage: WebpageReference, media: T) case stickerPack(stickerPack: StickerPackReference, media: T) case savedGif(media: T) + case avatarList(peer: PeerReference, media: T) public init?(decoder: PostboxDecoder) { guard let caseIdValue = decoder.decodeOptionalInt32ForKey("_r"), let caseId = CodingCase(rawValue: caseIdValue) else { @@ -421,6 +438,12 @@ public enum MediaReference { return nil } self = .savedGif(media: media) + case .avatarList: + let peer = decoder.decodeObjectForKey("pr", decoder: { StickerPackReference(decoder: $0) }) as! PeerReference + guard let media = decoder.decodeObjectForKey("m") as? T else { + return nil + } + self = .avatarList(peer: peer, media: media) } } @@ -444,6 +467,10 @@ public enum MediaReference { case let .savedGif(media): encoder.encodeInt32(CodingCase.savedGif.rawValue, forKey: "_r") encoder.encodeObject(media, forKey: "m") + case let .avatarList(peer, media): + encoder.encodeInt32(CodingCase.avatarList.rawValue, forKey: "_r") + encoder.encodeObject(peer, forKey: "pr") + encoder.encodeObject(media, forKey: "m") } } @@ -459,6 +486,8 @@ public enum MediaReference { return .stickerPack(stickerPack: stickerPack, media: media) case let .savedGif(media): return .savedGif(media: media) + case let .avatarList(peer, media): + return .avatarList(peer: peer, media: media) } } @@ -478,6 +507,8 @@ public enum MediaReference { return media case let .savedGif(media): return media + case let .avatarList(_, media): + return media } } @@ -493,6 +524,7 @@ public enum MediaResourceReference: Equatable { case media(media: AnyMediaReference, resource: MediaResource) case standalone(resource: MediaResource) case avatar(peer: PeerReference, resource: MediaResource) + case avatarList(peer: PeerReference, resource: MediaResource) case messageAuthorAvatar(message: MessageReference, resource: MediaResource) case wallpaper(wallpaper: WallpaperReference?, resource: MediaResource) case stickerPackThumbnail(stickerPack: StickerPackReference, resource: MediaResource) @@ -506,6 +538,8 @@ public enum MediaResourceReference: Equatable { return resource case let .avatar(_, resource): return resource + case let .avatarList(_, resource): + return resource case let .messageAuthorAvatar(_, resource): return resource case let .wallpaper(_, resource): @@ -537,6 +571,12 @@ public enum MediaResourceReference: Equatable { } else { return false } + case let .avatarList(lhsPeer, lhsResource): + if case let .avatarList(rhsPeer, rhsResource) = rhs, lhsPeer == rhsPeer, lhsResource.isEqual(to: rhsResource) { + return true + } else { + return false + } case let .messageAuthorAvatar(lhsMessage, lhsResource): if case let .messageAuthorAvatar(rhsMessage, rhsResource) = rhs, lhsMessage == rhsMessage, lhsResource.isEqual(to: rhsResource) { return true diff --git a/submodules/TelegramCore/Sources/FetchedMediaResource.swift b/submodules/TelegramCore/Sources/FetchedMediaResource.swift index 5f29868f03..f324e6ccdb 100644 --- a/submodules/TelegramCore/Sources/FetchedMediaResource.swift +++ b/submodules/TelegramCore/Sources/FetchedMediaResource.swift @@ -76,6 +76,11 @@ private func findMediaResource(media: Media, previousMedia: Media?, resource: Me return representation.resource } } + for representation in image.videoRepresentations { + if representation.resource.id.isEqual(to: resource.id) { + return representation.resource + } + } if let legacyResource = resource as? CloudFileMediaResource { for representation in image.representations { if let updatedResource = representation.resource as? CloudPhotoSizeMediaResource { @@ -155,6 +160,7 @@ private enum MediaReferenceRevalidationKey: Hashable { case wallpaper(wallpaper: WallpaperReference) case wallpapers case themes + case peerAvatars(peer: PeerReference) } private final class MediaReferenceRevalidationItemContext { @@ -447,6 +453,25 @@ final class MediaReferenceRevalidationContext { } } } + + func peerAvatars(postbox: Postbox, network: Network, background: Bool, peer: PeerReference) -> Signal<[TelegramPeerPhoto], RevalidateMediaReferenceError> { + return self.genericItem(key: .peerAvatars(peer: peer), background: background, request: { next, error in + return (requestPeerPhotos(postbox: postbox, network: network, peerId: peer.id) + |> mapError { _ -> RevalidateMediaReferenceError in + return .generic + }).start(next: { value in + next(value) + }, error: { _ in + error(.generic) + }) + }) |> mapToSignal { next -> Signal<[TelegramPeerPhoto], RevalidateMediaReferenceError> in + if let next = next as? [TelegramPeerPhoto] { + return .single(next) + } else { + return .fail(.generic) + } + } + } } struct RevalidatedMediaResource { @@ -546,6 +571,16 @@ func revalidateMediaResourceReference(postbox: Postbox, network: Network, revali } return .fail(.generic) } + case let .avatarList(peer, media): + return revalidationContext.peerAvatars(postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, peer: peer) + |> mapToSignal { result -> Signal in + for photo in result { + if let updatedResource = findUpdatedMediaResource(media: photo.image, previousMedia: media, resource: resource) { + return .single(RevalidatedMediaResource(updatedResource: updatedResource, updatedReference: nil)) + } + } + return .fail(.generic) + } case let .standalone(media): if let file = media as? TelegramMediaFile { for attribute in file.attributes { @@ -587,6 +622,16 @@ func revalidateMediaResourceReference(postbox: Postbox, network: Network, revali } return .fail(.generic) } + case let .avatarList(peer, _): + return revalidationContext.peerAvatars(postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, peer: peer) + |> mapToSignal { updatedPeerAvatars -> Signal in + for photo in updatedPeerAvatars { + if let updatedResource = findUpdatedMediaResource(media: photo.image, previousMedia: nil, resource: resource) { + return .single(RevalidatedMediaResource(updatedResource: updatedResource, updatedReference: nil)) + } + } + return .fail(.generic) + } case let .messageAuthorAvatar(message, _): return revalidationContext.message(postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, message: message) |> mapToSignal { updatedMessage -> Signal in @@ -647,7 +692,7 @@ func revalidateMediaResourceReference(postbox: Postbox, network: Network, revali } return .fail(.generic) } - case let .theme(themeReference, resource): + case let .theme(_, resource): return revalidationContext.themes(postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation) |> mapToSignal { themes -> Signal in for theme in themes { diff --git a/submodules/TelegramCore/Sources/ImageRepresentationWithReference.swift b/submodules/TelegramCore/Sources/ImageRepresentationWithReference.swift index 9c9242373e..5262bd91e0 100644 --- a/submodules/TelegramCore/Sources/ImageRepresentationWithReference.swift +++ b/submodules/TelegramCore/Sources/ImageRepresentationWithReference.swift @@ -10,3 +10,14 @@ public struct ImageRepresentationWithReference: Equatable { self.reference = reference } } + + +public struct VideoRepresentationWithReference: Equatable { + public let representation: TelegramMediaImage.VideoRepresentation + public let reference: MediaResourceReference + + public init(representation: TelegramMediaImage.VideoRepresentation, reference: MediaResourceReference) { + self.representation = representation + self.reference = reference + } +} diff --git a/submodules/TelegramCore/Sources/RequestUserPhotos.swift b/submodules/TelegramCore/Sources/RequestUserPhotos.swift index 1a63756521..c77b20abdf 100644 --- a/submodules/TelegramCore/Sources/RequestUserPhotos.swift +++ b/submodules/TelegramCore/Sources/RequestUserPhotos.swift @@ -16,13 +16,13 @@ public struct TelegramPeerPhoto { public let messageId: MessageId? } -public func requestPeerPhotos(account: Account, peerId: PeerId) -> Signal<[TelegramPeerPhoto], NoError> { - return account.postbox.transaction{ transaction -> Peer? in +public func requestPeerPhotos(postbox: Postbox, network: Network, peerId: PeerId) -> Signal<[TelegramPeerPhoto], NoError> { + return postbox.transaction{ transaction -> Peer? in return transaction.getPeer(peerId) } |> mapToSignal { peer -> Signal<[TelegramPeerPhoto], NoError> in if let peer = peer as? TelegramUser, let inputUser = apiInputUser(peer) { - return account.network.request(Api.functions.photos.getUserPhotos(userId: inputUser, offset: 0, maxId: 0, limit: 100)) + return network.request(Api.functions.photos.getUserPhotos(userId: inputUser, offset: 0, maxId: 0, limit: 100)) |> map {Optional($0)} |> mapError {_ in} |> `catch` { _ -> Signal in @@ -61,7 +61,7 @@ public func requestPeerPhotos(account: Account, peerId: PeerId) -> Signal<[Teleg } } } else if let peer = peer, let inputPeer = apiInputPeer(peer) { - return account.network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, filter: .inputMessagesFilterChatPhotos, minDate: 0, maxDate: 0, offsetId: 0, addOffset: 0, limit: 1000, maxId: 0, minId: 0, hash: 0)) + return network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, filter: .inputMessagesFilterChatPhotos, minDate: 0, maxDate: 0, offsetId: 0, addOffset: 0, limit: 1000, maxId: 0, minId: 0, hash: 0)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) @@ -90,7 +90,7 @@ public func requestPeerPhotos(account: Account, peerId: PeerId) -> Signal<[Teleg users = [] } - return account.postbox.transaction { transaction -> [Message] in + return postbox.transaction { transaction -> [Message] in var peers: [PeerId: Peer] = [:] for user in users { diff --git a/submodules/TelegramUI/Sources/OpenChatMessage.swift b/submodules/TelegramUI/Sources/OpenChatMessage.swift index 95c3a4cc38..fed47ab635 100644 --- a/submodules/TelegramUI/Sources/OpenChatMessage.swift +++ b/submodules/TelegramUI/Sources/OpenChatMessage.swift @@ -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, image.immediateThumbnailData, "action")]) + 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.map({ VideoRepresentationWithReference(representation: $0, reference: .media(media: .message(message: MessageReference(message), media: media), resource: $0.resource)) }), peer, message.timestamp, nil, message.id, image.immediateThumbnailData, "action")]) let galleryController = AvatarGalleryController(context: context, peer: peer, sourceCorners: .roundRect(15.5), remoteEntries: promise, skipInitial: true, replaceRootController: { controller, ready in }) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index c7b30e739a..28a8555c8c 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -158,8 +158,8 @@ final class PeerInfoHeaderNavigationTransition { } enum PeerInfoAvatarListItem: Equatable { - case topImage([ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], Data?) - case image(TelegramMediaImageReference?, [ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], Data?) + case topImage([ImageRepresentationWithReference], [VideoRepresentationWithReference], Data?) + case image(TelegramMediaImageReference?, [ImageRepresentationWithReference], [VideoRepresentationWithReference], Data?) var id: WrappedMediaResourceId { switch self { @@ -172,7 +172,7 @@ enum PeerInfoAvatarListItem: Equatable { } } - var videoRepresentations: [TelegramMediaImage.VideoRepresentation] { + var videoRepresentations: [VideoRepresentationWithReference] { switch self { case let .topImage(_, videoRepresentations, _): return videoRepresentations @@ -184,7 +184,7 @@ enum PeerInfoAvatarListItem: Equatable { final class PeerInfoAvatarListItemNode: ASDisplayNode { private let context: AccountContext - private let peerId: PeerId? + private let peer: Peer let imageNode: TransformImageNode private var videoNode: UniversalVideoNode? private var videoContent: NativeVideoContent? @@ -197,17 +197,25 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode { var item: PeerInfoAvatarListItem? - private var statusPromise = Promise<(MediaPlayerStatus?, Double?)?>() var mediaStatus: Signal<(MediaPlayerStatus?, Double?)?, NoError> { get { - return self.statusPromise.get() + if let videoNode = self.videoNode { + let videoStartTimestamp = self.videoStartTimestamp + return videoNode.status |> map { ($0, videoStartTimestamp) } + } else { + return .single(nil) + } } } - var isCentral: Bool = false { + var isCentral: Bool? = nil { didSet { - if self.isCentral { + guard self.isCentral != oldValue, let isCentral = self.isCentral else { + return + } + if isCentral { self.setupVideoPlayback() + self.preloadDisposable.set(nil) } else { if let videoNode = self.videoNode { self.videoNode = nil @@ -215,14 +223,17 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode { videoNode.removeFromSupernode() } } -// self.preloadDisposable.set(preloadVideoResource(postbox: self.context.account.postbox, resourceReference: )) + if let videoContent = self.videoContent { + let duration: Double = (self.videoStartTimestamp ?? 0.0) + 4.0 + self.preloadDisposable.set(preloadVideoResource(postbox: self.context.account.postbox, resourceReference: videoContent.fileReference.resourceReference(videoContent.fileReference.media.resource), duration: duration).start()) + } } } } - init(context: AccountContext, peerId: PeerId?) { + init(context: AccountContext, peer: Peer) { self.context = context - self.peerId = peerId + self.peer = peer self.imageNode = TransformImageNode() super.init() @@ -235,6 +246,7 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode { deinit { self.playbackStatusDisposable.dispose() + self.preloadDisposable.dispose() } func updateTransitionFraction(_ fraction: CGFloat, transition: ContainedViewLayoutTransition) { @@ -247,7 +259,7 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode { } private func setupVideoPlayback() { - guard let videoContent = self.videoContent, self.isCentral, self.videoNode == nil else { + guard let videoContent = self.videoContent, let isCentral = self.isCentral, isCentral, self.videoNode == nil else { return } @@ -284,9 +296,6 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode { videoNode.play() self.videoNode = videoNode - let videoStartTimestamp = self.videoStartTimestamp - self.statusPromise.set(videoNode.status |> map { ($0, videoStartTimestamp) }) - self.addSubnode(videoNode) self.isReady.set(videoNode.ready |> map { return true }) @@ -296,34 +305,34 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode { self.item = item let representations: [ImageRepresentationWithReference] - let videoRepresentations: [TelegramMediaImage.VideoRepresentation] + let videoRepresentations: [VideoRepresentationWithReference] let immediateThumbnailData: Data? - var id: Int64? + var id: Int64 switch item { case let .topImage(topRepresentations, videoRepresentationsValue, immediateThumbnail): representations = topRepresentations videoRepresentations = videoRepresentationsValue immediateThumbnailData = immediateThumbnail - if let peerId = self.peerId { - id = Int64(peerId.id) - } + id = Int64(self.peer.id.id) case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail): representations = imageRepresentations videoRepresentations = videoRepresentationsValue immediateThumbnailData = immediateThumbnail if case let .cloud(imageId, _, _) = reference { id = imageId + } else { + id = Int64(self.peer.id.id) } } self.imageNode.setSignal(chatAvatarGalleryPhoto(account: self.context.account, representations: representations, immediateThumbnailData: immediateThumbnailData, autoFetchFullSize: true, attemptSynchronously: synchronous), attemptSynchronously: synchronous, dispatchOnDisplayLink: false) - if let video = videoRepresentations.last, let id = id { - 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, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear) + if let video = videoRepresentations.last, let peerReference = PeerReference(self.peer) { + let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [])])) + let videoContent = NativeVideoContent(id: .profileVideo(id, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear) if videoContent.id != self.videoContent?.id { self.videoContent = videoContent - self.videoStartTimestamp = video.startTimestamp + self.videoStartTimestamp = video.representation.startTimestamp self.setupVideoPlayback() } } else { @@ -335,8 +344,6 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode { videoNode.removeFromSupernode() } - self.statusPromise.set(.single(nil)) - self.imageNode.imageUpdated = { [weak self] _ in guard let strongSelf = self else { return @@ -427,7 +434,7 @@ private class PeerInfoAvatarListLoadingStripNode: ASImageNode { final class PeerInfoAvatarListContainerNode: ASDisplayNode { private let context: AccountContext - var peerId: PeerId? + var peer: Peer? let controlsContainerNode: ASDisplayNode let controlsClippingNode: ASDisplayNode @@ -850,7 +857,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode { var entries: [AvatarGalleryEntry] = [] for entry in self.galleryEntries { switch entry { - case let .topImage(representations, videoRepresentations, _, immediateThumbnailData, _): + case let .topImage(representations, videoRepresentations, _, _, immediateThumbnailData, _): entries.append(entry) items.append(.topImage(representations, videoRepresentations, immediateThumbnailData)) case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _): @@ -886,7 +893,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode { let previousIndex = self.currentIndex for entry in self.galleryEntries { switch entry { - case let .topImage(representations, videoRepresentations, _, immediateThumbnailData, _): + case let .topImage(representations, videoRepresentations, _, _, immediateThumbnailData, _): entries.append(entry) items.append(.topImage(representations, videoRepresentations, immediateThumbnailData)) case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _): @@ -950,7 +957,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode { var items: [PeerInfoAvatarListItem] = [] for entry in entries { switch entry { - case let .topImage(representations, videoRepresentations, _, immediateThumbnailData, _): + case let .topImage(representations, videoRepresentations, _, _, immediateThumbnailData, _): items.append(.topImage(representations, videoRepresentations, immediateThumbnailData)) case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _): items.append(.image(reference, representations, videoRepresentations, immediateThumbnailData)) @@ -981,35 +988,37 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode { if self.currentIndex >= 0 && self.currentIndex < self.items.count { for i in max(0, self.currentIndex - 1) ... min(self.currentIndex + 1, self.items.count - 1) { validIds.append(self.items[i].id) - let itemNode: PeerInfoAvatarListItemNode + var itemNode: PeerInfoAvatarListItemNode? var wasAdded = false if let current = self.itemNodes[self.items[i].id] { itemNode = current if update { - itemNode.setup(item: self.items[i], synchronous: synchronous && i == self.currentIndex) + current.setup(item: self.items[i], synchronous: synchronous && i == self.currentIndex) } - } else { + } else if let peer = self.peer { wasAdded = true - itemNode = PeerInfoAvatarListItemNode(context: self.context, peerId: self.peerId) - itemNode.setup(item: self.items[i], synchronous: synchronous && i == self.currentIndex) - self.itemNodes[self.items[i].id] = itemNode - self.contentNode.addSubnode(itemNode) + let addedItemNode = PeerInfoAvatarListItemNode(context: self.context, peer: peer) + addedItemNode.setup(item: self.items[i], synchronous: synchronous && i == self.currentIndex) + self.itemNodes[self.items[i].id] = addedItemNode + self.contentNode.addSubnode(addedItemNode) } - itemNode.isCentral = i == self.currentIndex - - let indexOffset = CGFloat(i - self.currentIndex) - let itemFrame = CGRect(origin: CGPoint(x: indexOffset * size.width + self.transitionFraction * size.width - size.width / 2.0, y: -size.height / 2.0), size: size) - - - if wasAdded { - itemsAdded = true - addedItemNodesForAdditiveTransition.append(itemNode) - itemNode.frame = itemFrame - itemNode.update(size: size, transition: .immediate) - } else { - additiveTransitionOffset = itemNode.frame.minX - itemFrame.minX - transition.updateFrame(node: itemNode, frame: itemFrame) - itemNode.update(size: size, transition: transition) + if let itemNode = itemNode { + itemNode.isCentral = i == self.currentIndex + + let indexOffset = CGFloat(i - self.currentIndex) + let itemFrame = CGRect(origin: CGPoint(x: indexOffset * size.width + self.transitionFraction * size.width - size.width / 2.0, y: -size.height / 2.0), size: size) + + + if wasAdded { + itemsAdded = true + addedItemNodesForAdditiveTransition.append(itemNode) + itemNode.frame = itemFrame + itemNode.update(size: size, transition: .immediate) + } else { + additiveTransitionOffset = itemNode.frame.minX - itemFrame.minX + transition.updateFrame(node: itemNode, frame: itemFrame) + itemNode.update(size: size, transition: transition) + } } } } @@ -1090,9 +1099,12 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode { var frame = self.stripNodes[self.currentIndex].frame stripTransition.updateFrame(node: self.loadingStripNode, frame: frame) if let playbackProgress = self.playbackProgress { - frame.size.width = max(0.0, frame.size.width * playbackProgress) + frame.size.width = max(frame.size.height, frame.size.width * playbackProgress) } stripTransition.updateFrameAdditive(node: self.activeStripNode, frame: frame) + stripTransition.updateAlpha(node: self.activeStripNode, alpha: self.loading ? 0.0 : 1.0) + stripTransition.updateAlpha(node: self.loadingStripNode, alpha: self.loading ? 1.0 : 0.0) + self.activeStripNode.isHidden = self.stripNodes.count < 2 self.loadingStripNode.isHidden = !self.loading } @@ -1195,9 +1207,9 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode { if let item = item { let representations: [ImageRepresentationWithReference] - let videoRepresentations: [TelegramMediaImage.VideoRepresentation] + let videoRepresentations: [VideoRepresentationWithReference] let immediateThumbnailData: Data? - var id: Int64? + var id: Int64 switch item { case let .topImage(topRepresentations, videoRepresentationsValue, immediateThumbnail): representations = topRepresentations @@ -1210,19 +1222,21 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode { immediateThumbnailData = immediateThumbnail if case let .cloud(imageId, _, _) = reference { id = imageId + } else { + id = Int64(peer.id.id) } } - if let video = videoRepresentations.last, let id = id { - 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, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, autoFetchFullSizeThumbnail: true, startTimestamp: video.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear) + if let video = videoRepresentations.last, let peerReference = PeerReference(peer) { + let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [])])) + let videoContent = NativeVideoContent(id: .profileVideo(id, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear) if videoContent.id != self.videoContent?.id { let mediaManager = self.context.sharedContext.mediaManager let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded) videoNode.isUserInteractionEnabled = false videoNode.isHidden = true - if let startTimestamp = video.startTimestamp { + if let startTimestamp = video.representation.startTimestamp { self.videoStartTimestamp = startTimestamp self.playbackStatusDisposable.set((videoNode.status |> map { status -> Bool in @@ -1474,9 +1488,9 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode { if let item = item { let representations: [ImageRepresentationWithReference] - let videoRepresentations: [TelegramMediaImage.VideoRepresentation] + let videoRepresentations: [VideoRepresentationWithReference] let immediateThumbnailData: Data? - var id: Int64? + var id: Int64 switch item { case let .topImage(topRepresentations, videoRepresentationsValue, immediateThumbnail): representations = topRepresentations @@ -1489,17 +1503,19 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode { immediateThumbnailData = immediateThumbnail if case let .cloud(imageId, _, _) = reference { id = imageId - } + } else { + id = Int64(peer.id.id) + } } - if let video = videoRepresentations.last, let id = id { - 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, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, autoFetchFullSizeThumbnail: true, startTimestamp: video.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear) + if let video = videoRepresentations.last, let peerReference = PeerReference(peer) { + let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [])])) + let videoContent = NativeVideoContent(id: .profileVideo(id, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear) if videoContent.id != self.videoContent?.id { let mediaManager = self.context.sharedContext.mediaManager let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .overlay) videoNode.isUserInteractionEnabled = false - self.videoStartTimestamp = video.startTimestamp + self.videoStartTimestamp = video.representation.startTimestamp self.videoContent = videoContent self.videoNode = videoNode @@ -2594,7 +2610,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, notificationSettings: TelegramPeerNotificationSettings?, statusData: PeerInfoStatusData?, isContact: Bool, isSettings: Bool, state: PeerInfoState, transition: ContainedViewLayoutTransition, additive: Bool) -> CGFloat { self.state = state self.peer = peer - self.avatarListNode.listContainerNode.peerId = peer?.id + self.avatarListNode.listContainerNode.peer = peer let avatarSize: CGFloat = isModalOverlay ? 200.0 : 100.0 self.avatarSize = avatarSize @@ -2705,7 +2721,12 @@ final class PeerInfoHeaderNode: ASDisplayNode { if self.isSettings, let user = peer as? TelegramUser { let formattedPhone = formatPhoneNumber(user.phone ?? "") subtitleString = NSAttributedString(string: formattedPhone, font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor) - usernameString = NSAttributedString(string: user.addressName.flatMap { "@\($0)" } ?? "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor) + + var username = "" + if let addressName = user.addressName, !addressName.isEmpty { + username = "@\(addressName)" + } + usernameString = NSAttributedString(string: username, font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor) } else if let statusData = statusData { let subtitleColor: UIColor if statusData.isActivity { diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 6b2a138d98..dca1025f7f 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -3838,7 +3838,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD let mediaReference: AnyMediaReference if let video = videoRepresentations.last { - mediaReference = .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: [])])) + mediaReference = .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [])])) } else { let media = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: []) mediaReference = .standalone(media: media)