mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 11:23:48 +00:00
Merge commit 'cccc13ac499d5e0c47c690b4360850fc8f21ac56'
This commit is contained in:
commit
61bb79c25e
1
bazel-Telegram-iOS-Beta
Symbolic link
1
bazel-Telegram-iOS-Beta
Symbolic link
@ -0,0 +1 @@
|
||||
/private/var/tmp/_bazel_ilya/b5422c0ab62ebf818a9d47ff552063a2/execroot/__main__
|
@ -250,10 +250,10 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
||||
}
|
||||
}
|
||||
|
||||
override func setVisibilityAlpha(_ alpha: CGFloat) {
|
||||
override func setVisibilityAlpha(_ alpha: CGFloat, animated: Bool) {
|
||||
self.visibilityAlpha = alpha
|
||||
self.contentNode.alpha = alpha
|
||||
self.scrubberView?.setCollapsed(alpha < 1.0 ? true : false, animated: true)
|
||||
self.scrubberView?.setCollapsed(alpha < 1.0 ? true : false, animated: animated)
|
||||
}
|
||||
|
||||
init(context: AccountContext, presentationData: PresentationData, present: @escaping (ViewController, Any?) -> Void = { _, _ in }) {
|
||||
|
@ -120,13 +120,32 @@ final class ChatVideoGalleryItemScrubberView: UIView {
|
||||
self.fetchStatusDisposable.dispose()
|
||||
}
|
||||
|
||||
var collapsed: Bool = false
|
||||
func setCollapsed(_ collapsed: Bool, animated: Bool) {
|
||||
let alpha: CGFloat = collapsed ? 0.0 : 1.0
|
||||
guard self.collapsed != collapsed else {
|
||||
return
|
||||
}
|
||||
|
||||
self.scrubberNode.setCollapsed(collapsed, animated: animated)
|
||||
self.collapsed = collapsed
|
||||
|
||||
let alpha: CGFloat = collapsed ? 0.0 : 1.0
|
||||
self.leftTimestampNode.alpha = alpha
|
||||
self.rightTimestampNode.alpha = alpha
|
||||
self.infoNode.alpha = alpha
|
||||
self.updateScrubberVisibility(animated: animated)
|
||||
}
|
||||
|
||||
private func updateScrubberVisibility(animated: Bool) {
|
||||
var collapsed = self.collapsed
|
||||
var alpha: CGFloat = 1.0
|
||||
if let playbackStatus = self.playbackStatus, playbackStatus.duration <= 30.0 {
|
||||
} else {
|
||||
alpha = self.collapsed ? 0.0 : 1.0
|
||||
collapsed = false
|
||||
}
|
||||
self.scrubberNode.setCollapsed(collapsed, animated: animated)
|
||||
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.3, curve: .linear) : .immediate
|
||||
transition.updateAlpha(node: self.scrubberNode, alpha: alpha)
|
||||
}
|
||||
|
||||
func setStatusSignal(_ status: Signal<MediaPlayerStatus, NoError>?) {
|
||||
@ -232,7 +251,7 @@ final class ChatVideoGalleryItemScrubberView: UIView {
|
||||
self.containerLayout = (size, leftInset, rightInset)
|
||||
|
||||
let scrubberHeight: CGFloat = 14.0
|
||||
let scrubberInset: CGFloat
|
||||
var scrubberInset: CGFloat
|
||||
let leftTimestampOffset: CGFloat
|
||||
let rightTimestampOffset: CGFloat
|
||||
let infoOffset: CGFloat
|
||||
@ -257,7 +276,7 @@ final class ChatVideoGalleryItemScrubberView: UIView {
|
||||
let infoSize = self.infoNode.measure(infoConstrainedSize)
|
||||
self.infoNode.bounds = CGRect(origin: CGPoint(), size: infoSize)
|
||||
transition.updatePosition(node: self.infoNode, position: CGPoint(x: size.width / 2.0, y: infoOffset + infoSize.height / 2.0))
|
||||
self.infoNode.alpha = size.width < size.height ? 1.0 : 0.0
|
||||
self.infoNode.alpha = size.width < size.height && !self.collapsed ? 1.0 : 0.0
|
||||
|
||||
self.scrubberNode.frame = CGRect(origin: CGPoint(x: scrubberInset, y: 6.0), size: CGSize(width: size.width - leftInset - rightInset - scrubberInset * 2.0, height: scrubberHeight))
|
||||
}
|
||||
|
@ -935,6 +935,7 @@ public class GalleryController: ViewController, StandalonePresentableController
|
||||
|
||||
self.galleryNode.controlsVisibilityChanged = { [weak self] visible in
|
||||
self?.prefersOnScreenNavigationHidden = !visible
|
||||
self?.galleryNode.pager.centralItemNode()?.controlsVisibilityUpdated(isVisible: visible)
|
||||
}
|
||||
|
||||
let baseNavigationController = self.baseNavigationController
|
||||
|
@ -253,7 +253,7 @@ open class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGesture
|
||||
if displayThumbnailPanel {
|
||||
thumbnailPanelHeight = 52.0
|
||||
}
|
||||
let thumbnailsFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - 40.0 - panelHeight + 4.0 - layout.intrinsicInsets.bottom), size: CGSize(width: layout.size.width, height: panelHeight - 4.0))
|
||||
let thumbnailsFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - 40.0 - panelHeight + 4.0 - layout.intrinsicInsets.bottom + (self.areControlsHidden ? 106.0 : 0.0)), size: CGSize(width: layout.size.width, height: panelHeight - 4.0))
|
||||
transition.updateFrame(node: currentThumbnailContainerNode, frame: thumbnailsFrame)
|
||||
currentThumbnailContainerNode.updateLayout(size: thumbnailsFrame.size, transition: transition)
|
||||
|
||||
@ -279,6 +279,9 @@ open class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGesture
|
||||
}
|
||||
|
||||
open func setControlsHidden(_ hidden: Bool, animated: Bool) {
|
||||
guard self.areControlsHidden != hidden else {
|
||||
return
|
||||
}
|
||||
self.areControlsHidden = hidden
|
||||
self.controlsVisibilityChanged?(!hidden)
|
||||
if animated {
|
||||
@ -286,7 +289,7 @@ open class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGesture
|
||||
let alpha: CGFloat = self.areControlsHidden ? 0.0 : 1.0
|
||||
self.navigationBar?.alpha = alpha
|
||||
self.statusBar?.updateAlpha(alpha, transition: .animated(duration: 0.3, curve: .easeInOut))
|
||||
self.footerNode.setVisibilityAlpha(alpha)
|
||||
self.footerNode.setVisibilityAlpha(alpha, animated: animated)
|
||||
self.updateThumbnailContainerNodeAlpha(.immediate)
|
||||
})
|
||||
|
||||
@ -297,7 +300,7 @@ open class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGesture
|
||||
let alpha: CGFloat = self.areControlsHidden ? 0.0 : 1.0
|
||||
self.navigationBar?.alpha = alpha
|
||||
self.statusBar?.updateAlpha(alpha, transition: .immediate)
|
||||
self.footerNode.setVisibilityAlpha(alpha)
|
||||
self.footerNode.setVisibilityAlpha(alpha, animated: animated)
|
||||
self.updateThumbnailContainerNodeAlpha(.immediate)
|
||||
|
||||
if let (navigationBarHeight, layout) = self.containerLayout {
|
||||
|
@ -21,7 +21,7 @@ open class GalleryFooterContentNode: ASDisplayNode {
|
||||
public var controllerInteraction: GalleryControllerInteraction?
|
||||
|
||||
var visibilityAlpha: CGFloat = 1.0
|
||||
open func setVisibilityAlpha(_ alpha: CGFloat) {
|
||||
open func setVisibilityAlpha(_ alpha: CGFloat, animated: Bool) {
|
||||
self.visibilityAlpha = alpha
|
||||
self.alpha = alpha
|
||||
}
|
||||
|
@ -24,10 +24,10 @@ public final class GalleryFooterNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
private var visibilityAlpha: CGFloat = 1.0
|
||||
public func setVisibilityAlpha(_ alpha: CGFloat) {
|
||||
public func setVisibilityAlpha(_ alpha: CGFloat, animated: Bool) {
|
||||
self.visibilityAlpha = alpha
|
||||
self.backgroundNode.alpha = alpha
|
||||
self.currentFooterContentNode?.setVisibilityAlpha(alpha)
|
||||
self.currentFooterContentNode?.setVisibilityAlpha(alpha, animated: true)
|
||||
self.currentOverlayContentNode?.setVisibilityAlpha(alpha)
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ public final class GalleryFooterNode: ASDisplayNode {
|
||||
}
|
||||
self.currentFooterContentNode = footerContentNode
|
||||
if let footerContentNode = footerContentNode {
|
||||
footerContentNode.setVisibilityAlpha(self.visibilityAlpha)
|
||||
footerContentNode.setVisibilityAlpha(self.visibilityAlpha, animated: transition.isAnimated)
|
||||
footerContentNode.controllerInteraction = self.controllerInteraction
|
||||
footerContentNode.requestLayout = { [weak self] transition in
|
||||
if let strongSelf = self, let (currentLayout, currentThumbnailPanelHeight, isHidden) = strongSelf.currentLayout {
|
||||
@ -67,7 +67,7 @@ public final class GalleryFooterNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
var backgroundHeight: CGFloat = 0.0
|
||||
let verticalOffset: CGFloat = isHidden ? (layout.size.width > layout.size.height ? 44.0 : 54.0) : 0.0
|
||||
let verticalOffset: CGFloat = isHidden ? (layout.size.width > layout.size.height ? 44.0 : (thumbnailPanelHeight > 0.0 ? 106.0 : 54.0)) : 0.0
|
||||
if let footerContentNode = self.currentFooterContentNode {
|
||||
backgroundHeight = footerContentNode.updateLayout(size: layout.size, metrics: layout.metrics, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: cleanInsets.bottom, contentInset: thumbnailPanelHeight, transition: transition)
|
||||
transition.updateFrame(node: footerContentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - backgroundHeight + verticalOffset), size: CGSize(width: layout.size.width, height: backgroundHeight)))
|
||||
|
@ -82,6 +82,9 @@ open class GalleryItemNode: ASDisplayNode {
|
||||
open func visibilityUpdated(isVisible: Bool) {
|
||||
}
|
||||
|
||||
open func controlsVisibilityUpdated(isVisible: Bool) {
|
||||
}
|
||||
|
||||
open func animateIn(from node: (ASDisplayNode, CGRect, () -> (UIView?, UIView?)), addToTransitionSurface: (UIView) -> Void, completion: @escaping () -> Void) {
|
||||
}
|
||||
|
||||
|
@ -661,7 +661,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
seekable = value.duration >= 30.0
|
||||
}
|
||||
|
||||
if playing && strongSelf.previousPlaying != true {
|
||||
if strongSelf.isCentral && playing && strongSelf.previousPlaying != true && !disablePlayerControls {
|
||||
strongSelf.controlsTimer?.invalidate()
|
||||
|
||||
let timer = SwiftSignalKit.Timer(timeout: 3.0, repeat: false, completion: { [weak self] in
|
||||
self?.updateControlsVisibility(false)
|
||||
self?.controlsTimer = nil
|
||||
@ -762,6 +764,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
self.footerContentNode.setup(origin: item.originData, caption: item.caption)
|
||||
}
|
||||
|
||||
override func controlsVisibilityUpdated(isVisible: Bool) {
|
||||
self.controlsTimer?.invalidate()
|
||||
self.controlsTimer = nil
|
||||
}
|
||||
|
||||
private func updateDisplayPlaceholder(_ displayPlaceholder: Bool) {
|
||||
if displayPlaceholder {
|
||||
if self.pictureInPictureNode == nil {
|
||||
|
@ -445,7 +445,12 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500;
|
||||
TGMediaAssetImageType screenImageType = refresh ? TGMediaAssetImageTypeLargeThumbnail : TGMediaAssetImageTypeFastLargeThumbnail;
|
||||
TGMediaAssetImageType imageType = thumbnail ? TGMediaAssetImageTypeAspectRatioThumbnail : screenImageType;
|
||||
|
||||
SSignal *assetSignal = [TGMediaAssetImageSignals imageForAsset:asset imageType:imageType size:imageSize];
|
||||
TGMediaAsset *concreteAsset = asset;
|
||||
if ([concreteAsset isKindOfClass:[TGCameraCapturedVideo class]]) {
|
||||
concreteAsset = [(TGCameraCapturedVideo *)asset originalAsset];
|
||||
}
|
||||
|
||||
SSignal *assetSignal = [TGMediaAssetImageSignals imageForAsset:concreteAsset imageType:imageType size:imageSize];
|
||||
if (_editingContext == nil)
|
||||
return assetSignal;
|
||||
|
||||
|
@ -81,6 +81,7 @@ const CGFloat TGGifConverterMaximumSide = 720.0f;
|
||||
CGFloat renderWidth = CGFloor(sourceWidth / blockSize) * blockSize;
|
||||
CGFloat renderHeight = CGFloor(sourceHeight * renderWidth / sourceWidth);
|
||||
|
||||
CGSize renderSize = CGSizeMake(renderWidth, renderHeight);
|
||||
CGSize targetSize = TGFitSizeF(CGSizeMake(renderWidth, renderHeight), CGSizeMake(TGGifConverterMaximumSide, TGGifConverterMaximumSide));
|
||||
|
||||
NSDictionary *videoCleanApertureSettings = @
|
||||
@ -152,11 +153,11 @@ const CGFloat TGGifConverterMaximumSide = 720.0f;
|
||||
|
||||
if (gifProperties != NULL)
|
||||
{
|
||||
CVPixelBufferRef pxBuffer = [self newBufferFrom:imgRef size:targetSize withPixelBufferPool:adaptor.pixelBufferPool andAttributes:adaptor.sourcePixelBufferAttributes];
|
||||
CVPixelBufferRef pxBuffer = [self newBufferFrom:imgRef size:renderSize withPixelBufferPool:adaptor.pixelBufferPool andAttributes:adaptor.sourcePixelBufferAttributes];
|
||||
if (pxBuffer != NULL)
|
||||
{
|
||||
if (previewImage == nil) {
|
||||
previewImage = TGScaleImageToPixelSize([[UIImage alloc] initWithCGImage:imgRef], targetSize);
|
||||
previewImage = TGScaleImageToPixelSize([[UIImage alloc] initWithCGImage:imgRef], renderSize);
|
||||
}
|
||||
float frameDuration = 0.1f;
|
||||
NSNumber *delayTimeUnclampedProp = CFDictionaryGetValue(gifProperties, kCGImagePropertyGIFUnclampedDelayTime);
|
||||
|
@ -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];
|
||||
|
@ -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)
|
||||
}))
|
||||
|
@ -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
|
||||
|
@ -18,12 +18,14 @@ private func scanFiles(at path: String, olderThan minTimestamp: Int32, anyway: (
|
||||
continue
|
||||
}
|
||||
if let value = resourceValues[.contentModificationDateKey] as? NSDate {
|
||||
var unlinked = false
|
||||
if Int32(value.timeIntervalSince1970) < minTimestamp {
|
||||
if let file = url.path {
|
||||
f(file)
|
||||
unlinked = true
|
||||
}
|
||||
}
|
||||
if let file = url.path {
|
||||
if let file = url.path, !unlinked {
|
||||
if let size = (resourceValues[.fileSizeKey] as? NSNumber)?.intValue {
|
||||
anyway((file, size, Int32(value.timeIntervalSince1970)))
|
||||
}
|
||||
|
@ -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<T: Media> {
|
||||
case webPage
|
||||
case stickerPack
|
||||
case savedGif
|
||||
case avatarList
|
||||
}
|
||||
|
||||
case standalone(media: T)
|
||||
@ -387,6 +403,7 @@ public enum MediaReference<T: Media> {
|
||||
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<T: Media> {
|
||||
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<T: Media> {
|
||||
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<T: Media> {
|
||||
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<T: Media> {
|
||||
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
|
||||
|
@ -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<RevalidatedMediaResource, RevalidateMediaReferenceError> 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<RevalidatedMediaResource, RevalidateMediaReferenceError> 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<RevalidatedMediaResource, RevalidateMediaReferenceError> 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<RevalidatedMediaResource, RevalidateMediaReferenceError> in
|
||||
for theme in themes {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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<Api.photos.Photos?, NoError> 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<Api.messages.Messages?, NoError> 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 {
|
||||
|
@ -8,6 +8,8 @@ func parsedTelegramProfilePhoto(_ photo: Api.UserProfilePhoto) -> [TelegramMedia
|
||||
var representations: [TelegramMediaImageRepresentation] = []
|
||||
switch photo {
|
||||
case let .userProfilePhoto(flags, _, photoSmall, photoBig, dcId):
|
||||
let hasVideo = (flags & (1 << 0)) != 0
|
||||
|
||||
let smallResource: TelegramMediaResource
|
||||
let fullSizeResource: TelegramMediaResource
|
||||
switch photoSmall {
|
||||
|
@ -317,6 +317,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
private var focusOnSearchAfterAppearance: (ChatSearchDomain, String)?
|
||||
|
||||
private let keepPeerInfoScreenDataHotDisposable = MetaDisposable()
|
||||
private let preloadAvatarDisposable = MetaDisposable()
|
||||
|
||||
private let peekData: ChatPeekTimeout?
|
||||
private let peekTimerDisposable = MetaDisposable()
|
||||
@ -2749,6 +2750,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.reminderActivity?.invalidate()
|
||||
self.updateSlowmodeStatusDisposable.dispose()
|
||||
self.keepPeerInfoScreenDataHotDisposable.dispose()
|
||||
self.preloadAvatarDisposable.dispose()
|
||||
self.peekTimerDisposable.dispose()
|
||||
}
|
||||
|
||||
@ -5063,7 +5065,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}))
|
||||
|
||||
if case let .peer(peerId) = self.chatLocation {
|
||||
self.keepPeerInfoScreenDataHotDisposable.set(keepPeerInfoScreenDataHot(context: self.context, peerId: peerId).start())
|
||||
let context = self.context
|
||||
self.keepPeerInfoScreenDataHotDisposable.set(keepPeerInfoScreenDataHot(context: context, peerId: peerId).start())
|
||||
self.preloadAvatarDisposable.set((peerInfoProfilePhotosWithCache(context: context, peerId: peerId)
|
||||
|> mapToSignal { result -> Signal<Never, NoError> in
|
||||
var signals: [Signal<Never, NoError>] = [.complete()]
|
||||
for i in 0 ..< min(5, result.count) {
|
||||
if let video = result[i].videoRepresentations.first {
|
||||
let duration: Double = (video.representation.startTimestamp ?? 0.0) + (i == 0 ? 4.0 : 2.0)
|
||||
signals.append(preloadVideoResource(postbox: context.account.postbox, resourceReference: video.reference, duration: duration))
|
||||
}
|
||||
}
|
||||
return combineLatest(signals) |> mapToSignal { _ in
|
||||
return .never()
|
||||
}
|
||||
}).start())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
})
|
||||
|
@ -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?
|
||||
@ -200,29 +200,48 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
||||
private var statusPromise = Promise<(MediaPlayerStatus?, Double?)?>()
|
||||
var mediaStatus: Signal<(MediaPlayerStatus?, Double?)?, NoError> {
|
||||
get {
|
||||
if let videoNode = self.videoNode {
|
||||
let videoStartTimestamp = self.videoStartTimestamp
|
||||
return videoNode.status |> map { ($0, videoStartTimestamp) }
|
||||
} else {
|
||||
return self.statusPromise.get()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var isCentral: Bool = false {
|
||||
var delayCentralityLose = 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.playbackStatusDisposable.set(nil)
|
||||
self.statusPromise.set(.single(nil))
|
||||
self.videoNode = nil
|
||||
if self.delayCentralityLose {
|
||||
Queue.mainQueue().after(0.5) {
|
||||
videoNode.removeFromSupernode()
|
||||
}
|
||||
} else {
|
||||
videoNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
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())
|
||||
}
|
||||
// self.preloadDisposable.set(preloadVideoResource(postbox: self.context.account.postbox, resourceReference: ))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 +254,7 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
||||
|
||||
deinit {
|
||||
self.playbackStatusDisposable.dispose()
|
||||
self.preloadDisposable.dispose()
|
||||
}
|
||||
|
||||
func updateTransitionFraction(_ fraction: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
@ -247,7 +267,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
|
||||
}
|
||||
|
||||
@ -272,7 +292,7 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
Queue.mainQueue().after(0.15) {
|
||||
Queue.mainQueue().after(0.1) {
|
||||
strongSelf.videoNode?.isHidden = false
|
||||
}
|
||||
}
|
||||
@ -296,34 +316,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: false, 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 {
|
||||
@ -427,7 +447,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
|
||||
@ -765,7 +785,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
||||
if self.currentIndex != previousIndex {
|
||||
self.currentIndexUpdated?()
|
||||
}
|
||||
self.updateItems(size: size, transition: .immediate, stripTransition: .animated(duration: 0.3, curve: .spring))
|
||||
self.updateItems(size: size, transition: .immediate, stripTransition: .animated(duration: 0.3, curve: .spring), synchronous: true)
|
||||
} else if self.items.count > 1 {
|
||||
let previousIndex = self.currentIndex
|
||||
self.currentIndex = self.items.count - 1
|
||||
@ -781,7 +801,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
||||
if self.currentIndex != previousIndex {
|
||||
self.currentIndexUpdated?()
|
||||
}
|
||||
self.updateItems(size: size, transition: .immediate, stripTransition: .animated(duration: 0.3, curve: .spring))
|
||||
self.updateItems(size: size, transition: .immediate, stripTransition: .animated(duration: 0.3, curve: .spring), synchronous: true)
|
||||
} else if self.items.count > 1 {
|
||||
let previousIndex = self.currentIndex
|
||||
self.currentIndex = 0
|
||||
@ -798,6 +818,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
private var pageChangedByPan = false
|
||||
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .changed:
|
||||
@ -831,11 +852,13 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
||||
let previousIndex = self.currentIndex
|
||||
self.currentIndex = updatedIndex
|
||||
if self.currentIndex != previousIndex {
|
||||
self.pageChangedByPan = true
|
||||
self.currentIndexUpdated?()
|
||||
}
|
||||
self.transitionFraction = 0.0
|
||||
if let size = self.validLayout {
|
||||
self.updateItems(size: size, transition: .animated(duration: 0.3, curve: .spring), stripTransition: .animated(duration: 0.3, curve: .spring))
|
||||
self.pageChangedByPan = false
|
||||
}
|
||||
default:
|
||||
break
|
||||
@ -850,7 +873,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 +909,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 +973,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))
|
||||
@ -979,28 +1002,32 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
||||
var additiveTransitionOffset: CGFloat = 0.0
|
||||
var itemsAdded = false
|
||||
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) {
|
||||
let preloadSpan: Int = 2
|
||||
for i in max(0, self.currentIndex - preloadSpan) ... min(self.currentIndex + preloadSpan, 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)
|
||||
itemNode = addedItemNode
|
||||
addedItemNode.setup(item: self.items[i], synchronous: synchronous && i == self.currentIndex)
|
||||
self.itemNodes[self.items[i].id] = addedItemNode
|
||||
self.contentNode.addSubnode(addedItemNode)
|
||||
}
|
||||
if let itemNode = itemNode {
|
||||
itemNode.delayCentralityLose = self.pageChangedByPan
|
||||
itemNode.isCentral = i == self.currentIndex
|
||||
itemNode.delayCentralityLose = false
|
||||
|
||||
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)
|
||||
@ -1009,7 +1036,8 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
||||
} else {
|
||||
additiveTransitionOffset = itemNode.frame.minX - itemFrame.minX
|
||||
transition.updateFrame(node: itemNode, frame: itemFrame)
|
||||
itemNode.update(size: size, transition: transition)
|
||||
itemNode.update(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1090,9 +1118,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 +1226,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 +1241,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 +1507,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 +1522,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
|
||||
|
||||
@ -2542,8 +2577,9 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
|
||||
func initiateAvatarExpansion(gallery: Bool) {
|
||||
if self.isAvatarExpanded || gallery {
|
||||
if let currentEntry = self.avatarListNode.listContainerNode.currentEntry {
|
||||
self.requestAvatarExpansion?(true, self.avatarListNode.listContainerNode.galleryEntries, self.avatarListNode.listContainerNode.currentEntry, self.avatarTransitionArguments(entry: currentEntry))
|
||||
if let currentEntry = self.avatarListNode.listContainerNode.currentEntry, let firstEntry = self.avatarListNode.listContainerNode.galleryEntries.first {
|
||||
let entry = gallery ? firstEntry : currentEntry
|
||||
self.requestAvatarExpansion?(true, self.avatarListNode.listContainerNode.galleryEntries, entry, self.avatarTransitionArguments(entry: currentEntry))
|
||||
}
|
||||
} else if let entry = self.avatarListNode.listContainerNode.galleryEntries.first {
|
||||
let _ = self.avatarListNode.avatarContainerNode.avatarNode
|
||||
@ -2594,7 +2630,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 +2741,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 {
|
||||
|
@ -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)
|
||||
|
@ -2,7 +2,13 @@
|
||||
#define OngoingCallContext_h
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
#import <AppKit/AppKit.h>
|
||||
#define UIView NSView
|
||||
#endif
|
||||
|
||||
@interface OngoingCallConnectionDescriptionWebrtc : NSObject
|
||||
|
||||
|
@ -1,11 +1,22 @@
|
||||
#ifndef WEBRTC_IOS
|
||||
#import "OngoingCallThreadLocalContext.h"
|
||||
#else
|
||||
#import <TgVoip/OngoingCallThreadLocalContext.h>
|
||||
#endif
|
||||
|
||||
|
||||
#import "Instance.h"
|
||||
#import "InstanceImpl.h"
|
||||
#import "VideoCaptureInterface.h"
|
||||
|
||||
#ifndef WEBRTC_IOS
|
||||
#import "platform/darwin/VideoMetalViewMac.h"
|
||||
#define GLVideoView VideoMetalView
|
||||
#define UIViewContentModeScaleAspectFill kCAGravityResizeAspectFill
|
||||
#else
|
||||
#import "platform/darwin/VideoMetalView.h"
|
||||
#import "platform/darwin/GLVideoView.h"
|
||||
#endif
|
||||
|
||||
@implementation OngoingCallConnectionDescriptionWebrtc
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user