Video avatar fixes

This commit is contained in:
Ilya Laktyushin 2020-07-10 18:36:28 +03:00
parent ab41dbe22a
commit b51dd938f4
7 changed files with 202 additions and 106 deletions

View File

@ -23,12 +23,12 @@ public enum AvatarGalleryEntryId: Hashable {
} }
public enum AvatarGalleryEntry: Equatable { public enum AvatarGalleryEntry: Equatable {
case topImage([ImageRepresentationWithReference], GalleryItemIndexData?, Data?, String?) case topImage([ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], GalleryItemIndexData?, Data?, String?)
case image(MediaId, TelegramMediaImageReference?, [ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], Peer?, Int32, GalleryItemIndexData?, MessageId?, Data?, String?) case image(MediaId, TelegramMediaImageReference?, [ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], Peer?, Int32, GalleryItemIndexData?, MessageId?, Data?, String?)
public var id: AvatarGalleryEntryId { public var id: AvatarGalleryEntryId {
switch self { switch self {
case let .topImage(representations, _, _, _): case let .topImage(representations, _, _, _, _):
if let last = representations.last { if let last = representations.last {
return .resource(last.representation.resource.id.uniqueId) return .resource(last.representation.resource.id.uniqueId)
} }
@ -43,7 +43,7 @@ public enum AvatarGalleryEntry: Equatable {
public var representations: [ImageRepresentationWithReference] { public var representations: [ImageRepresentationWithReference] {
switch self { switch self {
case let .topImage(representations, _, _, _): case let .topImage(representations, _, _, _, _):
return representations return representations
case let .image(_, _, representations, _, _, _, _, _, _, _): case let .image(_, _, representations, _, _, _, _, _, _, _):
return representations return representations
@ -52,8 +52,8 @@ public enum AvatarGalleryEntry: Equatable {
public var videoRepresentations: [TelegramMediaImage.VideoRepresentation] { public var videoRepresentations: [TelegramMediaImage.VideoRepresentation] {
switch self { switch self {
case .topImage: case let .topImage(_, videoRepresentations, _, _, _):
return [] return videoRepresentations
case let .image(_, _, _, videoRepresentations, _, _, _, _, _, _): case let .image(_, _, _, videoRepresentations, _, _, _, _, _, _):
return videoRepresentations return videoRepresentations
} }
@ -61,7 +61,7 @@ public enum AvatarGalleryEntry: Equatable {
public var indexData: GalleryItemIndexData? { public var indexData: GalleryItemIndexData? {
switch self { switch self {
case let .topImage(_, indexData, _, _): case let .topImage(_, _, indexData, _, _):
return indexData return indexData
case let .image(_, _, _, _, _, _, indexData, _, _, _): case let .image(_, _, _, _, _, _, indexData, _, _, _):
return indexData return indexData
@ -70,8 +70,8 @@ public enum AvatarGalleryEntry: Equatable {
public static func ==(lhs: AvatarGalleryEntry, rhs: AvatarGalleryEntry) -> Bool { public static func ==(lhs: AvatarGalleryEntry, rhs: AvatarGalleryEntry) -> Bool {
switch lhs { switch lhs {
case let .topImage(lhsRepresentations, lhsIndexData, lhsImmediateThumbnailData, lhsCategory): case let .topImage(lhsRepresentations, lhsVideoRepresentations, lhsIndexData, lhsImmediateThumbnailData, lhsCategory):
if case let .topImage(rhsRepresentations, rhsIndexData, rhsImmediateThumbnailData, rhsCategory) = rhs, lhsRepresentations == rhsRepresentations, lhsIndexData == rhsIndexData, lhsImmediateThumbnailData == rhsImmediateThumbnailData, lhsCategory == rhsCategory { if case let .topImage(rhsRepresentations, rhsVideoRepresentations, rhsIndexData, rhsImmediateThumbnailData, rhsCategory) = rhs, lhsRepresentations == rhsRepresentations, lhsVideoRepresentations == rhsVideoRepresentations, lhsIndexData == rhsIndexData, lhsImmediateThumbnailData == rhsImmediateThumbnailData, lhsCategory == rhsCategory {
return true return true
} else { } else {
return false return false
@ -102,8 +102,8 @@ public func normalizeEntries(_ entries: [AvatarGalleryEntry]) -> [AvatarGalleryE
var index: Int32 = 0 var index: Int32 = 0
for entry in entries { for entry in entries {
let indexData = GalleryItemIndexData(position: index, totalCount: count) let indexData = GalleryItemIndexData(position: index, totalCount: count)
if case let .topImage(representations, _, immediateThumbnailData, category) = entry { if case let .topImage(representations, videoRepresentations, _, immediateThumbnailData, category) = entry {
updatedEntries.append(.topImage(representations, indexData, immediateThumbnailData, category)) updatedEntries.append(.topImage(representations, videoRepresentations, indexData, immediateThumbnailData, category))
} else if case let .image(id, reference, representations, videoRepresentations, peer, date, _, messageId, immediateThumbnailData, category) = entry { } 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)) updatedEntries.append(.image(id, reference, representations, videoRepresentations, peer, date, indexData, messageId, immediateThumbnailData, category))
} }
@ -115,15 +115,24 @@ public func normalizeEntries(_ entries: [AvatarGalleryEntry]) -> [AvatarGalleryE
public func initialAvatarGalleryEntries(account: Account, peer: Peer) -> Signal<[AvatarGalleryEntry], NoError> { public func initialAvatarGalleryEntries(account: Account, peer: Peer) -> Signal<[AvatarGalleryEntry], NoError> {
var initialEntries: [AvatarGalleryEntry] = [] var initialEntries: [AvatarGalleryEntry] = []
if !peer.profileImageRepresentations.isEmpty, let peerReference = PeerReference(peer) { 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)) }), [], nil, nil, nil))
} }
if let peer = peer as? TelegramChannel { if peer is TelegramChannel || peer is TelegramGroup {
return account.postbox.transaction { transaction in return account.postbox.transaction { transaction in
return transaction.getPeerCachedData(peerId: peer.id) return transaction.getPeerCachedData(peerId: peer.id)
} |> map { cachedData in } |> map { cachedData in
if let cachedData = cachedData as? CachedChannelData, let photo = cachedData.photo { var initialPhoto: TelegramMediaImage?
return [.image(photo.imageId, photo.reference, photo.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.standalone(resource: $0.resource)) }), photo.videoRepresentations, peer, 0, nil, nil, photo.immediateThumbnailData, nil)] if let cachedData = cachedData as? CachedGroupData, let photo = cachedData.photo {
initialPhoto = photo
}
else if let cachedData = cachedData as? CachedChannelData, let photo = cachedData.photo {
initialPhoto = photo
}
if let photo = initialPhoto {
return [.topImage(photo.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.standalone(resource: $0.resource)) }), photo.videoRepresentations, nil, nil, nil)]
// return [.image(photo.imageId, photo.reference, photo.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.standalone(resource: $0.resource)) }), photo.videoRepresentations, peer, 0, nil, nil, photo.immediateThumbnailData, nil)]
} else { } else {
return initialEntries return initialEntries
} }
@ -233,7 +242,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
private let editDisposable = MetaDisposable () private let editDisposable = MetaDisposable ()
public init(context: AccountContext, peer: Peer, sourceHasRoundCorners: Bool = true, remoteEntries: Promise<[AvatarGalleryEntry]>? = nil, centralEntryIndex: Int? = nil, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, synchronousLoad: Bool = false) { public init(context: AccountContext, peer: Peer, sourceHasRoundCorners: Bool = true, remoteEntries: Promise<[AvatarGalleryEntry]>? = nil, skipInitial: Bool = false, centralEntryIndex: Int? = nil, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, synchronousLoad: Bool = false) {
self.context = context self.context = context
self.peer = peer self.peer = peer
self.sourceHasRoundCorners = sourceHasRoundCorners self.sourceHasRoundCorners = sourceHasRoundCorners
@ -256,7 +265,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
remoteEntriesSignal = fetchedAvatarGalleryEntries(account: context.account, peer: peer) remoteEntriesSignal = fetchedAvatarGalleryEntries(account: context.account, peer: peer)
} }
let entriesSignal: Signal<[AvatarGalleryEntry], NoError> = initialAvatarGalleryEntries(account: context.account, peer: peer) |> then(remoteEntriesSignal) let entriesSignal: Signal<[AvatarGalleryEntry], NoError> = skipInitial ? remoteEntriesSignal : (initialAvatarGalleryEntries(account: context.account, peer: peer) |> then(remoteEntriesSignal))
let presentationData = self.presentationData let presentationData = self.presentationData

View File

@ -104,7 +104,7 @@ class PeerAvatarImageGalleryItem: GalleryItem {
func thumbnailItem() -> (Int64, GalleryThumbnailItem)? { func thumbnailItem() -> (Int64, GalleryThumbnailItem)? {
let content: [ImageRepresentationWithReference] let content: [ImageRepresentationWithReference]
switch self.entry { switch self.entry {
case let .topImage(representations, _, _, _): case let .topImage(representations, _, _, _, _):
content = representations content = representations
case let .image(_, _, representations, _, _, _, _, _, _, _): case let .image(_, _, representations, _, _, _, _, _, _, _):
content = representations content = representations
@ -256,6 +256,8 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
if case let .image(image) = entry { if case let .image(image) = entry {
id = image.0.id id = image.0.id
category = image.9 category = image.9
} else {
id = 1
} }
if let video = entry.videoRepresentations.last, let id = id { if let video = entry.videoRepresentations.last, let id = id {
if video != previousVideoRepresentations?.last { if video != previousVideoRepresentations?.last {
@ -551,7 +553,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
case .Remote: case .Remote:
let representations: [ImageRepresentationWithReference] let representations: [ImageRepresentationWithReference]
switch entry { switch entry {
case let .topImage(topRepresentations, _, _, _): case let .topImage(topRepresentations, _, _, _, _):
representations = topRepresentations representations = topRepresentations
case let .image(_, _, imageRepresentations, _, _, _, _, _, _, _): case let .image(_, _, imageRepresentations, _, _, _, _, _, _, _):
representations = imageRepresentations representations = imageRepresentations

View File

@ -49,6 +49,7 @@ public final class CachedGroupData: CachedPeerData {
public let flags: CachedGroupFlags public let flags: CachedGroupFlags
public let hasScheduledMessages: Bool public let hasScheduledMessages: Bool
public let invitedBy: PeerId? public let invitedBy: PeerId?
public let photo: TelegramMediaImage?
public let peerIds: Set<PeerId> public let peerIds: Set<PeerId>
public let messageIds: Set<MessageId> public let messageIds: Set<MessageId>
@ -66,9 +67,10 @@ public final class CachedGroupData: CachedPeerData {
self.flags = CachedGroupFlags() self.flags = CachedGroupFlags()
self.hasScheduledMessages = false self.hasScheduledMessages = false
self.invitedBy = nil self.invitedBy = nil
self.photo = nil
} }
public init(participants: CachedGroupParticipants?, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, about: String?, flags: CachedGroupFlags, hasScheduledMessages: Bool, invitedBy: PeerId?) { public init(participants: CachedGroupParticipants?, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, about: String?, flags: CachedGroupFlags, hasScheduledMessages: Bool, invitedBy: PeerId?, photo: TelegramMediaImage?) {
self.participants = participants self.participants = participants
self.exportedInvitation = exportedInvitation self.exportedInvitation = exportedInvitation
self.botInfos = botInfos self.botInfos = botInfos
@ -78,6 +80,7 @@ public final class CachedGroupData: CachedPeerData {
self.flags = flags self.flags = flags
self.hasScheduledMessages = hasScheduledMessages self.hasScheduledMessages = hasScheduledMessages
self.invitedBy = invitedBy self.invitedBy = invitedBy
self.photo = photo
var messageIds = Set<MessageId>() var messageIds = Set<MessageId>()
if let pinnedMessageId = self.pinnedMessageId { if let pinnedMessageId = self.pinnedMessageId {
@ -123,6 +126,12 @@ public final class CachedGroupData: CachedPeerData {
self.invitedBy = decoder.decodeOptionalInt64ForKey("invBy").flatMap(PeerId.init) self.invitedBy = decoder.decodeOptionalInt64ForKey("invBy").flatMap(PeerId.init)
if let photo = decoder.decodeObjectForKey("ph", decoder: { TelegramMediaImage(decoder: $0) }) as? TelegramMediaImage {
self.photo = photo
} else {
self.photo = nil
}
var messageIds = Set<MessageId>() var messageIds = Set<MessageId>()
if let pinnedMessageId = self.pinnedMessageId { if let pinnedMessageId = self.pinnedMessageId {
messageIds.insert(pinnedMessageId) messageIds.insert(pinnedMessageId)
@ -181,6 +190,12 @@ public final class CachedGroupData: CachedPeerData {
} else { } else {
encoder.encodeNil(forKey: "invBy") encoder.encodeNil(forKey: "invBy")
} }
if let photo = self.photo {
encoder.encodeObject(photo, forKey: "ph")
} else {
encoder.encodeNil(forKey: "ph")
}
} }
public func isEqual(to: CachedPeerData) -> Bool { public func isEqual(to: CachedPeerData) -> Bool {
@ -192,38 +207,42 @@ public final class CachedGroupData: CachedPeerData {
} }
public func withUpdatedParticipants(_ participants: CachedGroupParticipants?) -> CachedGroupData { public func withUpdatedParticipants(_ participants: CachedGroupParticipants?) -> CachedGroupData {
return CachedGroupData(participants: participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy) return CachedGroupData(participants: participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
} }
public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedGroupData { public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy) return CachedGroupData(participants: self.participants, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
} }
public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedGroupData { public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
} }
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedGroupData { public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
} }
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedGroupData { public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
} }
public func withUpdatedAbout(_ about: String?) -> CachedGroupData { public func withUpdatedAbout(_ about: String?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
} }
public func withUpdatedFlags(_ flags: CachedGroupFlags) -> CachedGroupData { public func withUpdatedFlags(_ flags: CachedGroupFlags) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
} }
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedGroupData { public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: hasScheduledMessages, invitedBy: self.invitedBy) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
} }
public func withUpdatedInvitedBy(_ invitedBy: PeerId?) -> CachedGroupData { public func withUpdatedInvitedBy(_ invitedBy: PeerId?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: invitedBy) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: invitedBy, photo: self.photo)
}
public func withUpdatedPhoto(_ photo: TelegramMediaImage?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: photo)
} }
} }

View File

@ -250,6 +250,8 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
} }
} }
let photo: TelegramMediaImage? = chatFull.chatPhoto.flatMap(telegramMediaImageFromApiPhoto)
let exportedInvitation = ExportedInvitation(apiExportedInvite: chatFull.exportedInvite) let exportedInvitation = ExportedInvitation(apiExportedInvite: chatFull.exportedInvite)
let pinnedMessageId = chatFull.pinnedMsgId.flatMap({ MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }) let pinnedMessageId = chatFull.pinnedMsgId.flatMap({ MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) })
@ -300,6 +302,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
.withUpdatedFlags(flags) .withUpdatedFlags(flags)
.withUpdatedHasScheduledMessages(hasScheduledMessages) .withUpdatedHasScheduledMessages(hasScheduledMessages)
.withUpdatedInvitedBy(invitedBy) .withUpdatedInvitedBy(invitedBy)
.withUpdatedPhoto(photo)
}) })
case .channelFull: case .channelFull:
break break

View File

@ -158,12 +158,12 @@ final class PeerInfoHeaderNavigationTransition {
} }
enum PeerInfoAvatarListItem: Equatable { enum PeerInfoAvatarListItem: Equatable {
case topImage([ImageRepresentationWithReference], Data?) case topImage([ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], Data?)
case image(TelegramMediaImageReference?, [ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], Data?) case image(TelegramMediaImageReference?, [ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], Data?)
var id: WrappedMediaResourceId { var id: WrappedMediaResourceId {
switch self { switch self {
case let .topImage(representations, _): case let .topImage(representations, _, _):
let representation = largestImageRepresentation(representations.map { $0.representation }) ?? representations[representations.count - 1].representation let representation = largestImageRepresentation(representations.map { $0.representation }) ?? representations[representations.count - 1].representation
return WrappedMediaResourceId(representation.resource.id) return WrappedMediaResourceId(representation.resource.id)
case let .image(_, representations, _, _): case let .image(_, representations, _, _):
@ -171,6 +171,15 @@ enum PeerInfoAvatarListItem: Equatable {
return WrappedMediaResourceId(representation.resource.id) return WrappedMediaResourceId(representation.resource.id)
} }
} }
var videoRepresentations: [TelegramMediaImage.VideoRepresentation] {
switch self {
case let .topImage(_, videoRepresentations, _):
return videoRepresentations
case let .image(_, _, videoRepresentations, _):
return videoRepresentations
}
}
} }
final class PeerInfoAvatarListItemNode: ASDisplayNode { final class PeerInfoAvatarListItemNode: ASDisplayNode {
@ -250,15 +259,15 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
let immediateThumbnailData: Data? let immediateThumbnailData: Data?
var id: Int64? var id: Int64?
switch item { switch item {
case let .topImage(topRepresentations, immediateThumbnail): case let .topImage(topRepresentations, videoRepresentationsValue, immediateThumbnail):
representations = topRepresentations representations = topRepresentations
videoRepresentations = [] videoRepresentations = videoRepresentationsValue
immediateThumbnailData = immediateThumbnail immediateThumbnailData = immediateThumbnail
id = 1
case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail): case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail):
representations = imageRepresentations representations = imageRepresentations
videoRepresentations = videoRepresentationsValue videoRepresentations = videoRepresentationsValue
immediateThumbnailData = immediateThumbnail immediateThumbnailData = immediateThumbnail
if case let .cloud(imageId, _, _) = reference { if case let .cloud(imageId, _, _) = reference {
id = imageId id = imageId
} }
@ -761,9 +770,9 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
var entries: [AvatarGalleryEntry] = [] var entries: [AvatarGalleryEntry] = []
for entry in self.galleryEntries { for entry in self.galleryEntries {
switch entry { switch entry {
case let .topImage(representations, _, immediateThumbnailData, _): case let .topImage(representations, videoRepresentations, _, immediateThumbnailData, _):
entries.append(entry) entries.append(entry)
items.append(.topImage(representations, immediateThumbnailData)) items.append(.topImage(representations, videoRepresentations, immediateThumbnailData))
case let .image(id, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _): case let .image(id, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _):
if image.0 == reference { if image.0 == reference {
entries.insert(entry, at: 0) entries.insert(entry, at: 0)
@ -797,9 +806,9 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
let previousIndex = self.currentIndex let previousIndex = self.currentIndex
for entry in self.galleryEntries { for entry in self.galleryEntries {
switch entry { switch entry {
case let .topImage(representations, _, immediateThumbnailData, _): case let .topImage(representations, videoRepresentations, _, immediateThumbnailData, _):
entries.append(entry) entries.append(entry)
items.append(.topImage(representations, immediateThumbnailData)) items.append(.topImage(representations, videoRepresentations, immediateThumbnailData))
case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _): case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _):
if image.0 != reference { if image.0 != reference {
entries.append(entry) entries.append(entry)
@ -861,8 +870,8 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
var items: [PeerInfoAvatarListItem] = [] var items: [PeerInfoAvatarListItem] = []
for entry in entries { for entry in entries {
switch entry { switch entry {
case let .topImage(representations, _, immediateThumbnailData, _): case let .topImage(representations, videoRepresentations, _, immediateThumbnailData, _):
items.append(.topImage(representations, immediateThumbnailData)) items.append(.topImage(representations, videoRepresentations, immediateThumbnailData))
case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _): case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _):
items.append(.image(reference, representations, videoRepresentations, immediateThumbnailData)) items.append(.image(reference, representations, videoRepresentations, immediateThumbnailData))
} }
@ -1112,53 +1121,78 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
self.avatarNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize)) self.avatarNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize))
self.avatarNode.font = avatarPlaceholderFont(size: floor(avatarSize * 16.0 / 37.0)) self.avatarNode.font = avatarPlaceholderFont(size: floor(avatarSize * 16.0 / 37.0))
if let item = item, case let .image(reference, representations, videoRepresentations, immediateThumbnailData) = item, let video = videoRepresentations.last, case let .cloud(imageId, _, _) = reference { if let item = item {
let id = imageId let representations: [ImageRepresentationWithReference]
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 videoRepresentations: [TelegramMediaImage.VideoRepresentation]
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) let immediateThumbnailData: Data?
if videoContent.id != self.videoContent?.id { var id: Int64?
let mediaManager = self.context.sharedContext.mediaManager switch item {
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded) case let .topImage(topRepresentations, videoRepresentationsValue, immediateThumbnail):
videoNode.isUserInteractionEnabled = false representations = topRepresentations
videoNode.isHidden = true videoRepresentations = videoRepresentationsValue
immediateThumbnailData = immediateThumbnail
if let startTimestamp = video.startTimestamp { id = 1
self.videoStartTimestamp = startTimestamp case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail):
self.playbackStatusDisposable.set((videoNode.status representations = imageRepresentations
|> map { status -> Bool in videoRepresentations = videoRepresentationsValue
if let status = status, case .playing = status.status { immediateThumbnailData = immediateThumbnail
return true if case let .cloud(imageId, _, _) = reference {
} else { id = imageId
return false }
} }
}
|> filter { playing in if let video = videoRepresentations.last, let id = id {
return playing 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)
|> take(1) if videoContent.id != self.videoContent?.id {
|> deliverOnMainQueue).start(completed: { [weak self] in let mediaManager = self.context.sharedContext.mediaManager
if let strongSelf = self { let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded)
Queue.mainQueue().after(0.15) { videoNode.isUserInteractionEnabled = false
strongSelf.videoNode?.isHidden = false videoNode.isHidden = true
if let startTimestamp = video.startTimestamp {
self.videoStartTimestamp = startTimestamp
self.playbackStatusDisposable.set((videoNode.status
|> map { status -> Bool in
if let status = status, case .playing = status.status {
return true
} else {
return false
} }
} }
})) |> filter { playing in
} else { return playing
self.videoStartTimestamp = nil }
self.playbackStatusDisposable.set(nil) |> take(1)
videoNode.isHidden = false |> deliverOnMainQueue).start(completed: { [weak self] in
if let strongSelf = self {
Queue.mainQueue().after(0.15) {
strongSelf.videoNode?.isHidden = false
}
}
}))
} else {
self.videoStartTimestamp = nil
self.playbackStatusDisposable.set(nil)
videoNode.isHidden = false
}
self.videoContent = videoContent
self.videoNode = videoNode
let maskPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size))
let shape = CAShapeLayer()
shape.path = maskPath.cgPath
videoNode.layer.mask = shape
self.addSubnode(videoNode)
} }
} else if let videoNode = self.videoNode {
self.videoContent = nil
self.videoNode = nil
self.videoContent = videoContent videoNode.removeFromSupernode()
self.videoNode = videoNode
let maskPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size))
let shape = CAShapeLayer()
shape.path = maskPath.cgPath
videoNode.layer.mask = shape
self.addSubnode(videoNode)
} }
} else if let videoNode = self.videoNode { } else if let videoNode = self.videoNode {
self.videoContent = nil self.videoContent = nil
@ -1283,7 +1317,7 @@ final class PeerInfoEditingAvatarOverlayNode: ASDisplayNode {
iconHidden = true iconHidden = true
overlayHidden = true overlayHidden = true
} }
Queue.mainQueue().after(0.15) { [weak self] in Queue.mainQueue().after(0.3) { [weak self] in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
@ -1351,28 +1385,53 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
self.avatarNode.setPeer(context: self.context, theme: theme, peer: peer, overrideImage: overrideImage, synchronousLoad: false, displayDimensions: CGSize(width: avatarSize, height: avatarSize)) self.avatarNode.setPeer(context: self.context, theme: theme, peer: peer, overrideImage: overrideImage, synchronousLoad: false, displayDimensions: CGSize(width: avatarSize, height: avatarSize))
self.avatarNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize)) self.avatarNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize))
if let item = item, case let .image(reference, representations, videoRepresentations, immediateThumbnailData) = item, let video = videoRepresentations.last, case let .cloud(imageId, _, _) = reference { if let item = item {
let id = imageId let representations: [ImageRepresentationWithReference]
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 videoRepresentations: [TelegramMediaImage.VideoRepresentation]
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) let immediateThumbnailData: Data?
if videoContent.id != self.videoContent?.id { var id: Int64?
let mediaManager = self.context.sharedContext.mediaManager switch item {
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .overlay) case let .topImage(topRepresentations, videoRepresentationsValue, immediateThumbnail):
videoNode.isUserInteractionEnabled = false representations = topRepresentations
videoNode.ownsContentNodeUpdated = { [weak self] owns in videoRepresentations = videoRepresentationsValue
if let strongSelf = self { immediateThumbnailData = immediateThumbnail
strongSelf.videoNode?.isHidden = !owns id = 1
} case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail):
representations = imageRepresentations
videoRepresentations = videoRepresentationsValue
immediateThumbnailData = immediateThumbnail
if case let .cloud(imageId, _, _) = reference {
id = imageId
} }
self.videoContent = videoContent }
self.videoNode = videoNode
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 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
videoNode.ownsContentNodeUpdated = { [weak self] owns in
if let strongSelf = self {
strongSelf.videoNode?.isHidden = !owns
}
}
self.videoContent = videoContent
self.videoNode = videoNode
let maskPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size))
let shape = CAShapeLayer()
shape.path = maskPath.cgPath
videoNode.layer.mask = shape
self.insertSubnode(videoNode, aboveSubnode: self.avatarNode)
}
} else if let videoNode = self.videoNode {
self.videoContent = nil
self.videoNode = nil
let maskPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size)) videoNode.removeFromSupernode()
let shape = CAShapeLayer()
shape.path = maskPath.cgPath
videoNode.layer.mask = shape
self.insertSubnode(videoNode, aboveSubnode: self.avatarNode)
} }
} else if let videoNode = self.videoNode { } else if let videoNode = self.videoNode {
self.videoContent = nil self.videoContent = nil

View File

@ -2086,7 +2086,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
} }
let entriesPromise = Promise<[AvatarGalleryEntry]>(entries) let entriesPromise = Promise<[AvatarGalleryEntry]>(entries)
let galleryController = AvatarGalleryController(context: strongSelf.context, peer: peer, sourceHasRoundCorners: !strongSelf.headerNode.isAvatarExpanded, remoteEntries: entriesPromise, centralEntryIndex: centralEntry.flatMap { entries.firstIndex(of: $0) }, replaceRootController: { controller, ready in let galleryController = AvatarGalleryController(context: strongSelf.context, peer: peer, sourceHasRoundCorners: !strongSelf.headerNode.isAvatarExpanded, remoteEntries: entriesPromise, skipInitial: true, centralEntryIndex: centralEntry.flatMap { entries.firstIndex(of: $0) }, replaceRootController: { controller, ready in
}) })
galleryController.openAvatarSetup = { [weak self] completion in galleryController.openAvatarSetup = { [weak self] completion in
self?.openAvatarForEditing(hasRemove: false, completion: completion) self?.openAvatarForEditing(hasRemove: false, completion: completion)

View File

@ -43,8 +43,12 @@ final class GenericEmbedImplementation: WebEmbedImplementation {
self.onPlaybackStarted = onPlaybackStarted self.onPlaybackStarted = onPlaybackStarted
updateStatus(self.status) updateStatus(self.status)
let html = String(format: htmlTemplate, self.url) if self.url.contains("player.twitch.tv/"), let url = URL(string: self.url) {
webView.loadHTMLString(html, baseURL: URL(string: "about:blank")) webView.load(URLRequest(url: url))
} else {
let html = String(format: htmlTemplate, self.url)
webView.loadHTMLString(html, baseURL: URL(string: "about:blank"))
}
userContentController.addUserScript(WKUserScript(source: userScript, injectionTime: .atDocumentEnd, forMainFrameOnly: false)) userContentController.addUserScript(WKUserScript(source: userScript, injectionTime: .atDocumentEnd, forMainFrameOnly: false))