Merge commit 'b51dd938f44a0e348bbed299b07e421a1f519378'

This commit is contained in:
Ali 2020-07-10 19:57:49 +04:00
commit 06b52aa249
31 changed files with 768 additions and 278 deletions

View File

@ -5688,8 +5688,8 @@ Any member of this group will be able to see messages in the channel.";
"Group.MessageVideoUpdated" = "Group video updated";
"Channel.MessageVideoUpdated" = "Channel video updated";
"Conversation.Dice.u1F3C0" = "Send a basketball emoji to shoot a free throw.";
"Conversation.Dice.u26BD" = "Send a football emoji to do a free kick.";
"Conversation.Dice.u1F3C0" = "Send a basketball emoji to try your luck.";
"Conversation.Dice.u26BD" = "Send a football emoji to try your luck.";
"SettingsSearch_Synonyms_ChatFolders" = "";

View File

@ -113,7 +113,7 @@ private func mappedInsertEntries(context: AccountContext, presentationData: Item
return entries.map { entry -> ListViewInsertItem in
switch entry.entry {
case let .displayTab(theme, text, value):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListSwitchItem(presentationData: presentationData, title: text, value: value, enabled: true, sectionId: 0, style: .blocks, updated: { value in
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListSwitchItem(presentationData: presentationData, title: text, value: value, enabled: true, noCorners: true, sectionId: 0, style: .blocks, updated: { value in
nodeInteraction.updateShowCallsTab(value)
}), directionHint: entry.directionHint)
case let .displayTabInfo(theme, text):
@ -130,7 +130,7 @@ private func mappedUpdateEntries(context: AccountContext, presentationData: Item
return entries.map { entry -> ListViewUpdateItem in
switch entry.entry {
case let .displayTab(theme, text, value):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListSwitchItem(presentationData: presentationData, title: text, value: value, enabled: true, sectionId: 0, style: .blocks, updated: { value in
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListSwitchItem(presentationData: presentationData, title: text, value: value, enabled: true, noCorners: true, sectionId: 0, style: .blocks, updated: { value in
nodeInteraction.updateShowCallsTab(value)
}), directionHint: entry.directionHint)
case let .displayTabInfo(theme, text):

View File

@ -928,8 +928,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
let transformedCopyViewFinalFrame = videoNode.view.convert(videoNode.view.bounds, to: self.view)
let (maybeSurfaceCopyView, _) = node.2()
let (maybeCopyView, copyViewBackgrond) = node.2()
copyViewBackgrond?.alpha = 0.0
let (maybeCopyView, copyViewBackground) = node.2()
copyViewBackground?.alpha = 0.0
let surfaceCopyView = maybeSurfaceCopyView!
let copyView = maybeCopyView!
@ -1032,8 +1032,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
var copyCompleted = false
let (maybeSurfaceCopyView, _) = node.2()
let (maybeCopyView, copyViewBackgrond) = node.2()
copyViewBackgrond?.alpha = 0.0
let (maybeCopyView, copyViewBackground) = node.2()
copyViewBackground?.alpha = 0.0
let surfaceCopyView = maybeSurfaceCopyView!
let copyView = maybeCopyView!

View File

@ -563,8 +563,8 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo
if strongSelf.updatingAvatarOverlay.supernode == nil {
strongSelf.insertSubnode(strongSelf.updatingAvatarOverlay, aboveSubnode: strongSelf.avatarNode)
}
if let updatingImage = item.updatingImage, case .image(_, true) = updatingImage {
strongSelf.activityIndicator.isHidden = false
if let updatingImage = item.updatingImage, case let .image(_, loading) = updatingImage {
strongSelf.activityIndicator.isHidden = !loading
}
} else if strongSelf.updatingAvatarOverlay.supernode != nil {
if animated {

View File

@ -948,7 +948,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
}
let hasCorners = itemListHasRoundedBlockLayout(params) || !item.noInsets
let hasCorners = itemListHasRoundedBlockLayout(params) && !item.noInsets
var hasTopCorners = false
var hasBottomCorners = false
switch neighbors.top {

View File

@ -20,13 +20,14 @@ public class ItemListSwitchItem: ListViewItem, ItemListItem {
let enabled: Bool
let disableLeadingInset: Bool
let maximumNumberOfLines: Int
let noCorners: Bool
public let sectionId: ItemListSectionId
let style: ItemListStyle
let updated: (Bool) -> Void
let activatedWhileDisabled: () -> Void
public let tag: ItemListItemTag?
public init(presentationData: ItemListPresentationData, title: String, value: Bool, type: ItemListSwitchItemNodeType = .regular, enableInteractiveChanges: Bool = true, enabled: Bool = true, disableLeadingInset: Bool = false, maximumNumberOfLines: Int = 1, sectionId: ItemListSectionId, style: ItemListStyle, updated: @escaping (Bool) -> Void, activatedWhileDisabled: @escaping () -> Void = {}, tag: ItemListItemTag? = nil) {
public init(presentationData: ItemListPresentationData, title: String, value: Bool, type: ItemListSwitchItemNodeType = .regular, enableInteractiveChanges: Bool = true, enabled: Bool = true, disableLeadingInset: Bool = false, maximumNumberOfLines: Int = 1, noCorners: Bool = false, sectionId: ItemListSectionId, style: ItemListStyle, updated: @escaping (Bool) -> Void, activatedWhileDisabled: @escaping () -> Void = {}, tag: ItemListItemTag? = nil) {
self.presentationData = presentationData
self.title = title
self.value = value
@ -35,6 +36,7 @@ public class ItemListSwitchItem: ListViewItem, ItemListItem {
self.enabled = enabled
self.disableLeadingInset = disableLeadingInset
self.maximumNumberOfLines = maximumNumberOfLines
self.noCorners = noCorners
self.sectionId = sectionId
self.style = style
self.updated = updated
@ -329,7 +331,7 @@ public class ItemListSwitchItemNode: ListViewItemNode, ItemListItemNode {
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
}
let hasCorners = itemListHasRoundedBlockLayout(params)
let hasCorners = itemListHasRoundedBlockLayout(params) && !item.noCorners
var hasTopCorners = false
var hasBottomCorners = false
switch neighbors.top {

View File

@ -1716,7 +1716,7 @@
CGFloat duration = self.view.frame.size.height / velocity;
CGRect targetFrame = CGRectOffset(self.view.frame, 0, self.view.frame.size.height);
[UIView animateWithDuration:duration animations:^
[UIView animateWithDuration:duration delay:0.4 options:kNilOptions animations:^
{
self.view.frame = targetFrame;
} completion:^(__unused BOOL finished)

View File

@ -23,12 +23,12 @@ public enum AvatarGalleryEntryId: Hashable {
}
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?)
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)
}
@ -43,7 +43,7 @@ public enum AvatarGalleryEntry: Equatable {
public var representations: [ImageRepresentationWithReference] {
switch self {
case let .topImage(representations, _, _, _):
case let .topImage(representations, _, _, _, _):
return representations
case let .image(_, _, representations, _, _, _, _, _, _, _):
return representations
@ -52,8 +52,8 @@ public enum AvatarGalleryEntry: Equatable {
public var videoRepresentations: [TelegramMediaImage.VideoRepresentation] {
switch self {
case .topImage:
return []
case let .topImage(_, videoRepresentations, _, _, _):
return videoRepresentations
case let .image(_, _, _, videoRepresentations, _, _, _, _, _, _):
return videoRepresentations
}
@ -61,7 +61,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
@ -70,8 +70,8 @@ public enum AvatarGalleryEntry: Equatable {
public static func ==(lhs: AvatarGalleryEntry, rhs: AvatarGalleryEntry) -> Bool {
switch lhs {
case let .topImage(lhsRepresentations, lhsIndexData, lhsImmediateThumbnailData, lhsCategory):
if case let .topImage(rhsRepresentations, rhsIndexData, rhsImmediateThumbnailData, rhsCategory) = rhs, lhsRepresentations == rhsRepresentations, lhsIndexData == rhsIndexData, lhsImmediateThumbnailData == rhsImmediateThumbnailData, lhsCategory == rhsCategory {
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 {
return true
} else {
return false
@ -102,8 +102,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, _, immediateThumbnailData, category) = entry {
updatedEntries.append(.topImage(representations, indexData, immediateThumbnailData, category))
if case let .topImage(representations, videoRepresentations, _, immediateThumbnailData, category) = entry {
updatedEntries.append(.topImage(representations, videoRepresentations, 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))
}
@ -112,39 +112,62 @@ public func normalizeEntries(_ entries: [AvatarGalleryEntry]) -> [AvatarGalleryE
return updatedEntries
}
public func initialAvatarGalleryEntries(peer: Peer) -> [AvatarGalleryEntry] {
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)) }), [], nil, nil, nil))
}
if peer is TelegramChannel || peer is TelegramGroup {
return account.postbox.transaction { transaction in
return transaction.getPeerCachedData(peerId: peer.id)
} |> map { cachedData in
var initialPhoto: TelegramMediaImage?
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 {
return initialEntries
}
}
} else {
return .single(initialEntries)
}
return initialEntries
}
public func fetchedAvatarGalleryEntries(account: Account, peer: Peer) -> Signal<[AvatarGalleryEntry], NoError> {
let initialEntries = initialAvatarGalleryEntries(peer: peer)
return Signal<[AvatarGalleryEntry], NoError>.single(initialEntries)
|> then(
requestPeerPhotos(account: account, peerId: peer.id)
|> map { photos -> [AvatarGalleryEntry] in
var result: [AvatarGalleryEntry] = []
let initialEntries = initialAvatarGalleryEntries(peer: peer)
if photos.isEmpty {
result = initialEntries
} else {
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))
} 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))
return initialAvatarGalleryEntries(account: account, peer: peer)
|> mapToSignal { initialEntries in
return .single(initialEntries)
|> then(
requestPeerPhotos(account: account, peerId: peer.id)
|> map { photos -> [AvatarGalleryEntry] in
var result: [AvatarGalleryEntry] = []
if photos.isEmpty {
result = initialEntries
} else {
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))
} 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))
}
index += 1
}
index += 1
}
return result
}
return result
}
)
)
}
}
public func fetchedAvatarGalleryEntries(account: Account, peer: Peer, firstEntry: AvatarGalleryEntry) -> Signal<[AvatarGalleryEntry], NoError> {
@ -219,7 +242,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
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.peer = peer
self.sourceHasRoundCorners = sourceHasRoundCorners
@ -242,7 +265,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
remoteEntriesSignal = fetchedAvatarGalleryEntries(account: context.account, peer: peer)
}
let entriesSignal: Signal<[AvatarGalleryEntry], NoError> = .single(initialAvatarGalleryEntries(peer: peer)) |> then(remoteEntriesSignal)
let entriesSignal: Signal<[AvatarGalleryEntry], NoError> = skipInitial ? remoteEntriesSignal : (initialAvatarGalleryEntries(account: context.account, peer: peer) |> then(remoteEntriesSignal))
let presentationData = self.presentationData

View File

@ -104,7 +104,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
@ -256,6 +256,8 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
if case let .image(image) = entry {
id = image.0.id
category = image.9
} else {
id = 1
}
if let video = entry.videoRepresentations.last, let id = id {
if video != previousVideoRepresentations?.last {
@ -449,7 +451,10 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
var copyCompleted = false
var surfaceCopyCompleted = false
let copyView = node.2().0!
let (maybeCopyView, copyViewBackground) = node.2()
copyViewBackground?.alpha = 1.0
let copyView = maybeCopyView!
if self.sourceHasRoundCorners {
self.view.insertSubview(copyView, belowSubview: self.scrollNode.view)
@ -548,7 +553,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

View File

@ -197,13 +197,13 @@ private func profileSearchableItems(context: AccountContext, canAddAccount: Bool
}
var items: [SettingsSearchableItem] = []
items.append(SettingsSearchableItem(id: .profile(0), title: strings.EditProfile_Title, alternate: synonyms(strings.SettingsSearch_Synonyms_EditProfile_Title), icon: icon, breadcrumbs: [], present: { context, _, present in
presentProfileSettings(context, present, nil)
}))
items.append(SettingsSearchableItem(id: .profile(1), title: strings.UserInfo_About_Placeholder, alternate: synonyms(strings.SettingsSearch_Synonyms_EditProfile_Title), icon: icon, breadcrumbs: [strings.EditProfile_Title], present: { context, _, present in
presentProfileSettings(context, present, .bio)
}))
// items.append(SettingsSearchableItem(id: .profile(0), title: strings.EditProfile_Title, alternate: synonyms(strings.SettingsSearch_Synonyms_EditProfile_Title), icon: icon, breadcrumbs: [], present: { context, _, present in
// presentProfileSettings(context, present, nil)
// }))
//
// items.append(SettingsSearchableItem(id: .profile(1), title: strings.UserInfo_About_Placeholder, alternate: synonyms(strings.SettingsSearch_Synonyms_EditProfile_Title), icon: icon, breadcrumbs: [strings.EditProfile_Title], present: { context, _, present in
// presentProfileSettings(context, present, .bio)
// }))
items.append(SettingsSearchableItem(id: .profile(2), title: strings.Settings_PhoneNumber, alternate: synonyms(strings.SettingsSearch_Synonyms_EditProfile_PhoneNumber), icon: icon, breadcrumbs: [strings.EditProfile_Title], present: { context, _, present in
let _ = (context.account.postbox.transaction { transaction -> String in
return (transaction.getPeer(context.account.peerId) as? TelegramUser)?.phone ?? ""

View File

@ -168,6 +168,7 @@ public final class CachedChannelData: CachedPeerData {
public let hasScheduledMessages: Bool
public let statsDatacenterId: Int32
public let invitedBy: PeerId?
public let photo: TelegramMediaImage?
public let peerIds: Set<PeerId>
public let messageIds: Set<MessageId>
@ -196,9 +197,10 @@ public final class CachedChannelData: CachedPeerData {
self.hasScheduledMessages = false
self.statsDatacenterId = 0
self.invitedBy = nil
self.photo = nil
}
public init(isNotAccessible: Bool, flags: CachedChannelFlags, about: String?, participantsSummary: CachedChannelParticipantsSummary, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, stickerPack: StickerPackCollectionInfo?, minAvailableMessageId: MessageId?, migrationReference: ChannelMigrationReference?, linkedDiscussionPeerId: PeerId?, peerGeoLocation: PeerGeoLocation?, slowModeTimeout: Int32?, slowModeValidUntilTimestamp: Int32?, hasScheduledMessages: Bool, statsDatacenterId: Int32, invitedBy: PeerId?) {
public init(isNotAccessible: Bool, flags: CachedChannelFlags, about: String?, participantsSummary: CachedChannelParticipantsSummary, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, stickerPack: StickerPackCollectionInfo?, minAvailableMessageId: MessageId?, migrationReference: ChannelMigrationReference?, linkedDiscussionPeerId: PeerId?, peerGeoLocation: PeerGeoLocation?, slowModeTimeout: Int32?, slowModeValidUntilTimestamp: Int32?, hasScheduledMessages: Bool, statsDatacenterId: Int32, invitedBy: PeerId?, photo: TelegramMediaImage?) {
self.isNotAccessible = isNotAccessible
self.flags = flags
self.about = about
@ -217,6 +219,7 @@ public final class CachedChannelData: CachedPeerData {
self.hasScheduledMessages = hasScheduledMessages
self.statsDatacenterId = statsDatacenterId
self.invitedBy = invitedBy
self.photo = photo
var peerIds = Set<PeerId>()
for botInfo in botInfos {
@ -242,75 +245,79 @@ public final class CachedChannelData: CachedPeerData {
}
public func withUpdatedIsNotAccessible(_ isNotAccessible: Bool) -> CachedChannelData {
return CachedChannelData(isNotAccessible: isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
return CachedChannelData(isNotAccessible: isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
}
public func withUpdatedFlags(_ flags: CachedChannelFlags) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
}
public func withUpdatedAbout(_ about: String?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
}
public func withUpdatedParticipantsSummary(_ participantsSummary: CachedChannelParticipantsSummary) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
}
public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
}
public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
}
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
}
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
}
public func withUpdatedStickerPack(_ stickerPack: StickerPackCollectionInfo?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
}
public func withUpdatedMinAvailableMessageId(_ minAvailableMessageId: MessageId?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
}
public func withUpdatedMigrationReference(_ migrationReference: ChannelMigrationReference?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
}
public func withUpdatedLinkedDiscussionPeerId(_ linkedDiscussionPeerId: PeerId?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
}
public func withUpdatedPeerGeoLocation(_ peerGeoLocation: PeerGeoLocation?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
}
public func withUpdatedSlowModeTimeout(_ slowModeTimeout: Int32?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
}
public func withUpdatedSlowModeValidUntilTimestamp(_ slowModeValidUntilTimestamp: Int32?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
}
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
}
public func withUpdatedStatsDatacenterId(_ statsDatacenterId: Int32) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: statsDatacenterId, invitedBy: self.invitedBy)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
}
public func withUpdatedInvitedBy(_ invitedBy: PeerId?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: invitedBy)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: invitedBy, photo: self.photo)
}
public func withUpdatedPhoto(_ photo: TelegramMediaImage?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: photo)
}
public init(decoder: PostboxDecoder) {
@ -372,6 +379,12 @@ public final class CachedChannelData: CachedPeerData {
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
}
if let linkedDiscussionPeerId = self.linkedDiscussionPeerId {
peerIds.insert(linkedDiscussionPeerId)
}
@ -462,6 +475,12 @@ public final class CachedChannelData: CachedPeerData {
} else {
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 {
@ -541,6 +560,10 @@ public final class CachedChannelData: CachedPeerData {
return false
}
if other.photo != self.photo {
return false
}
return true
}
}

View File

@ -49,6 +49,7 @@ public final class CachedGroupData: CachedPeerData {
public let flags: CachedGroupFlags
public let hasScheduledMessages: Bool
public let invitedBy: PeerId?
public let photo: TelegramMediaImage?
public let peerIds: Set<PeerId>
public let messageIds: Set<MessageId>
@ -66,9 +67,10 @@ public final class CachedGroupData: CachedPeerData {
self.flags = CachedGroupFlags()
self.hasScheduledMessages = false
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.exportedInvitation = exportedInvitation
self.botInfos = botInfos
@ -78,6 +80,7 @@ public final class CachedGroupData: CachedPeerData {
self.flags = flags
self.hasScheduledMessages = hasScheduledMessages
self.invitedBy = invitedBy
self.photo = photo
var messageIds = Set<MessageId>()
if let pinnedMessageId = self.pinnedMessageId {
@ -123,6 +126,12 @@ public final class CachedGroupData: CachedPeerData {
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>()
if let pinnedMessageId = self.pinnedMessageId {
messageIds.insert(pinnedMessageId)
@ -181,6 +190,12 @@ public final class CachedGroupData: CachedPeerData {
} else {
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 {
@ -192,38 +207,42 @@ public final class CachedGroupData: CachedPeerData {
}
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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

@ -36,7 +36,7 @@ public enum AdminLogEventAction {
case changeTitle(prev: String, new: String)
case changeAbout(prev: String, new: String)
case changeUsername(prev: String, new: String)
case changePhoto(prev: [TelegramMediaImageRepresentation], new: [TelegramMediaImageRepresentation])
case changePhoto(prev: ([TelegramMediaImageRepresentation], [TelegramMediaImage.VideoRepresentation]), new: ([TelegramMediaImageRepresentation], [TelegramMediaImage.VideoRepresentation]))
case toggleInvites(Bool)
case toggleSignatures(Bool)
case updatePinned(Message?)
@ -149,7 +149,9 @@ public func channelAdminLogEvents(postbox: Postbox, network: Network, peerId: Pe
case let .channelAdminLogEventActionChangeUsername(prev, new):
action = .changeUsername(prev: prev, new: new)
case let .channelAdminLogEventActionChangePhoto(prev, new):
action = .changePhoto(prev: telegramMediaImageFromApiPhoto(prev)?.representations ?? [], new: telegramMediaImageFromApiPhoto(new)?.representations ?? [])
let previousImage = telegramMediaImageFromApiPhoto(prev)
let newImage = telegramMediaImageFromApiPhoto(new)
action = .changePhoto(prev: (previousImage?.representations ?? [], previousImage?.videoRepresentations ?? []) , new: (newImage?.representations ?? [], newImage?.videoRepresentations ?? []))
case let .channelAdminLogEventActionToggleInvites(new):
action = .toggleInvites(boolFromApiValue(new))
case let .channelAdminLogEventActionToggleSignatures(new):

View File

@ -22,9 +22,17 @@ public func updateAccountPhoto(account: Account, resource: MediaResource?, video
public struct UploadedPeerPhotoData {
fileprivate let resource: MediaResource
fileprivate let content: UploadedPeerPhotoDataContent
public var isCompleted: Bool {
if case let .result(result) = content, case .inputFile = result {
return true
} else {
return false
}
}
}
private enum UploadedPeerPhotoDataContent {
enum UploadedPeerPhotoDataContent {
case result(MultipartUploadResult)
case error
}

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 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)
.withUpdatedHasScheduledMessages(hasScheduledMessages)
.withUpdatedInvitedBy(invitedBy)
.withUpdatedPhoto(photo)
})
case .channelFull:
break
@ -337,7 +340,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
}
switch fullChat {
case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, _, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, folderId, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, statsDc, pts):
case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, chatPhoto, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, folderId, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, statsDc, pts):
var channelFlags = CachedChannelFlags()
if (flags & (1 << 3)) != 0 {
channelFlags.insert(.canDisplayParticipants)
@ -467,6 +470,8 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
}
}
let photo = telegramMediaImageFromApiPhoto(chatPhoto)
var minAvailableMessageIdUpdated = false
transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in
var previous: CachedChannelData
@ -496,6 +501,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
.withUpdatedHasScheduledMessages(hasScheduledMessages)
.withUpdatedStatsDatacenterId(statsDc ?? 0)
.withUpdatedInvitedBy(invitedBy)
.withUpdatedPhoto(photo)
})
if let minAvailableMessageId = minAvailableMessageId, minAvailableMessageIdUpdated {

View File

@ -83,7 +83,7 @@ final class AuthorizationSequenceSignUpController: ViewController {
let currentAvatarMixin = Atomic<NSObject?>(value: nil)
self.displayNode = AuthorizationSequenceSignUpControllerNode(theme: self.presentationData.theme, strings: self.presentationData.strings, addPhoto: { [weak self] in
presentLegacyAvatarPicker(holder: currentAvatarMixin, signup: true, theme: defaultPresentationTheme, present: { c, a in
presentLegacyAvatarPicker(holder: currentAvatarMixin, signup: false, theme: defaultPresentationTheme, present: { c, a in
self?.view.endEditing(true)
self?.present(c, in: .window(.root), with: a)
}, openCurrent: nil, completion: { image in

View File

@ -132,7 +132,7 @@ final class AuthorizationSequenceSignUpControllerNode: ASDisplayNode, UITextFiel
self.currentPhotoNode.displayWithoutProcessing = true
self.addPhotoButton = HighlightableButtonNode()
self.addPhotoButton.setAttributedTitle(NSAttributedString(string: "\(self.strings.Login_InfoAvatarAdd)\n\(self.strings.Login_InfoAvatarPhoto)", font: Font.regular(16.0), textColor: self.theme.list.itemPlaceholderTextColor, paragraphAlignment: .center), for: .normal)
self.addPhotoButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Avatar/EditAvatarIconLarge"), color: self.theme.list.itemPlaceholderTextColor), for: .normal)
self.addPhotoButton.setBackgroundImage(generateCircleImage(diameter: 110.0, lineWidth: 1.0, color: self.theme.list.itemPlaceholderTextColor), for: .normal)
self.addPhotoButton.addSubnode(self.currentPhotoNode)

View File

@ -119,7 +119,7 @@ public final class ChatControllerInteraction {
let displayPsa: (String, ASDisplayNode) -> Void
let displayDiceTooltip: (TelegramMediaDice) -> Void
let animateDiceSuccess: () -> Void
let greetingStickerNode: () -> (ASDisplayNode, ASDisplayNode, () -> Void)?
let greetingStickerNode: () -> (ASDisplayNode, ASDisplayNode, ASDisplayNode, () -> Void)?
let requestMessageUpdate: (MessageId) -> Void
let cancelInteractiveKeyboardGestures: () -> Void
@ -137,7 +137,7 @@ public final class ChatControllerInteraction {
var searchTextHighightState: (String, [MessageIndex])?
var seenOneTimeAnimatedMedia = Set<MessageId>()
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId, Bool) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> ChatControllerInteractionSwipeAction, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, NSAttributedString, TextSelectionAction) -> Void, updateMessageLike: @escaping (MessageId, Bool) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, displayPollSolution: @escaping (TelegramMediaPollResults.Solution, ASDisplayNode) -> Void, displayPsa: @escaping (String, ASDisplayNode) -> Void, displayDiceTooltip: @escaping (TelegramMediaDice) -> Void, animateDiceSuccess: @escaping () -> Void, greetingStickerNode: @escaping () -> (ASDisplayNode, ASDisplayNode, () -> Void)?, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId, Bool) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> ChatControllerInteractionSwipeAction, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, NSAttributedString, TextSelectionAction) -> Void, updateMessageLike: @escaping (MessageId, Bool) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, displayPollSolution: @escaping (TelegramMediaPollResults.Solution, ASDisplayNode) -> Void, displayPsa: @escaping (String, ASDisplayNode) -> Void, displayDiceTooltip: @escaping (TelegramMediaDice) -> Void, animateDiceSuccess: @escaping () -> Void, greetingStickerNode: @escaping () -> (ASDisplayNode, ASDisplayNode, ASDisplayNode, () -> Void)?, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
self.openMessage = openMessage
self.openPeer = openPeer
self.openPeerMention = openPeerMention

View File

@ -725,10 +725,10 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
}
var greetingStickerNode: (ASDisplayNode, ASDisplayNode, () -> Void)? {
var greetingStickerNode: (ASDisplayNode, ASDisplayNode, ASDisplayNode, () -> Void)? {
if let greetingStickerNode = self.emptyNode?.greetingStickerNode {
self.historyNode.itemHeaderNodesAlpha = 0.0
return (greetingStickerNode, self, { [weak self] in
return (greetingStickerNode, self, self.historyNode, { [weak self] in
self?.historyNode.forEachItemHeaderNode { node in
node.alpha = 1.0
node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)

View File

@ -65,9 +65,28 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
override func transitionNode(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
if let imageNode = self.imageNode, self.item?.message.id == messageId {
return (imageNode, imageNode.bounds, { [weak imageNode] in
let snapshot = imageNode?.view.snapshotContentTree(unhide: true)
return (snapshot, nil)
return (imageNode, imageNode.bounds, { [weak self] in
guard let strongSelf = self, let imageNode = strongSelf.imageNode else {
return (nil, nil)
}
let resultView = imageNode.view.snapshotContentTree(unhide: true)
if let resultView = resultView, strongSelf.mediaBackgroundNode.supernode != nil, let backgroundView = strongSelf.mediaBackgroundNode.view.snapshotContentTree(unhide: true) {
let backgroundContainer = UIView()
backgroundContainer.addSubview(backgroundView)
let backgroundFrame = strongSelf.mediaBackgroundNode.layer.convert(strongSelf.mediaBackgroundNode.bounds, to: resultView.layer)
backgroundContainer.frame = CGRect(origin: CGPoint(x: -2.0, y: -2.0), size: CGSize(width: resultView.frame.width + 4.0, height: resultView.frame.height + 4.0))
backgroundView.frame = backgroundContainer.bounds
let viewWithBackground = UIView()
viewWithBackground.addSubview(backgroundContainer)
viewWithBackground.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: resultView.frame.size)
resultView.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: resultView.frame.size)
viewWithBackground.addSubview(resultView)
return (viewWithBackground, backgroundContainer)
}
return (resultView, nil)
})
} else {
return nil
@ -100,6 +119,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
}
self.imageNode?.isHidden = mediaHidden
self.mediaBackgroundNode.isHidden = mediaHidden
return mediaHidden
}

View File

@ -41,6 +41,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
private var isPlaying = false
private var animateGreeting = false
private weak var greetingStickerParentNode: ASDisplayNode?
private weak var greetingStickerListNode: ASDisplayNode?
private var greetingCompletion: (() -> Void)?
private var swipeToReplyNode: ChatMessageSwipeToReplyNode?
@ -238,11 +239,12 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
self.animationNode = animationNode
} else {
let animationNode: AnimatedStickerNode
if let (node, parentNode, greetingCompletion) = item.controllerInteraction.greetingStickerNode(), let greetingStickerNode = node as? AnimatedStickerNode {
if let (node, parentNode, listNode, greetingCompletion) = item.controllerInteraction.greetingStickerNode(), let greetingStickerNode = node as? AnimatedStickerNode {
animationNode = greetingStickerNode
self.imageNode.alpha = 0.0
self.animateGreeting = true
self.greetingStickerParentNode = parentNode
self.greetingStickerListNode = listNode
self.greetingCompletion = greetingCompletion
self.dateAndStatusNode.alpha = 0.0
} else {
@ -767,26 +769,31 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
let initialFrame = animationNode.view.convert(animationNode.bounds, to: parentNode.view)
parentNode.addSubnode(animationNode)
animationNode.frame = initialFrame
if true {
let targetScale = animationNodeFrame.width / initialFrame.width
animationNode.layer.animateScale(from: 1.0, to: targetScale, duration: 0.3, removeOnCompletion: false)
animationNode.layer.animatePosition(from: initialFrame.center, to: CGPoint(x: animationNodeFrame.midX, y: initialFrame.center.y + 173.0), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { [weak self] finished in
if let strongSelf = self {
animationNode.layer.removeAllAnimations()
strongSelf.animationNode?.frame = animationNodeFrame
strongSelf.contextSourceNode.contentNode.insertSubnode(animationNode, aboveSubnode: strongSelf.imageNode)
if let animationNode = strongSelf.animationNode as? AnimatedStickerNode {
animationNode.updateLayout(size: updatedContentFrame.insetBy(dx: imageInset, dy: imageInset).size)
}
strongSelf.dateAndStatusNode.alpha = 1.0
strongSelf.dateAndStatusNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
strongSelf.greetingCompletion?()
}
})
var targetPosition = initialFrame.center.y
if let listNode = strongSelf.greetingStickerListNode as? ListView {
targetPosition = listNode.frame.height - listNode.insets.top - animationNodeFrame.height / 2.0 - 12.0
}
let targetScale = animationNodeFrame.width / initialFrame.width
animationNode.layer.animateScale(from: 1.0, to: targetScale, duration: 0.3, removeOnCompletion: false)
animationNode.layer.animatePosition(from: initialFrame.center, to: CGPoint(x: animationNodeFrame.midX, y: targetPosition), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { [weak self] finished in
if let strongSelf = self {
animationNode.layer.removeAllAnimations()
strongSelf.animationNode?.frame = animationNodeFrame
strongSelf.contextSourceNode.contentNode.insertSubnode(animationNode, aboveSubnode: strongSelf.imageNode)
if let animationNode = strongSelf.animationNode as? AnimatedStickerNode {
animationNode.updateLayout(size: updatedContentFrame.insetBy(dx: imageInset, dy: imageInset).size)
}
strongSelf.dateAndStatusNode.alpha = 1.0
strongSelf.dateAndStatusNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
strongSelf.greetingCompletion?()
}
})
} else if strongSelf.animationNode?.supernode === strongSelf.contextSourceNode.contentNode {
strongSelf.animationNode?.frame = animationNodeFrame
}

View File

@ -44,6 +44,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
private var controllerInteraction: ChatControllerInteraction!
private let galleryHiddenMesageAndMediaDisposable = MetaDisposable()
private let temporaryHiddenGalleryMediaDisposable = MetaDisposable()
private var chatPresentationDataPromise: Promise<ChatPresentationData>
private var presentationDataDisposable: Disposable?
@ -183,7 +184,27 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
}, callPeer: { peerId, isVideo in
self?.controllerInteraction?.callPeer(peerId, isVideo)
}, enqueueMessage: { _ in
}, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }))
}, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { signal, media in
if let strongSelf = self {
strongSelf.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).start(next: { messageId in
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
var messageIdAndMedia: [MessageId: [Media]] = [:]
if let messageId = messageId {
messageIdAndMedia[messageId] = [media]
}
controllerInteraction.hiddenMedia = messageIdAndMedia
strongSelf.listNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? ChatMessageItemView {
itemNode.updateHiddenMedia()
}
}
}
}))
}
}))
}
return false
}, openPeer: { [weak self] peerId, _, message in
@ -516,6 +537,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
self.historyDisposable?.dispose()
self.navigationActionDisposable.dispose()
self.galleryHiddenMesageAndMediaDisposable.dispose()
self.temporaryHiddenGalleryMediaDisposable.dispose()
self.resolvePeerByNameDisposable.dispose()
self.adminsDisposable?.dispose()
self.banDisposables.dispose()

View File

@ -219,8 +219,9 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
peers[peer.id] = peer
var photo: TelegramMediaImage?
if !new.isEmpty {
photo = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: new, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
let (newPhoto, newVideo) = new
if !newPhoto.isEmpty || !newVideo.isEmpty {
photo = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: newPhoto, videoRepresentations: newVideo, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
}
let action = TelegramMediaActionType.photoUpdated(image: photo)

View File

@ -17,6 +17,7 @@ import ItemListAvatarAndNameInfoItem
import WebSearchUI
import PeerInfoUI
import MapResourceToAvatarSizes
import LegacyMediaPickerUI
private struct CreateChannelArguments {
let context: AccountContext
@ -214,6 +215,7 @@ public func createChannelController(context: AccountContext) -> ViewController {
let currentAvatarMixin = Atomic<TGMediaAvatarMenuMixin?>(value: nil)
let uploadedAvatar = Promise<UploadedPeerPhotoData>()
var uploadedVideoAvatar: (Promise<UploadedPeerPhotoData?>, Double?)? = nil
let arguments = CreateChannelArguments(context: context, updateEditingName: { editingName in
updateState { current in
@ -260,7 +262,7 @@ public func createChannelController(context: AccountContext) -> ViewController {
return $0.avatar
}
if let _ = updatingAvatar {
let _ = updatePeerPhoto(postbox: context.account.postbox, network: context.account.network, stateManager: context.account.stateManager, accountPeerId: context.account.peerId, peerId: peerId, photo: uploadedAvatar.get(), mapResourceToAvatarSizes: { resource, representations in
let _ = updatePeerPhoto(postbox: context.account.postbox, network: context.account.network, stateManager: context.account.stateManager, accountPeerId: context.account.peerId, peerId: peerId, photo: uploadedAvatar.get(), video: uploadedVideoAvatar?.0.get(), videoStartTimestamp: uploadedVideoAvatar?.1, mapResourceToAvatarSizes: { resource, representations in
return mapResourceToAvatarSizes(postbox: context.account.postbox, resource: resource, representations: representations)
}).start()
}
@ -311,12 +313,13 @@ public func createChannelController(context: AccountContext) -> ViewController {
endEditingImpl?()
presentControllerImpl?(legacyController, nil)
let completedImpl: (UIImage) -> Void = { image in
let completedChannelPhotoImpl: (UIImage) -> Void = { image in
if let data = image.jpegData(compressionQuality: 0.6) {
let resource = LocalFileMediaResource(fileId: arc4random64())
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource)
uploadedAvatar.set(uploadedPeerPhoto(postbox: context.account.postbox, network: context.account.network, resource: resource))
uploadedVideoAvatar = nil
updateState { current in
var current = current
current.avatar = .image(representation, false)
@ -325,18 +328,119 @@ public func createChannelController(context: AccountContext) -> ViewController {
}
}
let completedChannelVideoImpl: (UIImage, URL, TGVideoEditAdjustments?) -> Void = { image, url, adjustments in
if let data = image.jpegData(compressionQuality: 0.6) {
let photoResource = LocalFileMediaResource(fileId: arc4random64())
context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource)
updateState { state in
var state = state
state.avatar = .image(representation, true)
return state
}
var videoStartTimestamp: Double? = nil
if let adjustments = adjustments, adjustments.videoStartValue > 0.0 {
videoStartTimestamp = adjustments.videoStartValue - adjustments.trimStartValue
}
let signal = Signal<TelegramMediaResource?, UploadPeerPhotoError> { subscriber in
var filteredPath = url.path
if filteredPath.hasPrefix("file://") {
filteredPath = String(filteredPath[filteredPath.index(filteredPath.startIndex, offsetBy: "file://".count)])
}
let avAsset = AVURLAsset(url: URL(fileURLWithPath: filteredPath))
let entityRenderer: LegacyPaintEntityRenderer? = adjustments.flatMap { adjustments in
if let paintingData = adjustments.paintingData, paintingData.hasAnimation {
return LegacyPaintEntityRenderer(account: context.account, adjustments: adjustments)
} else {
return nil
}
}
let uploadInterface = LegacyLiveUploadInterface(account: context.account)
let signal = TGMediaVideoConverter.convert(avAsset, adjustments: adjustments, watcher: uploadInterface, entityRenderer: entityRenderer)!
let signalDisposable = signal.start(next: { next in
if let result = next as? TGMediaVideoConversionResult {
if let image = result.coverImage, let data = image.jpegData(compressionQuality: 0.7) {
context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
}
if let timestamp = videoStartTimestamp {
videoStartTimestamp = max(0.0, min(timestamp, result.duration))
}
var value = stat()
if stat(result.fileURL.path, &value) == 0 {
if let data = try? Data(contentsOf: result.fileURL) {
let resource: TelegramMediaResource
if let liveUploadData = result.liveUploadData as? LegacyLiveUploadInterfaceResult {
resource = LocalFileMediaResource(fileId: liveUploadData.id)
} else {
resource = LocalFileMediaResource(fileId: arc4random64())
}
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
subscriber.putNext(resource)
}
}
subscriber.putCompletion()
}
}, error: { _ in
}, completed: nil)
let disposable = ActionDisposable {
signalDisposable?.dispose()
}
return ActionDisposable {
disposable.dispose()
}
}
uploadedAvatar.set(uploadedPeerPhoto(postbox: context.account.postbox, network: context.account.network, resource: photoResource))
let promise = Promise<UploadedPeerPhotoData?>()
promise.set(signal
|> `catch` { _ -> Signal<TelegramMediaResource?, NoError> in
return .single(nil)
}
|> mapToSignal { resource -> Signal<UploadedPeerPhotoData?, NoError> in
if let resource = resource {
return uploadedPeerVideo(postbox: context.account.postbox, network: context.account.network, messageMediaPreuploadManager: context.account.messageMediaPreuploadManager, resource: resource) |> map(Optional.init)
} else {
return .single(nil)
}
} |> afterNext { next in
if let next = next, next.isCompleted {
updateState { state in
var state = state
state.avatar = .image(representation, false)
return state
}
}
})
uploadedVideoAvatar = (promise, videoStartTimestamp)
}
}
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: stateValue.with({ $0.avatar }) != nil, hasViewButton: false, personalPhoto: false, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: false)!
let _ = currentAvatarMixin.swap(mixin)
mixin.requestSearchController = { assetsController in
let controller = WebSearchController(context: context, peer: peer, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: title, completion: { result in
assetsController?.dismiss()
completedImpl(result)
completedChannelPhotoImpl(result)
}))
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}
mixin.didFinishWithImage = { image in
if let image = image {
completedImpl(image)
completedChannelPhotoImpl(image)
}
}
mixin.didFinishWithVideo = { image, url, adjustments in
if let image = image, let url = url {
completedChannelVideoImpl(image, url, adjustments)
}
}
if stateValue.with({ $0.avatar }) != nil {

View File

@ -26,6 +26,7 @@ import PeerInfoUI
import MapResourceToAvatarSizes
import ItemListAddressItem
import ItemListVenueItem
import LegacyMediaPickerUI
private struct CreateGroupArguments {
let context: AccountContext
@ -383,6 +384,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
let currentAvatarMixin = Atomic<TGMediaAvatarMenuMixin?>(value: nil)
let uploadedAvatar = Promise<UploadedPeerPhotoData>()
var uploadedVideoAvatar: (Promise<UploadedPeerPhotoData?>, Double?)? = nil
let addressPromise = Promise<String?>(nil)
let venuesPromise = Promise<[TelegramMediaMap]?>(nil)
@ -480,7 +482,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
return $0.avatar
}
if let _ = updatingAvatar {
return updatePeerPhoto(postbox: context.account.postbox, network: context.account.network, stateManager: context.account.stateManager, accountPeerId: context.account.peerId, peerId: peerId, photo: uploadedAvatar.get(), mapResourceToAvatarSizes: { resource, representations in
return updatePeerPhoto(postbox: context.account.postbox, network: context.account.network, stateManager: context.account.stateManager, accountPeerId: context.account.peerId, peerId: peerId, photo: uploadedAvatar.get(), video: uploadedVideoAvatar?.0.get(), videoStartTimestamp: uploadedVideoAvatar?.1, mapResourceToAvatarSizes: { resource, representations in
return mapResourceToAvatarSizes(postbox: context.account.postbox, resource: resource, representations: representations)
})
|> ignoreValues
@ -569,12 +571,13 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
endEditingImpl?()
presentControllerImpl?(legacyController, nil)
let completedImpl: (UIImage) -> Void = { image in
let completedGroupPhotoImpl: (UIImage) -> Void = { image in
if let data = image.jpegData(compressionQuality: 0.6) {
let resource = LocalFileMediaResource(fileId: arc4random64())
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource)
uploadedAvatar.set(uploadedPeerPhoto(postbox: context.account.postbox, network: context.account.network, resource: resource))
uploadedVideoAvatar = nil
updateState { current in
var current = current
current.avatar = .image(representation, false)
@ -583,18 +586,119 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
}
}
let completedGroupVideoImpl: (UIImage, URL, TGVideoEditAdjustments?) -> Void = { image, url, adjustments in
if let data = image.jpegData(compressionQuality: 0.6) {
let photoResource = LocalFileMediaResource(fileId: arc4random64())
context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource)
updateState { state in
var state = state
state.avatar = .image(representation, true)
return state
}
var videoStartTimestamp: Double? = nil
if let adjustments = adjustments, adjustments.videoStartValue > 0.0 {
videoStartTimestamp = adjustments.videoStartValue - adjustments.trimStartValue
}
let signal = Signal<TelegramMediaResource?, UploadPeerPhotoError> { subscriber in
var filteredPath = url.path
if filteredPath.hasPrefix("file://") {
filteredPath = String(filteredPath[filteredPath.index(filteredPath.startIndex, offsetBy: "file://".count)])
}
let avAsset = AVURLAsset(url: URL(fileURLWithPath: filteredPath))
let entityRenderer: LegacyPaintEntityRenderer? = adjustments.flatMap { adjustments in
if let paintingData = adjustments.paintingData, paintingData.hasAnimation {
return LegacyPaintEntityRenderer(account: context.account, adjustments: adjustments)
} else {
return nil
}
}
let uploadInterface = LegacyLiveUploadInterface(account: context.account)
let signal = TGMediaVideoConverter.convert(avAsset, adjustments: adjustments, watcher: uploadInterface, entityRenderer: entityRenderer)!
let signalDisposable = signal.start(next: { next in
if let result = next as? TGMediaVideoConversionResult {
if let image = result.coverImage, let data = image.jpegData(compressionQuality: 0.7) {
context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
}
if let timestamp = videoStartTimestamp {
videoStartTimestamp = max(0.0, min(timestamp, result.duration))
}
var value = stat()
if stat(result.fileURL.path, &value) == 0 {
if let data = try? Data(contentsOf: result.fileURL) {
let resource: TelegramMediaResource
if let liveUploadData = result.liveUploadData as? LegacyLiveUploadInterfaceResult {
resource = LocalFileMediaResource(fileId: liveUploadData.id)
} else {
resource = LocalFileMediaResource(fileId: arc4random64())
}
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
subscriber.putNext(resource)
}
}
subscriber.putCompletion()
}
}, error: { _ in
}, completed: nil)
let disposable = ActionDisposable {
signalDisposable?.dispose()
}
return ActionDisposable {
disposable.dispose()
}
}
uploadedAvatar.set(uploadedPeerPhoto(postbox: context.account.postbox, network: context.account.network, resource: photoResource))
let promise = Promise<UploadedPeerPhotoData?>()
promise.set(signal
|> `catch` { _ -> Signal<TelegramMediaResource?, NoError> in
return .single(nil)
}
|> mapToSignal { resource -> Signal<UploadedPeerPhotoData?, NoError> in
if let resource = resource {
return uploadedPeerVideo(postbox: context.account.postbox, network: context.account.network, messageMediaPreuploadManager: context.account.messageMediaPreuploadManager, resource: resource) |> map(Optional.init)
} else {
return .single(nil)
}
} |> afterNext { next in
if let next = next, next.isCompleted {
updateState { state in
var state = state
state.avatar = .image(representation, false)
return state
}
}
})
uploadedVideoAvatar = (promise, videoStartTimestamp)
}
}
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: stateValue.with({ $0.avatar }) != nil, hasViewButton: false, personalPhoto: false, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: false)!
let _ = currentAvatarMixin.swap(mixin)
mixin.requestSearchController = { assetsController in
let controller = WebSearchController(context: context, peer: peer, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: title, completion: { result in
assetsController?.dismiss()
completedImpl(result)
completedGroupPhotoImpl(result)
}))
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}
mixin.didFinishWithImage = { image in
if let image = image {
completedImpl(image)
completedGroupPhotoImpl(image)
}
}
mixin.didFinishWithVideo = { image, url, adjustments in
if let image = image, let url = url {
completedGroupVideoImpl(image, url, adjustments)
}
}
if stateValue.with({ $0.avatar }) != nil {

View File

@ -175,9 +175,11 @@ private final class PeerInfoScreenMemberItemNode: PeerInfoScreenItemNode {
let itemHeight: ItemListPeerItemHeight
let itemText: ItemListPeerItemText
var synchronousLoads = false
if case .account = item.member {
itemHeight = .generic
itemText = .none
synchronousLoads = true
} else {
itemHeight = .peerList
itemText = .presence
@ -207,7 +209,7 @@ private final class PeerInfoScreenMemberItemNode: PeerInfoScreenItemNode {
})
} else {
var itemNodeValue: ListViewItemNode?
peerItem.nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: false, previousItem: nil, nextItem: nil, completion: { node, apply in
peerItem.nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: synchronousLoads, previousItem: nil, nextItem: nil, completion: { node, apply in
itemNodeValue = node
apply().1(ListViewItemApply(isOnScreen: true))
})

View File

@ -318,11 +318,14 @@ private func peerInfoScreenInputData(context: AccountContext, peerId: PeerId, is
private func peerInfoProfilePhotos(context: AccountContext, peerId: PeerId) -> Signal<Any, NoError> {
return context.account.postbox.combinedView(keys: [.basicPeer(peerId)])
|> map { view -> AvatarGalleryEntry? in
|> mapToSignal { view -> Signal<AvatarGalleryEntry?, NoError> in
guard let peer = (view.views[.basicPeer(peerId)] as? BasicPeerView)?.peer else {
return nil
return .single(nil)
}
return initialAvatarGalleryEntries(account: context.account, peer: peer)
|> map { entries in
return entries.first
}
return initialAvatarGalleryEntries(peer: peer).first
}
|> distinctUntilChanged
|> mapToSignal { firstEntry -> Signal<[AvatarGalleryEntry], NoError> in

View File

@ -158,12 +158,12 @@ final class PeerInfoHeaderNavigationTransition {
}
enum PeerInfoAvatarListItem: Equatable {
case topImage([ImageRepresentationWithReference], Data?)
case topImage([ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], Data?)
case image(TelegramMediaImageReference?, [ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], Data?)
var id: WrappedMediaResourceId {
switch self {
case let .topImage(representations, _):
case let .topImage(representations, _, _):
let representation = largestImageRepresentation(representations.map { $0.representation }) ?? representations[representations.count - 1].representation
return WrappedMediaResourceId(representation.resource.id)
case let .image(_, representations, _, _):
@ -171,6 +171,15 @@ enum PeerInfoAvatarListItem: Equatable {
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 {
@ -250,15 +259,15 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
let immediateThumbnailData: Data?
var id: Int64?
switch item {
case let .topImage(topRepresentations, immediateThumbnail):
case let .topImage(topRepresentations, videoRepresentationsValue, immediateThumbnail):
representations = topRepresentations
videoRepresentations = []
videoRepresentations = videoRepresentationsValue
immediateThumbnailData = immediateThumbnail
id = 1
case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail):
representations = imageRepresentations
videoRepresentations = videoRepresentationsValue
immediateThumbnailData = immediateThumbnail
if case let .cloud(imageId, _, _) = reference {
id = imageId
}
@ -268,45 +277,44 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
if let video = videoRepresentations.last, let id = id {
let mediaManager = self.context.sharedContext.mediaManager
let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: 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, 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
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.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
return playing
}
|> take(1)
|> deliverOnMainQueue).start(completed: { [weak self] in
if let strongSelf = self {
Queue.mainQueue().after(0.1) {
strongSelf.videoNode?.isHidden = false
if videoContent.id != self.videoContent?.id {
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 _ = video.startTimestamp {
self.playbackStatusDisposable.set((videoNode.status
|> map { status -> Bool in
if let status = status, case .playing = status.status {
return true
} else {
return false
}
}
}))
} else {
self.playbackStatusDisposable.set(nil)
videoNode.isHidden = false
|> filter { playing in
return playing
}
|> take(1)
|> deliverOnMainQueue).start(completed: { [weak self] in
if let strongSelf = self {
Queue.mainQueue().after(0.12) {
strongSelf.videoNode?.isHidden = false
}
}
}))
} else {
self.playbackStatusDisposable.set(nil)
videoNode.isHidden = false
}
self.videoContent = videoContent
self.videoNode = videoNode
self.statusPromise.set(videoNode.status |> map { ($0, video.startTimestamp) })
self.addSubnode(videoNode)
}
if let startTimestamp = video.startTimestamp {
videoNode.seek(startTimestamp)
}
self.videoContent = videoContent
self.videoNode = videoNode
self.statusPromise.set(videoNode.status |> map { ($0, video.startTimestamp) })
self.addSubnode(videoNode)
} else {
if let videoNode = self.videoNode {
self.videoContent = nil
@ -762,9 +770,9 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
var entries: [AvatarGalleryEntry] = []
for entry in self.galleryEntries {
switch entry {
case let .topImage(representations, _, immediateThumbnailData, _):
case let .topImage(representations, videoRepresentations, _, immediateThumbnailData, _):
entries.append(entry)
items.append(.topImage(representations, immediateThumbnailData))
items.append(.topImage(representations, videoRepresentations, immediateThumbnailData))
case let .image(id, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _):
if image.0 == reference {
entries.insert(entry, at: 0)
@ -798,9 +806,9 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
let previousIndex = self.currentIndex
for entry in self.galleryEntries {
switch entry {
case let .topImage(representations, _, immediateThumbnailData, _):
case let .topImage(representations, videoRepresentations, _, immediateThumbnailData, _):
entries.append(entry)
items.append(.topImage(representations, immediateThumbnailData))
items.append(.topImage(representations, videoRepresentations, immediateThumbnailData))
case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _):
if image.0 != reference {
entries.append(entry)
@ -862,8 +870,8 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
var items: [PeerInfoAvatarListItem] = []
for entry in entries {
switch entry {
case let .topImage(representations, _, immediateThumbnailData, _):
items.append(.topImage(representations, 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))
}
@ -1035,6 +1043,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
var tapped: (() -> Void)?
private var isFirstAvatarLoading = true
var item: PeerInfoAvatarListItem?
private let playbackStatusDisposable = MetaDisposable()
@ -1090,64 +1099,100 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
}
}
var removedPhotoResourceIds = Set<String>()
func update(peer: Peer?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool) {
if let peer = peer {
let previousItem = self.item
self.item = item
var overrideImage: AvatarNodeImageOverride?
if peer.isDeleted {
overrideImage = .deletedIcon
} else if let previousItem = previousItem, item == nil {
if case let .image(image) = previousItem, let rep = image.1.last {
self.removedPhotoResourceIds.insert(rep.representation.resource.id.uniqueId)
}
overrideImage = AvatarNodeImageOverride.none
} else if let rep = peer.profileImageRepresentations.last, self.removedPhotoResourceIds.contains(rep.resource.id.uniqueId) {
overrideImage = AvatarNodeImageOverride.none
}
self.avatarNode.setPeer(context: self.context, theme: theme, peer: peer, overrideImage: overrideImage, synchronousLoad: self.isFirstAvatarLoading, displayDimensions: CGSize(width: avatarSize, height: avatarSize), storeUnrounded: true)
self.isFirstAvatarLoading = false
self.avatarNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize))
self.avatarNode.font = avatarPlaceholderFont(size: floor(avatarSize * 16.0 / 37.0))
if let item = item, case let .image(reference, representations, videoRepresentations, immediateThumbnailData) = item, let video = videoRepresentations.last, case let .cloud(imageId, _, _) = reference {
let id = imageId
let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: 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, 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 {
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
return playing
}
|> take(1)
|> deliverOnMainQueue).start(completed: { [weak self] in
if let strongSelf = self {
Queue.mainQueue().after(0.1) {
strongSelf.videoNode?.isHidden = false
if let item = item {
let representations: [ImageRepresentationWithReference]
let videoRepresentations: [TelegramMediaImage.VideoRepresentation]
let immediateThumbnailData: Data?
var id: Int64?
switch item {
case let .topImage(topRepresentations, videoRepresentationsValue, immediateThumbnail):
representations = topRepresentations
videoRepresentations = videoRepresentationsValue
immediateThumbnailData = immediateThumbnail
id = 1
case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail):
representations = imageRepresentations
videoRepresentations = videoRepresentationsValue
immediateThumbnailData = immediateThumbnail
if case let .cloud(imageId, _, _) = reference {
id = imageId
}
}
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: .embedded)
videoNode.isUserInteractionEnabled = 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
}
}
}))
} else {
self.videoStartTimestamp = nil
self.playbackStatusDisposable.set(nil)
videoNode.isHidden = false
|> filter { playing in
return playing
}
|> take(1)
|> 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
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)
videoNode.removeFromSupernode()
}
} else if let videoNode = self.videoNode {
self.videoContent = nil
@ -1206,13 +1251,14 @@ final class PeerInfoEditingAvatarOverlayNode: ASDisplayNode {
self.updatingAvatarOverlay = ASImageNode()
self.updatingAvatarOverlay.displayWithoutProcessing = true
self.updatingAvatarOverlay.displaysAsynchronously = false
self.updatingAvatarOverlay.isHidden = true
self.updatingAvatarOverlay.alpha = 0.0
self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(rgb: 0x000000, alpha: 0.6))
self.statusNode.isUserInteractionEnabled = false
self.iconNode = ASImageNode()
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Avatar/EditAvatarIconLarge"), color: .white)
self.iconNode.alpha = 0.0
super.init()
@ -1241,6 +1287,8 @@ final class PeerInfoEditingAvatarOverlayNode: ASDisplayNode {
self.imageNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize))
self.updatingAvatarOverlay.frame = self.imageNode.frame
let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .linear)
if canEditPeerInfo(context: self.context, peer: peer) {
var overlayHidden = false
var iconHidden = false
@ -1248,7 +1296,7 @@ final class PeerInfoEditingAvatarOverlayNode: ASDisplayNode {
overlayHidden = false
iconHidden = true
self.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: uploadProgress ?? 0.0, cancelEnabled: true))
self.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: max(0.027, uploadProgress ?? 0.0), cancelEnabled: true))
if case let .image(representation) = updatingAvatar {
if representation != self.currentRepresentation {
@ -1258,21 +1306,26 @@ final class PeerInfoEditingAvatarOverlayNode: ASDisplayNode {
}
}
}
self.iconNode.isHidden = iconHidden
self.updatingAvatarOverlay.isHidden = overlayHidden
transition.updateAlpha(node: self.iconNode, alpha: iconHidden ? 0.0 : 1.0)
transition.updateAlpha(node: self.updatingAvatarOverlay, alpha: overlayHidden ? 0.0 : 1.0)
} else {
if isEditing {
iconHidden = false
iconHidden = peer.profileImageRepresentations.isEmpty
overlayHidden = peer.profileImageRepresentations.isEmpty
} else {
iconHidden = true
overlayHidden = true
}
Queue.mainQueue().after(0.1) { [weak self] in
self?.statusNode.transitionToState(.none)
self?.currentRepresentation = nil
self?.imageNode.setSignal(.single(nil))
self?.iconNode.isHidden = iconHidden
self?.updatingAvatarOverlay.isHidden = overlayHidden
Queue.mainQueue().after(0.3) { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.statusNode.transitionToState(.none)
strongSelf.currentRepresentation = nil
strongSelf.imageNode.setSignal(.single(nil))
transition.updateAlpha(node: strongSelf.iconNode, alpha: iconHidden ? 0.0 : 1.0)
transition.updateAlpha(node: strongSelf.updatingAvatarOverlay, alpha: overlayHidden ? 0.0 : 1.0)
}
}
if !overlayHidden && self.updatingAvatarOverlay.image == nil {
@ -1280,10 +1333,9 @@ final class PeerInfoEditingAvatarOverlayNode: ASDisplayNode {
}
} else {
self.statusNode.transitionToState(.none)
self.iconNode.isHidden = true
self.updatingAvatarOverlay.isHidden = true
self.currentRepresentation = nil
transition.updateAlpha(node: self.iconNode, alpha: 0.0)
transition.updateAlpha(node: self.updatingAvatarOverlay, alpha: 0.0)
}
}
}
@ -1296,7 +1348,7 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
private var videoStartTimestamp: Double?
var tapped: ((Bool) -> Void)?
var canAttachVideo: Bool = true
init(context: AccountContext) {
@ -1322,7 +1374,7 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
guard let peer = peer else {
return
}
let overrideImage: AvatarNodeImageOverride?
if canEditPeerInfo(context: self.context, peer: peer), peer.profileImageRepresentations.isEmpty {
overrideImage = .editAvatarIcon
@ -1333,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.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 {
let id = imageId
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, 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
}
if let item = item {
let representations: [ImageRepresentationWithReference]
let videoRepresentations: [TelegramMediaImage.VideoRepresentation]
let immediateThumbnailData: Data?
var id: Int64?
switch item {
case let .topImage(topRepresentations, videoRepresentationsValue, immediateThumbnail):
representations = topRepresentations
videoRepresentations = videoRepresentationsValue
immediateThumbnailData = immediateThumbnail
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))
let shape = CAShapeLayer()
shape.path = maskPath.cgPath
videoNode.layer.mask = shape
self.insertSubnode(videoNode, aboveSubnode: self.avatarNode)
videoNode.removeFromSupernode()
}
} else if let videoNode = self.videoNode {
self.videoContent = nil
@ -1393,6 +1470,8 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
var arguments: (Peer?, PresentationTheme, CGFloat, Bool)?
var item: PeerInfoAvatarListItem?
var itemsUpdated: (([PeerInfoAvatarListItem]) -> Void)?
init(context: AccountContext, readyWhenGalleryLoads: Bool) {
self.avatarContainerNode = PeerInfoAvatarTransformContainerNode(context: context)
self.listContainerTransformNode = ASDisplayNode()
@ -1440,6 +1519,7 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
self.listContainerNode.itemsUpdated = { [weak self] items in
if let strongSelf = self {
strongSelf.item = items.first
strongSelf.itemsUpdated?(items)
if let (peer, theme, avatarSize, isExpanded) = strongSelf.arguments {
strongSelf.avatarContainerNode.update(peer: peer, item: strongSelf.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded)
}
@ -2190,6 +2270,9 @@ private let TitleNodeStateExpanded = 1
final class PeerInfoHeaderNode: ASDisplayNode {
private var context: AccountContext
private var presentationData: PresentationData?
private var state: PeerInfoState?
private var peer: Peer?
private var avatarSize: CGFloat?
private let isOpenedFromChat: Bool
private let isSettings: Bool
@ -2218,6 +2301,10 @@ final class PeerInfoHeaderNode: ASDisplayNode {
private let backgroundNode: ASDisplayNode
private let expandedBackgroundNode: ASDisplayNode
let separatorNode: ASDisplayNode
let navigationBackgroundNode: ASDisplayNode
var navigationTitle: String?
let navigationTitleNode: ImmediateTextNode
let navigationSeparatorNode: ASDisplayNode
let navigationButtonContainer: PeerInfoHeaderNavigationButtonContainerNode
var performButtonAction: ((PeerInfoHeaderButtonKey) -> Void)?
@ -2272,6 +2359,13 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.avatarOverlayNode = PeerInfoEditingAvatarOverlayNode(context: context)
self.navigationBackgroundNode = ASDisplayNode()
self.navigationBackgroundNode.isUserInteractionEnabled = false
self.navigationTitleNode = ImmediateTextNode()
self.navigationSeparatorNode = ASDisplayNode()
self.navigationButtonContainer = PeerInfoHeaderNavigationButtonContainerNode()
self.backgroundNode = ASDisplayNode()
@ -2303,6 +2397,9 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.addSubnode(self.regularContentNode)
self.addSubnode(self.editingContentNode)
self.addSubnode(self.avatarOverlayNode)
self.addSubnode(self.navigationBackgroundNode)
self.navigationBackgroundNode.addSubnode(self.navigationTitleNode)
self.navigationBackgroundNode.addSubnode(self.navigationSeparatorNode)
self.addSubnode(self.navigationButtonContainer)
self.addSubnode(self.separatorNode)
@ -2310,10 +2407,14 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self?.initiateAvatarExpansion()
}
self.editingContentNode.avatarNode.tapped = { [weak self] confirm in
guard let strongSelf = self else {
self?.requestOpenAvatarForEditing?(confirm)
}
self.avatarListNode.itemsUpdated = { [weak self] items in
guard let strongSelf = self, let state = strongSelf.state, let peer = strongSelf.peer, let presentationData = strongSelf.presentationData, let avatarSize = strongSelf.avatarSize else {
return
}
strongSelf.requestOpenAvatarForEditing?(confirm)
strongSelf.editingContentNode.avatarNode.update(peer: peer, item: strongSelf.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing)
}
}
@ -2387,6 +2488,12 @@ 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
let avatarSize: CGFloat = isModalOverlay ? 200.0 : 100.0
self.avatarSize = avatarSize
var contentOffset = contentOffset
if isMediaOnly {
@ -2446,7 +2553,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.avatarListNode.animateAvatarCollapse(transition: transition)
}
} else {
let avatarSize: CGFloat = isModalOverlay ? 200.0 : 100.0
let backgroundTransitionFraction: CGFloat = max(0.0, min(1.0, contentOffset / (112.0 + avatarSize)))
transition.updateAlpha(node: self.expandedBackgroundNode, alpha: backgroundTransitionFraction)
}
@ -2454,6 +2560,18 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.avatarListNode.avatarContainerNode.updateTransitionFraction(transitionFraction, transition: transition)
self.avatarListNode.listContainerNode.currentItemNode?.updateTransitionFraction(transitionFraction, transition: transition)
if self.navigationTitle != presentationData.strings.EditProfile_Title || themeUpdated {
self.navigationTitleNode.attributedText = NSAttributedString(string: presentationData.strings.EditProfile_Title, font: Font.bold(17.0), textColor: presentationData.theme.rootController.navigationBar.primaryTextColor)
}
let navigationTitleSize = self.navigationTitleNode.updateLayout(CGSize(width: width, height: navigationHeight))
self.navigationTitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - navigationTitleSize.width) / 2.0), y: navigationHeight - 44.0 + floorToScreenPixels((44.0 - navigationTitleSize.height) / 2.0)), size: navigationTitleSize)
self.navigationBackgroundNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: navigationHeight))
self.navigationSeparatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: navigationHeight), size: CGSize(width: width, height: UIScreenPixel))
self.navigationBackgroundNode.backgroundColor = presentationData.theme.rootController.navigationBar.backgroundColor
self.navigationSeparatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor
transition.updateAlpha(node: self.navigationBackgroundNode, alpha: state.isEditing && self.isSettings ? min(1.0, contentOffset / (navigationHeight * 0.5)) : 0.0)
self.separatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
let defaultButtonSize: CGFloat = 40.0
@ -2521,7 +2639,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
TitleNodeStateExpanded: MultiScaleTextState(attributedText: usernameString, constrainedSize: CGSize(width: width - titleNodeLayout[TitleNodeStateExpanded]!.size.width - 8.0, height: titleConstrainedSize.height))
], mainState: TitleNodeStateRegular)
let avatarSize: CGFloat = isModalOverlay ? 200.0 : 100.0
let avatarFrame = CGRect(origin: CGPoint(x: floor((width - avatarSize) / 2.0), y: statusBarHeight + 10.0), size: CGSize(width: avatarSize, height: avatarSize))
let avatarCenter = CGPoint(x: (1.0 - transitionFraction) * avatarFrame.midX + transitionFraction * transitionSourceAvatarFrame.midX, y: (1.0 - transitionFraction) * avatarFrame.midY + transitionFraction * transitionSourceAvatarFrame.midY)

View File

@ -2086,7 +2086,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
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
self?.openAvatarForEditing(hasRemove: false, completion: completion)
@ -2535,7 +2535,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
self.headerNode.avatarListNode.listContainerNode.currentIndexUpdated = { [weak self] in
self?.updateNavigation(transition: .immediate, additive: false)
self?.updateNavigation(transition: .immediate, additive: true)
}
self.dataDisposable = (screenData
@ -4067,6 +4067,10 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
}
if let timestamp = videoStartTimestamp {
videoStartTimestamp = max(0.0, min(timestamp, result.duration))
}
var value = stat()
if stat(result.fileURL.path, &value) == 0 {
if let data = try? Data(contentsOf: result.fileURL) {
@ -5415,11 +5419,19 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
guard let (_, navigationHeight) = self.validLayout else {
return
}
var height: CGFloat = self.isSettings ? 140.0 : 212.0
if self.headerNode.twoLineInfo {
height += 17.0
}
if !self.state.isEditing {
if self.state.isEditing && self.isSettings {
if targetContentOffset.pointee.y < navigationHeight {
if targetContentOffset.pointee.y < navigationHeight / 2.0 {
targetContentOffset.pointee.y = 0.0
} else {
targetContentOffset.pointee.y = navigationHeight
}
}
} else {
var height: CGFloat = self.isSettings ? 140.0 : 212.0
if self.headerNode.twoLineInfo {
height += 17.0
}
if targetContentOffset.pointee.y < height {
if targetContentOffset.pointee.y < height / 2.0 {
targetContentOffset.pointee.y = 0.0

View File

@ -43,8 +43,12 @@ final class GenericEmbedImplementation: WebEmbedImplementation {
self.onPlaybackStarted = onPlaybackStarted
updateStatus(self.status)
let html = String(format: htmlTemplate, self.url)
webView.loadHTMLString(html, baseURL: URL(string: "about:blank"))
if self.url.contains("player.twitch.tv/"), let url = URL(string: self.url) {
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))

View File

@ -32,11 +32,12 @@ public final class NativeVideoContent: UniversalVideoContent {
let fetchAutomatically: Bool
let onlyFullSizeThumbnail: Bool
let autoFetchFullSizeThumbnail: Bool
let startTimestamp: Double?
let continuePlayingWithoutSoundOnLostAudioSession: Bool
let placeholderColor: UIColor
let tempFilePath: String?
public init(id: NativeVideoContentId, fileReference: FileMediaReference, imageReference: ImageMediaReference? = nil, streamVideo: MediaPlayerStreaming = .none, loopVideo: Bool = false, enableSound: Bool = true, baseRate: Double = 1.0, fetchAutomatically: Bool = true, onlyFullSizeThumbnail: Bool = false, autoFetchFullSizeThumbnail: Bool = false, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor = .white, tempFilePath: String? = nil) {
public init(id: NativeVideoContentId, fileReference: FileMediaReference, imageReference: ImageMediaReference? = nil, streamVideo: MediaPlayerStreaming = .none, loopVideo: Bool = false, enableSound: Bool = true, baseRate: Double = 1.0, fetchAutomatically: Bool = true, onlyFullSizeThumbnail: Bool = false, autoFetchFullSizeThumbnail: Bool = false, startTimestamp: Double? = nil, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor = .white, tempFilePath: String? = nil) {
self.id = id
self.nativeId = id
self.fileReference = fileReference
@ -62,13 +63,14 @@ public final class NativeVideoContent: UniversalVideoContent {
self.fetchAutomatically = fetchAutomatically
self.onlyFullSizeThumbnail = onlyFullSizeThumbnail
self.autoFetchFullSizeThumbnail = autoFetchFullSizeThumbnail
self.startTimestamp = startTimestamp
self.continuePlayingWithoutSoundOnLostAudioSession = continuePlayingWithoutSoundOnLostAudioSession
self.placeholderColor = placeholderColor
self.tempFilePath = tempFilePath
}
public func makeContentNode(postbox: Postbox, audioSession: ManagedAudioSession) -> UniversalVideoContentNode & ASDisplayNode {
return NativeVideoContentNode(postbox: postbox, audioSessionManager: audioSession, fileReference: self.fileReference, imageReference: self.imageReference, streamVideo: self.streamVideo, loopVideo: self.loopVideo, enableSound: self.enableSound, baseRate: self.baseRate, fetchAutomatically: self.fetchAutomatically, onlyFullSizeThumbnail: self.onlyFullSizeThumbnail, autoFetchFullSizeThumbnail: self.autoFetchFullSizeThumbnail, continuePlayingWithoutSoundOnLostAudioSession: self.continuePlayingWithoutSoundOnLostAudioSession, placeholderColor: self.placeholderColor, tempFilePath: self.tempFilePath)
return NativeVideoContentNode(postbox: postbox, audioSessionManager: audioSession, fileReference: self.fileReference, imageReference: self.imageReference, streamVideo: self.streamVideo, loopVideo: self.loopVideo, enableSound: self.enableSound, baseRate: self.baseRate, fetchAutomatically: self.fetchAutomatically, onlyFullSizeThumbnail: self.onlyFullSizeThumbnail, autoFetchFullSizeThumbnail: self.autoFetchFullSizeThumbnail, startTimestamp: self.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: self.continuePlayingWithoutSoundOnLostAudioSession, placeholderColor: self.placeholderColor, tempFilePath: self.tempFilePath)
}
public func isEqual(to other: UniversalVideoContent) -> Bool {
@ -141,7 +143,7 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
private var shouldPlay: Bool = false
init(postbox: Postbox, audioSessionManager: ManagedAudioSession, fileReference: FileMediaReference, imageReference: ImageMediaReference?, streamVideo: MediaPlayerStreaming, loopVideo: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, onlyFullSizeThumbnail: Bool, autoFetchFullSizeThumbnail: Bool, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor, tempFilePath: String?) {
init(postbox: Postbox, audioSessionManager: ManagedAudioSession, fileReference: FileMediaReference, imageReference: ImageMediaReference?, streamVideo: MediaPlayerStreaming, loopVideo: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, onlyFullSizeThumbnail: Bool, autoFetchFullSizeThumbnail: Bool, startTimestamp: Double?, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor, tempFilePath: String?) {
self.postbox = postbox
self.fileReference = fileReference
self.placeholderColor = placeholderColor
@ -226,6 +228,10 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
self.imageNode.imageUpdated = { [weak self] _ in
self?._ready.set(.single(Void()))
}
if let startTimestamp = startTimestamp {
self.seek(startTimestamp)
}
}
deinit {