mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-09-05 04:10:16 +00:00
Merge commit 'b51dd938f44a0e348bbed299b07e421a1f519378'
This commit is contained in:
commit
06b52aa249
@ -5688,8 +5688,8 @@ Any member of this group will be able to see messages in the channel.";
|
|||||||
"Group.MessageVideoUpdated" = "Group video updated";
|
"Group.MessageVideoUpdated" = "Group video updated";
|
||||||
"Channel.MessageVideoUpdated" = "Channel video updated";
|
"Channel.MessageVideoUpdated" = "Channel video updated";
|
||||||
|
|
||||||
"Conversation.Dice.u1F3C0" = "Send a basketball emoji to shoot a free throw.";
|
"Conversation.Dice.u1F3C0" = "Send a basketball emoji to try your luck.";
|
||||||
"Conversation.Dice.u26BD" = "Send a football emoji to do a free kick.";
|
"Conversation.Dice.u26BD" = "Send a football emoji to try your luck.";
|
||||||
|
|
||||||
"SettingsSearch_Synonyms_ChatFolders" = "";
|
"SettingsSearch_Synonyms_ChatFolders" = "";
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ private func mappedInsertEntries(context: AccountContext, presentationData: Item
|
|||||||
return entries.map { entry -> ListViewInsertItem in
|
return entries.map { entry -> ListViewInsertItem in
|
||||||
switch entry.entry {
|
switch entry.entry {
|
||||||
case let .displayTab(theme, text, value):
|
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)
|
nodeInteraction.updateShowCallsTab(value)
|
||||||
}), directionHint: entry.directionHint)
|
}), directionHint: entry.directionHint)
|
||||||
case let .displayTabInfo(theme, text):
|
case let .displayTabInfo(theme, text):
|
||||||
@ -130,7 +130,7 @@ private func mappedUpdateEntries(context: AccountContext, presentationData: Item
|
|||||||
return entries.map { entry -> ListViewUpdateItem in
|
return entries.map { entry -> ListViewUpdateItem in
|
||||||
switch entry.entry {
|
switch entry.entry {
|
||||||
case let .displayTab(theme, text, value):
|
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)
|
nodeInteraction.updateShowCallsTab(value)
|
||||||
}), directionHint: entry.directionHint)
|
}), directionHint: entry.directionHint)
|
||||||
case let .displayTabInfo(theme, text):
|
case let .displayTabInfo(theme, text):
|
||||||
|
@ -928,8 +928,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
let transformedCopyViewFinalFrame = videoNode.view.convert(videoNode.view.bounds, to: self.view)
|
let transformedCopyViewFinalFrame = videoNode.view.convert(videoNode.view.bounds, to: self.view)
|
||||||
|
|
||||||
let (maybeSurfaceCopyView, _) = node.2()
|
let (maybeSurfaceCopyView, _) = node.2()
|
||||||
let (maybeCopyView, copyViewBackgrond) = node.2()
|
let (maybeCopyView, copyViewBackground) = node.2()
|
||||||
copyViewBackgrond?.alpha = 0.0
|
copyViewBackground?.alpha = 0.0
|
||||||
let surfaceCopyView = maybeSurfaceCopyView!
|
let surfaceCopyView = maybeSurfaceCopyView!
|
||||||
let copyView = maybeCopyView!
|
let copyView = maybeCopyView!
|
||||||
|
|
||||||
@ -1032,8 +1032,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
var copyCompleted = false
|
var copyCompleted = false
|
||||||
|
|
||||||
let (maybeSurfaceCopyView, _) = node.2()
|
let (maybeSurfaceCopyView, _) = node.2()
|
||||||
let (maybeCopyView, copyViewBackgrond) = node.2()
|
let (maybeCopyView, copyViewBackground) = node.2()
|
||||||
copyViewBackgrond?.alpha = 0.0
|
copyViewBackground?.alpha = 0.0
|
||||||
let surfaceCopyView = maybeSurfaceCopyView!
|
let surfaceCopyView = maybeSurfaceCopyView!
|
||||||
let copyView = maybeCopyView!
|
let copyView = maybeCopyView!
|
||||||
|
|
||||||
|
@ -563,8 +563,8 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo
|
|||||||
if strongSelf.updatingAvatarOverlay.supernode == nil {
|
if strongSelf.updatingAvatarOverlay.supernode == nil {
|
||||||
strongSelf.insertSubnode(strongSelf.updatingAvatarOverlay, aboveSubnode: strongSelf.avatarNode)
|
strongSelf.insertSubnode(strongSelf.updatingAvatarOverlay, aboveSubnode: strongSelf.avatarNode)
|
||||||
}
|
}
|
||||||
if let updatingImage = item.updatingImage, case .image(_, true) = updatingImage {
|
if let updatingImage = item.updatingImage, case let .image(_, loading) = updatingImage {
|
||||||
strongSelf.activityIndicator.isHidden = false
|
strongSelf.activityIndicator.isHidden = !loading
|
||||||
}
|
}
|
||||||
} else if strongSelf.updatingAvatarOverlay.supernode != nil {
|
} else if strongSelf.updatingAvatarOverlay.supernode != nil {
|
||||||
if animated {
|
if animated {
|
||||||
|
@ -948,7 +948,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
let hasCorners = itemListHasRoundedBlockLayout(params) || !item.noInsets
|
let hasCorners = itemListHasRoundedBlockLayout(params) && !item.noInsets
|
||||||
var hasTopCorners = false
|
var hasTopCorners = false
|
||||||
var hasBottomCorners = false
|
var hasBottomCorners = false
|
||||||
switch neighbors.top {
|
switch neighbors.top {
|
||||||
|
@ -20,13 +20,14 @@ public class ItemListSwitchItem: ListViewItem, ItemListItem {
|
|||||||
let enabled: Bool
|
let enabled: Bool
|
||||||
let disableLeadingInset: Bool
|
let disableLeadingInset: Bool
|
||||||
let maximumNumberOfLines: Int
|
let maximumNumberOfLines: Int
|
||||||
|
let noCorners: Bool
|
||||||
public let sectionId: ItemListSectionId
|
public let sectionId: ItemListSectionId
|
||||||
let style: ItemListStyle
|
let style: ItemListStyle
|
||||||
let updated: (Bool) -> Void
|
let updated: (Bool) -> Void
|
||||||
let activatedWhileDisabled: () -> Void
|
let activatedWhileDisabled: () -> Void
|
||||||
public let tag: ItemListItemTag?
|
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.presentationData = presentationData
|
||||||
self.title = title
|
self.title = title
|
||||||
self.value = value
|
self.value = value
|
||||||
@ -35,6 +36,7 @@ public class ItemListSwitchItem: ListViewItem, ItemListItem {
|
|||||||
self.enabled = enabled
|
self.enabled = enabled
|
||||||
self.disableLeadingInset = disableLeadingInset
|
self.disableLeadingInset = disableLeadingInset
|
||||||
self.maximumNumberOfLines = maximumNumberOfLines
|
self.maximumNumberOfLines = maximumNumberOfLines
|
||||||
|
self.noCorners = noCorners
|
||||||
self.sectionId = sectionId
|
self.sectionId = sectionId
|
||||||
self.style = style
|
self.style = style
|
||||||
self.updated = updated
|
self.updated = updated
|
||||||
@ -329,7 +331,7 @@ public class ItemListSwitchItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
let hasCorners = itemListHasRoundedBlockLayout(params) && !item.noCorners
|
||||||
var hasTopCorners = false
|
var hasTopCorners = false
|
||||||
var hasBottomCorners = false
|
var hasBottomCorners = false
|
||||||
switch neighbors.top {
|
switch neighbors.top {
|
||||||
|
@ -1716,7 +1716,7 @@
|
|||||||
CGFloat duration = self.view.frame.size.height / velocity;
|
CGFloat duration = self.view.frame.size.height / velocity;
|
||||||
CGRect targetFrame = CGRectOffset(self.view.frame, 0, self.view.frame.size.height);
|
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;
|
self.view.frame = targetFrame;
|
||||||
} completion:^(__unused BOOL finished)
|
} completion:^(__unused BOOL finished)
|
||||||
|
@ -23,12 +23,12 @@ public enum AvatarGalleryEntryId: Hashable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum AvatarGalleryEntry: Equatable {
|
public enum AvatarGalleryEntry: Equatable {
|
||||||
case topImage([ImageRepresentationWithReference], GalleryItemIndexData?, Data?, String?)
|
case topImage([ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], GalleryItemIndexData?, Data?, String?)
|
||||||
case image(MediaId, TelegramMediaImageReference?, [ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], Peer?, Int32, GalleryItemIndexData?, MessageId?, Data?, String?)
|
case image(MediaId, TelegramMediaImageReference?, [ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], Peer?, Int32, GalleryItemIndexData?, MessageId?, Data?, String?)
|
||||||
|
|
||||||
public var id: AvatarGalleryEntryId {
|
public var id: AvatarGalleryEntryId {
|
||||||
switch self {
|
switch self {
|
||||||
case let .topImage(representations, _, _, _):
|
case let .topImage(representations, _, _, _, _):
|
||||||
if let last = representations.last {
|
if let last = representations.last {
|
||||||
return .resource(last.representation.resource.id.uniqueId)
|
return .resource(last.representation.resource.id.uniqueId)
|
||||||
}
|
}
|
||||||
@ -43,7 +43,7 @@ public enum AvatarGalleryEntry: Equatable {
|
|||||||
|
|
||||||
public var representations: [ImageRepresentationWithReference] {
|
public var representations: [ImageRepresentationWithReference] {
|
||||||
switch self {
|
switch self {
|
||||||
case let .topImage(representations, _, _, _):
|
case let .topImage(representations, _, _, _, _):
|
||||||
return representations
|
return representations
|
||||||
case let .image(_, _, representations, _, _, _, _, _, _, _):
|
case let .image(_, _, representations, _, _, _, _, _, _, _):
|
||||||
return representations
|
return representations
|
||||||
@ -52,8 +52,8 @@ public enum AvatarGalleryEntry: Equatable {
|
|||||||
|
|
||||||
public var videoRepresentations: [TelegramMediaImage.VideoRepresentation] {
|
public var videoRepresentations: [TelegramMediaImage.VideoRepresentation] {
|
||||||
switch self {
|
switch self {
|
||||||
case .topImage:
|
case let .topImage(_, videoRepresentations, _, _, _):
|
||||||
return []
|
return videoRepresentations
|
||||||
case let .image(_, _, _, videoRepresentations, _, _, _, _, _, _):
|
case let .image(_, _, _, videoRepresentations, _, _, _, _, _, _):
|
||||||
return videoRepresentations
|
return videoRepresentations
|
||||||
}
|
}
|
||||||
@ -61,7 +61,7 @@ public enum AvatarGalleryEntry: Equatable {
|
|||||||
|
|
||||||
public var indexData: GalleryItemIndexData? {
|
public var indexData: GalleryItemIndexData? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .topImage(_, indexData, _, _):
|
case let .topImage(_, _, indexData, _, _):
|
||||||
return indexData
|
return indexData
|
||||||
case let .image(_, _, _, _, _, _, indexData, _, _, _):
|
case let .image(_, _, _, _, _, _, indexData, _, _, _):
|
||||||
return indexData
|
return indexData
|
||||||
@ -70,8 +70,8 @@ public enum AvatarGalleryEntry: Equatable {
|
|||||||
|
|
||||||
public static func ==(lhs: AvatarGalleryEntry, rhs: AvatarGalleryEntry) -> Bool {
|
public static func ==(lhs: AvatarGalleryEntry, rhs: AvatarGalleryEntry) -> Bool {
|
||||||
switch lhs {
|
switch lhs {
|
||||||
case let .topImage(lhsRepresentations, lhsIndexData, lhsImmediateThumbnailData, lhsCategory):
|
case let .topImage(lhsRepresentations, lhsVideoRepresentations, lhsIndexData, lhsImmediateThumbnailData, lhsCategory):
|
||||||
if case let .topImage(rhsRepresentations, rhsIndexData, rhsImmediateThumbnailData, rhsCategory) = rhs, lhsRepresentations == rhsRepresentations, lhsIndexData == rhsIndexData, lhsImmediateThumbnailData == rhsImmediateThumbnailData, lhsCategory == rhsCategory {
|
if case let .topImage(rhsRepresentations, rhsVideoRepresentations, rhsIndexData, rhsImmediateThumbnailData, rhsCategory) = rhs, lhsRepresentations == rhsRepresentations, lhsVideoRepresentations == rhsVideoRepresentations, lhsIndexData == rhsIndexData, lhsImmediateThumbnailData == rhsImmediateThumbnailData, lhsCategory == rhsCategory {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -102,8 +102,8 @@ public func normalizeEntries(_ entries: [AvatarGalleryEntry]) -> [AvatarGalleryE
|
|||||||
var index: Int32 = 0
|
var index: Int32 = 0
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
let indexData = GalleryItemIndexData(position: index, totalCount: count)
|
let indexData = GalleryItemIndexData(position: index, totalCount: count)
|
||||||
if case let .topImage(representations, _, immediateThumbnailData, category) = entry {
|
if case let .topImage(representations, videoRepresentations, _, immediateThumbnailData, category) = entry {
|
||||||
updatedEntries.append(.topImage(representations, indexData, immediateThumbnailData, category))
|
updatedEntries.append(.topImage(representations, videoRepresentations, indexData, immediateThumbnailData, category))
|
||||||
} else if case let .image(id, reference, representations, videoRepresentations, peer, date, _, messageId, immediateThumbnailData, category) = entry {
|
} else if case let .image(id, reference, representations, videoRepresentations, peer, date, _, messageId, immediateThumbnailData, category) = entry {
|
||||||
updatedEntries.append(.image(id, reference, representations, videoRepresentations, peer, date, indexData, messageId, immediateThumbnailData, category))
|
updatedEntries.append(.image(id, reference, representations, videoRepresentations, peer, date, indexData, messageId, immediateThumbnailData, category))
|
||||||
}
|
}
|
||||||
@ -112,39 +112,62 @@ public func normalizeEntries(_ entries: [AvatarGalleryEntry]) -> [AvatarGalleryE
|
|||||||
return updatedEntries
|
return updatedEntries
|
||||||
}
|
}
|
||||||
|
|
||||||
public func initialAvatarGalleryEntries(peer: Peer) -> [AvatarGalleryEntry] {
|
public func initialAvatarGalleryEntries(account: Account, peer: Peer) -> Signal<[AvatarGalleryEntry], NoError> {
|
||||||
var initialEntries: [AvatarGalleryEntry] = []
|
var initialEntries: [AvatarGalleryEntry] = []
|
||||||
if !peer.profileImageRepresentations.isEmpty, let peerReference = PeerReference(peer) {
|
if !peer.profileImageRepresentations.isEmpty, let peerReference = PeerReference(peer) {
|
||||||
initialEntries.append(.topImage(peer.profileImageRepresentations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), nil, nil, nil))
|
initialEntries.append(.topImage(peer.profileImageRepresentations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), [], nil, nil, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
if 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> {
|
public func fetchedAvatarGalleryEntries(account: Account, peer: Peer) -> Signal<[AvatarGalleryEntry], NoError> {
|
||||||
let initialEntries = initialAvatarGalleryEntries(peer: peer)
|
return initialAvatarGalleryEntries(account: account, peer: peer)
|
||||||
return Signal<[AvatarGalleryEntry], NoError>.single(initialEntries)
|
|> mapToSignal { initialEntries in
|
||||||
|> then(
|
return .single(initialEntries)
|
||||||
requestPeerPhotos(account: account, peerId: peer.id)
|
|> then(
|
||||||
|> map { photos -> [AvatarGalleryEntry] in
|
requestPeerPhotos(account: account, peerId: peer.id)
|
||||||
var result: [AvatarGalleryEntry] = []
|
|> map { photos -> [AvatarGalleryEntry] in
|
||||||
let initialEntries = initialAvatarGalleryEntries(peer: peer)
|
var result: [AvatarGalleryEntry] = []
|
||||||
if photos.isEmpty {
|
if photos.isEmpty {
|
||||||
result = initialEntries
|
result = initialEntries
|
||||||
} else {
|
} else {
|
||||||
var index: Int32 = 0
|
var index: Int32 = 0
|
||||||
for photo in photos {
|
for photo in photos {
|
||||||
let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photos.count))
|
let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photos.count))
|
||||||
if result.isEmpty, let first = initialEntries.first {
|
if result.isEmpty, let first = initialEntries.first {
|
||||||
result.append(.image(photo.image.imageId, photo.image.reference, first.representations, photo.image.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil))
|
result.append(.image(photo.image.imageId, photo.image.reference, first.representations, photo.image.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil))
|
||||||
} else {
|
} else {
|
||||||
result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.standalone(resource: $0.resource)) }), photo.image.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil))
|
result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.standalone(resource: $0.resource)) }), photo.image.videoRepresentations, 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> {
|
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 ()
|
private let editDisposable = MetaDisposable ()
|
||||||
|
|
||||||
public init(context: AccountContext, peer: Peer, sourceHasRoundCorners: Bool = true, remoteEntries: Promise<[AvatarGalleryEntry]>? = nil, centralEntryIndex: Int? = nil, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, synchronousLoad: Bool = false) {
|
public init(context: AccountContext, peer: Peer, sourceHasRoundCorners: Bool = true, remoteEntries: Promise<[AvatarGalleryEntry]>? = nil, skipInitial: Bool = false, centralEntryIndex: Int? = nil, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, synchronousLoad: Bool = false) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
self.sourceHasRoundCorners = sourceHasRoundCorners
|
self.sourceHasRoundCorners = sourceHasRoundCorners
|
||||||
@ -242,7 +265,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
|||||||
remoteEntriesSignal = fetchedAvatarGalleryEntries(account: context.account, peer: peer)
|
remoteEntriesSignal = fetchedAvatarGalleryEntries(account: context.account, peer: peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
let entriesSignal: Signal<[AvatarGalleryEntry], NoError> = .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
|
let presentationData = self.presentationData
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ class PeerAvatarImageGalleryItem: GalleryItem {
|
|||||||
func thumbnailItem() -> (Int64, GalleryThumbnailItem)? {
|
func thumbnailItem() -> (Int64, GalleryThumbnailItem)? {
|
||||||
let content: [ImageRepresentationWithReference]
|
let content: [ImageRepresentationWithReference]
|
||||||
switch self.entry {
|
switch self.entry {
|
||||||
case let .topImage(representations, _, _, _):
|
case let .topImage(representations, _, _, _, _):
|
||||||
content = representations
|
content = representations
|
||||||
case let .image(_, _, representations, _, _, _, _, _, _, _):
|
case let .image(_, _, representations, _, _, _, _, _, _, _):
|
||||||
content = representations
|
content = representations
|
||||||
@ -256,6 +256,8 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
if case let .image(image) = entry {
|
if case let .image(image) = entry {
|
||||||
id = image.0.id
|
id = image.0.id
|
||||||
category = image.9
|
category = image.9
|
||||||
|
} else {
|
||||||
|
id = 1
|
||||||
}
|
}
|
||||||
if let video = entry.videoRepresentations.last, let id = id {
|
if let video = entry.videoRepresentations.last, let id = id {
|
||||||
if video != previousVideoRepresentations?.last {
|
if video != previousVideoRepresentations?.last {
|
||||||
@ -449,7 +451,10 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
var copyCompleted = false
|
var copyCompleted = false
|
||||||
var surfaceCopyCompleted = false
|
var surfaceCopyCompleted = false
|
||||||
|
|
||||||
let copyView = node.2().0!
|
let (maybeCopyView, copyViewBackground) = node.2()
|
||||||
|
copyViewBackground?.alpha = 1.0
|
||||||
|
|
||||||
|
let copyView = maybeCopyView!
|
||||||
|
|
||||||
if self.sourceHasRoundCorners {
|
if self.sourceHasRoundCorners {
|
||||||
self.view.insertSubview(copyView, belowSubview: self.scrollNode.view)
|
self.view.insertSubview(copyView, belowSubview: self.scrollNode.view)
|
||||||
@ -548,7 +553,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
case .Remote:
|
case .Remote:
|
||||||
let representations: [ImageRepresentationWithReference]
|
let representations: [ImageRepresentationWithReference]
|
||||||
switch entry {
|
switch entry {
|
||||||
case let .topImage(topRepresentations, _, _, _):
|
case let .topImage(topRepresentations, _, _, _, _):
|
||||||
representations = topRepresentations
|
representations = topRepresentations
|
||||||
case let .image(_, _, imageRepresentations, _, _, _, _, _, _, _):
|
case let .image(_, _, imageRepresentations, _, _, _, _, _, _, _):
|
||||||
representations = imageRepresentations
|
representations = imageRepresentations
|
||||||
|
@ -197,13 +197,13 @@ private func profileSearchableItems(context: AccountContext, canAddAccount: Bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
var items: [SettingsSearchableItem] = []
|
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
|
// 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)
|
// 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
|
// 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)
|
// 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
|
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
|
let _ = (context.account.postbox.transaction { transaction -> String in
|
||||||
return (transaction.getPeer(context.account.peerId) as? TelegramUser)?.phone ?? ""
|
return (transaction.getPeer(context.account.peerId) as? TelegramUser)?.phone ?? ""
|
||||||
|
@ -168,6 +168,7 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
public let hasScheduledMessages: Bool
|
public let hasScheduledMessages: Bool
|
||||||
public let statsDatacenterId: Int32
|
public let statsDatacenterId: Int32
|
||||||
public let invitedBy: PeerId?
|
public let invitedBy: PeerId?
|
||||||
|
public let photo: TelegramMediaImage?
|
||||||
|
|
||||||
public let peerIds: Set<PeerId>
|
public let peerIds: Set<PeerId>
|
||||||
public let messageIds: Set<MessageId>
|
public let messageIds: Set<MessageId>
|
||||||
@ -196,9 +197,10 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
self.hasScheduledMessages = false
|
self.hasScheduledMessages = false
|
||||||
self.statsDatacenterId = 0
|
self.statsDatacenterId = 0
|
||||||
self.invitedBy = nil
|
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.isNotAccessible = isNotAccessible
|
||||||
self.flags = flags
|
self.flags = flags
|
||||||
self.about = about
|
self.about = about
|
||||||
@ -217,6 +219,7 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
self.hasScheduledMessages = hasScheduledMessages
|
self.hasScheduledMessages = hasScheduledMessages
|
||||||
self.statsDatacenterId = statsDatacenterId
|
self.statsDatacenterId = statsDatacenterId
|
||||||
self.invitedBy = invitedBy
|
self.invitedBy = invitedBy
|
||||||
|
self.photo = photo
|
||||||
|
|
||||||
var peerIds = Set<PeerId>()
|
var peerIds = Set<PeerId>()
|
||||||
for botInfo in botInfos {
|
for botInfo in botInfos {
|
||||||
@ -242,75 +245,79 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedIsNotAccessible(_ isNotAccessible: Bool) -> CachedChannelData {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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) {
|
public init(decoder: PostboxDecoder) {
|
||||||
@ -372,6 +379,12 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
|
|
||||||
self.invitedBy = decoder.decodeOptionalInt64ForKey("invBy").flatMap(PeerId.init)
|
self.invitedBy = decoder.decodeOptionalInt64ForKey("invBy").flatMap(PeerId.init)
|
||||||
|
|
||||||
|
if let photo = decoder.decodeObjectForKey("ph", decoder: { TelegramMediaImage(decoder: $0) }) as? TelegramMediaImage {
|
||||||
|
self.photo = photo
|
||||||
|
} else {
|
||||||
|
self.photo = nil
|
||||||
|
}
|
||||||
|
|
||||||
if let linkedDiscussionPeerId = self.linkedDiscussionPeerId {
|
if let linkedDiscussionPeerId = self.linkedDiscussionPeerId {
|
||||||
peerIds.insert(linkedDiscussionPeerId)
|
peerIds.insert(linkedDiscussionPeerId)
|
||||||
}
|
}
|
||||||
@ -462,6 +475,12 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
} else {
|
} else {
|
||||||
encoder.encodeNil(forKey: "invBy")
|
encoder.encodeNil(forKey: "invBy")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let photo = self.photo {
|
||||||
|
encoder.encodeObject(photo, forKey: "ph")
|
||||||
|
} else {
|
||||||
|
encoder.encodeNil(forKey: "ph")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func isEqual(to: CachedPeerData) -> Bool {
|
public func isEqual(to: CachedPeerData) -> Bool {
|
||||||
@ -541,6 +560,10 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if other.photo != self.photo {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ public final class CachedGroupData: CachedPeerData {
|
|||||||
public let flags: CachedGroupFlags
|
public let flags: CachedGroupFlags
|
||||||
public let hasScheduledMessages: Bool
|
public let hasScheduledMessages: Bool
|
||||||
public let invitedBy: PeerId?
|
public let invitedBy: PeerId?
|
||||||
|
public let photo: TelegramMediaImage?
|
||||||
|
|
||||||
public let peerIds: Set<PeerId>
|
public let peerIds: Set<PeerId>
|
||||||
public let messageIds: Set<MessageId>
|
public let messageIds: Set<MessageId>
|
||||||
@ -66,9 +67,10 @@ public final class CachedGroupData: CachedPeerData {
|
|||||||
self.flags = CachedGroupFlags()
|
self.flags = CachedGroupFlags()
|
||||||
self.hasScheduledMessages = false
|
self.hasScheduledMessages = false
|
||||||
self.invitedBy = nil
|
self.invitedBy = nil
|
||||||
|
self.photo = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(participants: CachedGroupParticipants?, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, about: String?, flags: CachedGroupFlags, hasScheduledMessages: Bool, invitedBy: PeerId?) {
|
public init(participants: CachedGroupParticipants?, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, about: String?, flags: CachedGroupFlags, hasScheduledMessages: Bool, invitedBy: PeerId?, photo: TelegramMediaImage?) {
|
||||||
self.participants = participants
|
self.participants = participants
|
||||||
self.exportedInvitation = exportedInvitation
|
self.exportedInvitation = exportedInvitation
|
||||||
self.botInfos = botInfos
|
self.botInfos = botInfos
|
||||||
@ -78,6 +80,7 @@ public final class CachedGroupData: CachedPeerData {
|
|||||||
self.flags = flags
|
self.flags = flags
|
||||||
self.hasScheduledMessages = hasScheduledMessages
|
self.hasScheduledMessages = hasScheduledMessages
|
||||||
self.invitedBy = invitedBy
|
self.invitedBy = invitedBy
|
||||||
|
self.photo = photo
|
||||||
|
|
||||||
var messageIds = Set<MessageId>()
|
var messageIds = Set<MessageId>()
|
||||||
if let pinnedMessageId = self.pinnedMessageId {
|
if let pinnedMessageId = self.pinnedMessageId {
|
||||||
@ -123,6 +126,12 @@ public final class CachedGroupData: CachedPeerData {
|
|||||||
|
|
||||||
self.invitedBy = decoder.decodeOptionalInt64ForKey("invBy").flatMap(PeerId.init)
|
self.invitedBy = decoder.decodeOptionalInt64ForKey("invBy").flatMap(PeerId.init)
|
||||||
|
|
||||||
|
if let photo = decoder.decodeObjectForKey("ph", decoder: { TelegramMediaImage(decoder: $0) }) as? TelegramMediaImage {
|
||||||
|
self.photo = photo
|
||||||
|
} else {
|
||||||
|
self.photo = nil
|
||||||
|
}
|
||||||
|
|
||||||
var messageIds = Set<MessageId>()
|
var messageIds = Set<MessageId>()
|
||||||
if let pinnedMessageId = self.pinnedMessageId {
|
if let pinnedMessageId = self.pinnedMessageId {
|
||||||
messageIds.insert(pinnedMessageId)
|
messageIds.insert(pinnedMessageId)
|
||||||
@ -181,6 +190,12 @@ public final class CachedGroupData: CachedPeerData {
|
|||||||
} else {
|
} else {
|
||||||
encoder.encodeNil(forKey: "invBy")
|
encoder.encodeNil(forKey: "invBy")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let photo = self.photo {
|
||||||
|
encoder.encodeObject(photo, forKey: "ph")
|
||||||
|
} else {
|
||||||
|
encoder.encodeNil(forKey: "ph")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func isEqual(to: CachedPeerData) -> Bool {
|
public func isEqual(to: CachedPeerData) -> Bool {
|
||||||
@ -192,38 +207,42 @@ public final class CachedGroupData: CachedPeerData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedParticipants(_ participants: CachedGroupParticipants?) -> CachedGroupData {
|
public func withUpdatedParticipants(_ participants: CachedGroupParticipants?) -> CachedGroupData {
|
||||||
return CachedGroupData(participants: participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy)
|
return CachedGroupData(participants: participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedGroupData {
|
public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedGroupData {
|
||||||
return CachedGroupData(participants: self.participants, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy)
|
return CachedGroupData(participants: self.participants, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedGroupData {
|
public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedGroupData {
|
||||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy)
|
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedGroupData {
|
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedGroupData {
|
||||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy)
|
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedGroupData {
|
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedGroupData {
|
||||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy)
|
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedAbout(_ about: String?) -> CachedGroupData {
|
public func withUpdatedAbout(_ about: String?) -> CachedGroupData {
|
||||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy)
|
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedFlags(_ flags: CachedGroupFlags) -> CachedGroupData {
|
public func withUpdatedFlags(_ flags: CachedGroupFlags) -> CachedGroupData {
|
||||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy)
|
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedGroupData {
|
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedGroupData {
|
||||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: hasScheduledMessages, invitedBy: self.invitedBy)
|
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedInvitedBy(_ invitedBy: PeerId?) -> CachedGroupData {
|
public func withUpdatedInvitedBy(_ invitedBy: PeerId?) -> CachedGroupData {
|
||||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: invitedBy)
|
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: invitedBy, photo: self.photo)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withUpdatedPhoto(_ photo: TelegramMediaImage?) -> CachedGroupData {
|
||||||
|
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: photo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ public enum AdminLogEventAction {
|
|||||||
case changeTitle(prev: String, new: String)
|
case changeTitle(prev: String, new: String)
|
||||||
case changeAbout(prev: String, new: String)
|
case changeAbout(prev: String, new: String)
|
||||||
case changeUsername(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 toggleInvites(Bool)
|
||||||
case toggleSignatures(Bool)
|
case toggleSignatures(Bool)
|
||||||
case updatePinned(Message?)
|
case updatePinned(Message?)
|
||||||
@ -149,7 +149,9 @@ public func channelAdminLogEvents(postbox: Postbox, network: Network, peerId: Pe
|
|||||||
case let .channelAdminLogEventActionChangeUsername(prev, new):
|
case let .channelAdminLogEventActionChangeUsername(prev, new):
|
||||||
action = .changeUsername(prev: prev, new: new)
|
action = .changeUsername(prev: prev, new: new)
|
||||||
case let .channelAdminLogEventActionChangePhoto(prev, 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):
|
case let .channelAdminLogEventActionToggleInvites(new):
|
||||||
action = .toggleInvites(boolFromApiValue(new))
|
action = .toggleInvites(boolFromApiValue(new))
|
||||||
case let .channelAdminLogEventActionToggleSignatures(new):
|
case let .channelAdminLogEventActionToggleSignatures(new):
|
||||||
|
@ -22,9 +22,17 @@ public func updateAccountPhoto(account: Account, resource: MediaResource?, video
|
|||||||
public struct UploadedPeerPhotoData {
|
public struct UploadedPeerPhotoData {
|
||||||
fileprivate let resource: MediaResource
|
fileprivate let resource: MediaResource
|
||||||
fileprivate let content: UploadedPeerPhotoDataContent
|
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 result(MultipartUploadResult)
|
||||||
case error
|
case error
|
||||||
}
|
}
|
||||||
|
@ -250,6 +250,8 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let photo: TelegramMediaImage? = chatFull.chatPhoto.flatMap(telegramMediaImageFromApiPhoto)
|
||||||
|
|
||||||
let exportedInvitation = ExportedInvitation(apiExportedInvite: chatFull.exportedInvite)
|
let exportedInvitation = ExportedInvitation(apiExportedInvite: chatFull.exportedInvite)
|
||||||
let pinnedMessageId = chatFull.pinnedMsgId.flatMap({ MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) })
|
let pinnedMessageId = chatFull.pinnedMsgId.flatMap({ MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) })
|
||||||
|
|
||||||
@ -300,6 +302,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
|||||||
.withUpdatedFlags(flags)
|
.withUpdatedFlags(flags)
|
||||||
.withUpdatedHasScheduledMessages(hasScheduledMessages)
|
.withUpdatedHasScheduledMessages(hasScheduledMessages)
|
||||||
.withUpdatedInvitedBy(invitedBy)
|
.withUpdatedInvitedBy(invitedBy)
|
||||||
|
.withUpdatedPhoto(photo)
|
||||||
})
|
})
|
||||||
case .channelFull:
|
case .channelFull:
|
||||||
break
|
break
|
||||||
@ -337,7 +340,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch fullChat {
|
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()
|
var channelFlags = CachedChannelFlags()
|
||||||
if (flags & (1 << 3)) != 0 {
|
if (flags & (1 << 3)) != 0 {
|
||||||
channelFlags.insert(.canDisplayParticipants)
|
channelFlags.insert(.canDisplayParticipants)
|
||||||
@ -467,6 +470,8 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let photo = telegramMediaImageFromApiPhoto(chatPhoto)
|
||||||
|
|
||||||
var minAvailableMessageIdUpdated = false
|
var minAvailableMessageIdUpdated = false
|
||||||
transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in
|
transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in
|
||||||
var previous: CachedChannelData
|
var previous: CachedChannelData
|
||||||
@ -496,6 +501,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
|||||||
.withUpdatedHasScheduledMessages(hasScheduledMessages)
|
.withUpdatedHasScheduledMessages(hasScheduledMessages)
|
||||||
.withUpdatedStatsDatacenterId(statsDc ?? 0)
|
.withUpdatedStatsDatacenterId(statsDc ?? 0)
|
||||||
.withUpdatedInvitedBy(invitedBy)
|
.withUpdatedInvitedBy(invitedBy)
|
||||||
|
.withUpdatedPhoto(photo)
|
||||||
})
|
})
|
||||||
|
|
||||||
if let minAvailableMessageId = minAvailableMessageId, minAvailableMessageIdUpdated {
|
if let minAvailableMessageId = minAvailableMessageId, minAvailableMessageIdUpdated {
|
||||||
|
@ -83,7 +83,7 @@ final class AuthorizationSequenceSignUpController: ViewController {
|
|||||||
let currentAvatarMixin = Atomic<NSObject?>(value: nil)
|
let currentAvatarMixin = Atomic<NSObject?>(value: nil)
|
||||||
|
|
||||||
self.displayNode = AuthorizationSequenceSignUpControllerNode(theme: self.presentationData.theme, strings: self.presentationData.strings, addPhoto: { [weak self] in
|
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?.view.endEditing(true)
|
||||||
self?.present(c, in: .window(.root), with: a)
|
self?.present(c, in: .window(.root), with: a)
|
||||||
}, openCurrent: nil, completion: { image in
|
}, openCurrent: nil, completion: { image in
|
||||||
|
@ -132,7 +132,7 @@ final class AuthorizationSequenceSignUpControllerNode: ASDisplayNode, UITextFiel
|
|||||||
self.currentPhotoNode.displayWithoutProcessing = true
|
self.currentPhotoNode.displayWithoutProcessing = true
|
||||||
|
|
||||||
self.addPhotoButton = HighlightableButtonNode()
|
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.setBackgroundImage(generateCircleImage(diameter: 110.0, lineWidth: 1.0, color: self.theme.list.itemPlaceholderTextColor), for: .normal)
|
||||||
|
|
||||||
self.addPhotoButton.addSubnode(self.currentPhotoNode)
|
self.addPhotoButton.addSubnode(self.currentPhotoNode)
|
||||||
|
@ -119,7 +119,7 @@ public final class ChatControllerInteraction {
|
|||||||
let displayPsa: (String, ASDisplayNode) -> Void
|
let displayPsa: (String, ASDisplayNode) -> Void
|
||||||
let displayDiceTooltip: (TelegramMediaDice) -> Void
|
let displayDiceTooltip: (TelegramMediaDice) -> Void
|
||||||
let animateDiceSuccess: () -> Void
|
let animateDiceSuccess: () -> Void
|
||||||
let greetingStickerNode: () -> (ASDisplayNode, ASDisplayNode, () -> Void)?
|
let greetingStickerNode: () -> (ASDisplayNode, ASDisplayNode, ASDisplayNode, () -> Void)?
|
||||||
|
|
||||||
let requestMessageUpdate: (MessageId) -> Void
|
let requestMessageUpdate: (MessageId) -> Void
|
||||||
let cancelInteractiveKeyboardGestures: () -> Void
|
let cancelInteractiveKeyboardGestures: () -> Void
|
||||||
@ -137,7 +137,7 @@ public final class ChatControllerInteraction {
|
|||||||
var searchTextHighightState: (String, [MessageIndex])?
|
var searchTextHighightState: (String, [MessageIndex])?
|
||||||
var seenOneTimeAnimatedMedia = Set<MessageId>()
|
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.openMessage = openMessage
|
||||||
self.openPeer = openPeer
|
self.openPeer = openPeer
|
||||||
self.openPeerMention = openPeerMention
|
self.openPeerMention = openPeerMention
|
||||||
|
@ -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 {
|
if let greetingStickerNode = self.emptyNode?.greetingStickerNode {
|
||||||
self.historyNode.itemHeaderNodesAlpha = 0.0
|
self.historyNode.itemHeaderNodesAlpha = 0.0
|
||||||
return (greetingStickerNode, self, { [weak self] in
|
return (greetingStickerNode, self, self.historyNode, { [weak self] in
|
||||||
self?.historyNode.forEachItemHeaderNode { node in
|
self?.historyNode.forEachItemHeaderNode { node in
|
||||||
node.alpha = 1.0
|
node.alpha = 1.0
|
||||||
node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
|
@ -65,9 +65,28 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
|
|
||||||
override func transitionNode(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
|
override func transitionNode(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
|
||||||
if let imageNode = self.imageNode, self.item?.message.id == messageId {
|
if let imageNode = self.imageNode, self.item?.message.id == messageId {
|
||||||
return (imageNode, imageNode.bounds, { [weak imageNode] in
|
return (imageNode, imageNode.bounds, { [weak self] in
|
||||||
let snapshot = imageNode?.view.snapshotContentTree(unhide: true)
|
guard let strongSelf = self, let imageNode = strongSelf.imageNode else {
|
||||||
return (snapshot, nil)
|
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 {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
@ -100,6 +119,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.imageNode?.isHidden = mediaHidden
|
self.imageNode?.isHidden = mediaHidden
|
||||||
|
self.mediaBackgroundNode.isHidden = mediaHidden
|
||||||
return mediaHidden
|
return mediaHidden
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
private var isPlaying = false
|
private var isPlaying = false
|
||||||
private var animateGreeting = false
|
private var animateGreeting = false
|
||||||
private weak var greetingStickerParentNode: ASDisplayNode?
|
private weak var greetingStickerParentNode: ASDisplayNode?
|
||||||
|
private weak var greetingStickerListNode: ASDisplayNode?
|
||||||
private var greetingCompletion: (() -> Void)?
|
private var greetingCompletion: (() -> Void)?
|
||||||
|
|
||||||
private var swipeToReplyNode: ChatMessageSwipeToReplyNode?
|
private var swipeToReplyNode: ChatMessageSwipeToReplyNode?
|
||||||
@ -238,11 +239,12 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
self.animationNode = animationNode
|
self.animationNode = animationNode
|
||||||
} else {
|
} else {
|
||||||
let animationNode: AnimatedStickerNode
|
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
|
animationNode = greetingStickerNode
|
||||||
self.imageNode.alpha = 0.0
|
self.imageNode.alpha = 0.0
|
||||||
self.animateGreeting = true
|
self.animateGreeting = true
|
||||||
self.greetingStickerParentNode = parentNode
|
self.greetingStickerParentNode = parentNode
|
||||||
|
self.greetingStickerListNode = listNode
|
||||||
self.greetingCompletion = greetingCompletion
|
self.greetingCompletion = greetingCompletion
|
||||||
self.dateAndStatusNode.alpha = 0.0
|
self.dateAndStatusNode.alpha = 0.0
|
||||||
} else {
|
} else {
|
||||||
@ -767,26 +769,31 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
let initialFrame = animationNode.view.convert(animationNode.bounds, to: parentNode.view)
|
let initialFrame = animationNode.view.convert(animationNode.bounds, to: parentNode.view)
|
||||||
parentNode.addSubnode(animationNode)
|
parentNode.addSubnode(animationNode)
|
||||||
animationNode.frame = initialFrame
|
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 {
|
var targetPosition = initialFrame.center.y
|
||||||
animationNode.updateLayout(size: updatedContentFrame.insetBy(dx: imageInset, dy: imageInset).size)
|
if let listNode = strongSelf.greetingStickerListNode as? ListView {
|
||||||
}
|
targetPosition = listNode.frame.height - listNode.insets.top - animationNodeFrame.height / 2.0 - 12.0
|
||||||
|
|
||||||
strongSelf.dateAndStatusNode.alpha = 1.0
|
|
||||||
strongSelf.dateAndStatusNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
|
||||||
|
|
||||||
strongSelf.greetingCompletion?()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
} else if strongSelf.animationNode?.supernode === strongSelf.contextSourceNode.contentNode {
|
||||||
strongSelf.animationNode?.frame = animationNodeFrame
|
strongSelf.animationNode?.frame = animationNodeFrame
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
private var controllerInteraction: ChatControllerInteraction!
|
private var controllerInteraction: ChatControllerInteraction!
|
||||||
|
|
||||||
private let galleryHiddenMesageAndMediaDisposable = MetaDisposable()
|
private let galleryHiddenMesageAndMediaDisposable = MetaDisposable()
|
||||||
|
private let temporaryHiddenGalleryMediaDisposable = MetaDisposable()
|
||||||
|
|
||||||
private var chatPresentationDataPromise: Promise<ChatPresentationData>
|
private var chatPresentationDataPromise: Promise<ChatPresentationData>
|
||||||
private var presentationDataDisposable: Disposable?
|
private var presentationDataDisposable: Disposable?
|
||||||
@ -183,7 +184,27 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
}, callPeer: { peerId, isVideo in
|
}, callPeer: { peerId, isVideo in
|
||||||
self?.controllerInteraction?.callPeer(peerId, isVideo)
|
self?.controllerInteraction?.callPeer(peerId, isVideo)
|
||||||
}, enqueueMessage: { _ in
|
}, 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
|
return false
|
||||||
}, openPeer: { [weak self] peerId, _, message in
|
}, openPeer: { [weak self] peerId, _, message in
|
||||||
@ -516,6 +537,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
self.historyDisposable?.dispose()
|
self.historyDisposable?.dispose()
|
||||||
self.navigationActionDisposable.dispose()
|
self.navigationActionDisposable.dispose()
|
||||||
self.galleryHiddenMesageAndMediaDisposable.dispose()
|
self.galleryHiddenMesageAndMediaDisposable.dispose()
|
||||||
|
self.temporaryHiddenGalleryMediaDisposable.dispose()
|
||||||
self.resolvePeerByNameDisposable.dispose()
|
self.resolvePeerByNameDisposable.dispose()
|
||||||
self.adminsDisposable?.dispose()
|
self.adminsDisposable?.dispose()
|
||||||
self.banDisposables.dispose()
|
self.banDisposables.dispose()
|
||||||
|
@ -219,8 +219,9 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
|
|||||||
peers[peer.id] = peer
|
peers[peer.id] = peer
|
||||||
|
|
||||||
var photo: TelegramMediaImage?
|
var photo: TelegramMediaImage?
|
||||||
if !new.isEmpty {
|
let (newPhoto, newVideo) = new
|
||||||
photo = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: new, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
|
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)
|
let action = TelegramMediaActionType.photoUpdated(image: photo)
|
||||||
|
@ -17,6 +17,7 @@ import ItemListAvatarAndNameInfoItem
|
|||||||
import WebSearchUI
|
import WebSearchUI
|
||||||
import PeerInfoUI
|
import PeerInfoUI
|
||||||
import MapResourceToAvatarSizes
|
import MapResourceToAvatarSizes
|
||||||
|
import LegacyMediaPickerUI
|
||||||
|
|
||||||
private struct CreateChannelArguments {
|
private struct CreateChannelArguments {
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
@ -214,6 +215,7 @@ public func createChannelController(context: AccountContext) -> ViewController {
|
|||||||
let currentAvatarMixin = Atomic<TGMediaAvatarMenuMixin?>(value: nil)
|
let currentAvatarMixin = Atomic<TGMediaAvatarMenuMixin?>(value: nil)
|
||||||
|
|
||||||
let uploadedAvatar = Promise<UploadedPeerPhotoData>()
|
let uploadedAvatar = Promise<UploadedPeerPhotoData>()
|
||||||
|
var uploadedVideoAvatar: (Promise<UploadedPeerPhotoData?>, Double?)? = nil
|
||||||
|
|
||||||
let arguments = CreateChannelArguments(context: context, updateEditingName: { editingName in
|
let arguments = CreateChannelArguments(context: context, updateEditingName: { editingName in
|
||||||
updateState { current in
|
updateState { current in
|
||||||
@ -260,7 +262,7 @@ public func createChannelController(context: AccountContext) -> ViewController {
|
|||||||
return $0.avatar
|
return $0.avatar
|
||||||
}
|
}
|
||||||
if let _ = updatingAvatar {
|
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)
|
return mapResourceToAvatarSizes(postbox: context.account.postbox, resource: resource, representations: representations)
|
||||||
}).start()
|
}).start()
|
||||||
}
|
}
|
||||||
@ -311,12 +313,13 @@ public func createChannelController(context: AccountContext) -> ViewController {
|
|||||||
endEditingImpl?()
|
endEditingImpl?()
|
||||||
presentControllerImpl?(legacyController, nil)
|
presentControllerImpl?(legacyController, nil)
|
||||||
|
|
||||||
let completedImpl: (UIImage) -> Void = { image in
|
let completedChannelPhotoImpl: (UIImage) -> Void = { image in
|
||||||
if let data = image.jpegData(compressionQuality: 0.6) {
|
if let data = image.jpegData(compressionQuality: 0.6) {
|
||||||
let resource = LocalFileMediaResource(fileId: arc4random64())
|
let resource = LocalFileMediaResource(fileId: arc4random64())
|
||||||
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
|
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
|
||||||
let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource)
|
let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource)
|
||||||
uploadedAvatar.set(uploadedPeerPhoto(postbox: context.account.postbox, network: context.account.network, resource: resource))
|
uploadedAvatar.set(uploadedPeerPhoto(postbox: context.account.postbox, network: context.account.network, resource: resource))
|
||||||
|
uploadedVideoAvatar = nil
|
||||||
updateState { current in
|
updateState { current in
|
||||||
var current = current
|
var current = current
|
||||||
current.avatar = .image(representation, false)
|
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 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)
|
let _ = currentAvatarMixin.swap(mixin)
|
||||||
mixin.requestSearchController = { assetsController in
|
mixin.requestSearchController = { assetsController in
|
||||||
let controller = WebSearchController(context: context, peer: peer, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: title, completion: { result in
|
let controller = WebSearchController(context: context, peer: peer, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: title, completion: { result in
|
||||||
assetsController?.dismiss()
|
assetsController?.dismiss()
|
||||||
completedImpl(result)
|
completedChannelPhotoImpl(result)
|
||||||
}))
|
}))
|
||||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
}
|
}
|
||||||
mixin.didFinishWithImage = { image in
|
mixin.didFinishWithImage = { image in
|
||||||
if let image = image {
|
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 {
|
if stateValue.with({ $0.avatar }) != nil {
|
||||||
|
@ -26,6 +26,7 @@ import PeerInfoUI
|
|||||||
import MapResourceToAvatarSizes
|
import MapResourceToAvatarSizes
|
||||||
import ItemListAddressItem
|
import ItemListAddressItem
|
||||||
import ItemListVenueItem
|
import ItemListVenueItem
|
||||||
|
import LegacyMediaPickerUI
|
||||||
|
|
||||||
private struct CreateGroupArguments {
|
private struct CreateGroupArguments {
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
@ -383,6 +384,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
|
|||||||
let currentAvatarMixin = Atomic<TGMediaAvatarMenuMixin?>(value: nil)
|
let currentAvatarMixin = Atomic<TGMediaAvatarMenuMixin?>(value: nil)
|
||||||
|
|
||||||
let uploadedAvatar = Promise<UploadedPeerPhotoData>()
|
let uploadedAvatar = Promise<UploadedPeerPhotoData>()
|
||||||
|
var uploadedVideoAvatar: (Promise<UploadedPeerPhotoData?>, Double?)? = nil
|
||||||
|
|
||||||
let addressPromise = Promise<String?>(nil)
|
let addressPromise = Promise<String?>(nil)
|
||||||
let venuesPromise = Promise<[TelegramMediaMap]?>(nil)
|
let venuesPromise = Promise<[TelegramMediaMap]?>(nil)
|
||||||
@ -480,7 +482,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
|
|||||||
return $0.avatar
|
return $0.avatar
|
||||||
}
|
}
|
||||||
if let _ = updatingAvatar {
|
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)
|
return mapResourceToAvatarSizes(postbox: context.account.postbox, resource: resource, representations: representations)
|
||||||
})
|
})
|
||||||
|> ignoreValues
|
|> ignoreValues
|
||||||
@ -569,12 +571,13 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
|
|||||||
endEditingImpl?()
|
endEditingImpl?()
|
||||||
presentControllerImpl?(legacyController, nil)
|
presentControllerImpl?(legacyController, nil)
|
||||||
|
|
||||||
let completedImpl: (UIImage) -> Void = { image in
|
let completedGroupPhotoImpl: (UIImage) -> Void = { image in
|
||||||
if let data = image.jpegData(compressionQuality: 0.6) {
|
if let data = image.jpegData(compressionQuality: 0.6) {
|
||||||
let resource = LocalFileMediaResource(fileId: arc4random64())
|
let resource = LocalFileMediaResource(fileId: arc4random64())
|
||||||
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
|
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
|
||||||
let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource)
|
let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource)
|
||||||
uploadedAvatar.set(uploadedPeerPhoto(postbox: context.account.postbox, network: context.account.network, resource: resource))
|
uploadedAvatar.set(uploadedPeerPhoto(postbox: context.account.postbox, network: context.account.network, resource: resource))
|
||||||
|
uploadedVideoAvatar = nil
|
||||||
updateState { current in
|
updateState { current in
|
||||||
var current = current
|
var current = current
|
||||||
current.avatar = .image(representation, false)
|
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 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)
|
let _ = currentAvatarMixin.swap(mixin)
|
||||||
mixin.requestSearchController = { assetsController in
|
mixin.requestSearchController = { assetsController in
|
||||||
let controller = WebSearchController(context: context, peer: peer, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: title, completion: { result in
|
let controller = WebSearchController(context: context, peer: peer, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: title, completion: { result in
|
||||||
assetsController?.dismiss()
|
assetsController?.dismiss()
|
||||||
completedImpl(result)
|
completedGroupPhotoImpl(result)
|
||||||
}))
|
}))
|
||||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
}
|
}
|
||||||
mixin.didFinishWithImage = { image in
|
mixin.didFinishWithImage = { image in
|
||||||
if let image = image {
|
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 {
|
if stateValue.with({ $0.avatar }) != nil {
|
||||||
|
@ -175,9 +175,11 @@ private final class PeerInfoScreenMemberItemNode: PeerInfoScreenItemNode {
|
|||||||
|
|
||||||
let itemHeight: ItemListPeerItemHeight
|
let itemHeight: ItemListPeerItemHeight
|
||||||
let itemText: ItemListPeerItemText
|
let itemText: ItemListPeerItemText
|
||||||
|
var synchronousLoads = false
|
||||||
if case .account = item.member {
|
if case .account = item.member {
|
||||||
itemHeight = .generic
|
itemHeight = .generic
|
||||||
itemText = .none
|
itemText = .none
|
||||||
|
synchronousLoads = true
|
||||||
} else {
|
} else {
|
||||||
itemHeight = .peerList
|
itemHeight = .peerList
|
||||||
itemText = .presence
|
itemText = .presence
|
||||||
@ -207,7 +209,7 @@ private final class PeerInfoScreenMemberItemNode: PeerInfoScreenItemNode {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
var itemNodeValue: ListViewItemNode?
|
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
|
itemNodeValue = node
|
||||||
apply().1(ListViewItemApply(isOnScreen: true))
|
apply().1(ListViewItemApply(isOnScreen: true))
|
||||||
})
|
})
|
||||||
|
@ -318,11 +318,14 @@ private func peerInfoScreenInputData(context: AccountContext, peerId: PeerId, is
|
|||||||
|
|
||||||
private func peerInfoProfilePhotos(context: AccountContext, peerId: PeerId) -> Signal<Any, NoError> {
|
private func peerInfoProfilePhotos(context: AccountContext, peerId: PeerId) -> Signal<Any, NoError> {
|
||||||
return context.account.postbox.combinedView(keys: [.basicPeer(peerId)])
|
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 {
|
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
|
|> distinctUntilChanged
|
||||||
|> mapToSignal { firstEntry -> Signal<[AvatarGalleryEntry], NoError> in
|
|> mapToSignal { firstEntry -> Signal<[AvatarGalleryEntry], NoError> in
|
||||||
|
@ -158,12 +158,12 @@ final class PeerInfoHeaderNavigationTransition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum PeerInfoAvatarListItem: Equatable {
|
enum PeerInfoAvatarListItem: Equatable {
|
||||||
case topImage([ImageRepresentationWithReference], Data?)
|
case topImage([ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], Data?)
|
||||||
case image(TelegramMediaImageReference?, [ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], Data?)
|
case image(TelegramMediaImageReference?, [ImageRepresentationWithReference], [TelegramMediaImage.VideoRepresentation], Data?)
|
||||||
|
|
||||||
var id: WrappedMediaResourceId {
|
var id: WrappedMediaResourceId {
|
||||||
switch self {
|
switch self {
|
||||||
case let .topImage(representations, _):
|
case let .topImage(representations, _, _):
|
||||||
let representation = largestImageRepresentation(representations.map { $0.representation }) ?? representations[representations.count - 1].representation
|
let representation = largestImageRepresentation(representations.map { $0.representation }) ?? representations[representations.count - 1].representation
|
||||||
return WrappedMediaResourceId(representation.resource.id)
|
return WrappedMediaResourceId(representation.resource.id)
|
||||||
case let .image(_, representations, _, _):
|
case let .image(_, representations, _, _):
|
||||||
@ -171,6 +171,15 @@ enum PeerInfoAvatarListItem: Equatable {
|
|||||||
return WrappedMediaResourceId(representation.resource.id)
|
return WrappedMediaResourceId(representation.resource.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var videoRepresentations: [TelegramMediaImage.VideoRepresentation] {
|
||||||
|
switch self {
|
||||||
|
case let .topImage(_, videoRepresentations, _):
|
||||||
|
return videoRepresentations
|
||||||
|
case let .image(_, _, videoRepresentations, _):
|
||||||
|
return videoRepresentations
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
||||||
@ -250,15 +259,15 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
|||||||
let immediateThumbnailData: Data?
|
let immediateThumbnailData: Data?
|
||||||
var id: Int64?
|
var id: Int64?
|
||||||
switch item {
|
switch item {
|
||||||
case let .topImage(topRepresentations, immediateThumbnail):
|
case let .topImage(topRepresentations, videoRepresentationsValue, immediateThumbnail):
|
||||||
representations = topRepresentations
|
representations = topRepresentations
|
||||||
videoRepresentations = []
|
videoRepresentations = videoRepresentationsValue
|
||||||
immediateThumbnailData = immediateThumbnail
|
immediateThumbnailData = immediateThumbnail
|
||||||
|
id = 1
|
||||||
case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail):
|
case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail):
|
||||||
representations = imageRepresentations
|
representations = imageRepresentations
|
||||||
videoRepresentations = videoRepresentationsValue
|
videoRepresentations = videoRepresentationsValue
|
||||||
immediateThumbnailData = immediateThumbnail
|
immediateThumbnailData = immediateThumbnail
|
||||||
|
|
||||||
if case let .cloud(imageId, _, _) = reference {
|
if case let .cloud(imageId, _, _) = reference {
|
||||||
id = imageId
|
id = imageId
|
||||||
}
|
}
|
||||||
@ -268,45 +277,44 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
|||||||
if let video = videoRepresentations.last, let id = id {
|
if let video = videoRepresentations.last, let id = id {
|
||||||
let mediaManager = self.context.sharedContext.mediaManager
|
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 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 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)
|
||||||
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 {
|
if videoContent.id != self.videoContent?.id {
|
||||||
self.playbackStatusDisposable.set((videoNode.status
|
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded)
|
||||||
|> map { status -> Bool in
|
videoNode.isUserInteractionEnabled = false
|
||||||
if let status = status, case .playing = status.status {
|
videoNode.isHidden = true
|
||||||
return true
|
|
||||||
} else {
|
if let _ = video.startTimestamp {
|
||||||
return false
|
self.playbackStatusDisposable.set((videoNode.status
|
||||||
}
|
|> map { status -> Bool in
|
||||||
}
|
if let status = status, case .playing = status.status {
|
||||||
|> filter { playing in
|
return true
|
||||||
return playing
|
} else {
|
||||||
}
|
return false
|
||||||
|> take(1)
|
|
||||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
|
||||||
if let strongSelf = self {
|
|
||||||
Queue.mainQueue().after(0.1) {
|
|
||||||
strongSelf.videoNode?.isHidden = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
|> filter { playing in
|
||||||
} else {
|
return playing
|
||||||
self.playbackStatusDisposable.set(nil)
|
}
|
||||||
videoNode.isHidden = false
|
|> 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 {
|
} else {
|
||||||
if let videoNode = self.videoNode {
|
if let videoNode = self.videoNode {
|
||||||
self.videoContent = nil
|
self.videoContent = nil
|
||||||
@ -762,9 +770,9 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
|||||||
var entries: [AvatarGalleryEntry] = []
|
var entries: [AvatarGalleryEntry] = []
|
||||||
for entry in self.galleryEntries {
|
for entry in self.galleryEntries {
|
||||||
switch entry {
|
switch entry {
|
||||||
case let .topImage(representations, _, immediateThumbnailData, _):
|
case let .topImage(representations, videoRepresentations, _, immediateThumbnailData, _):
|
||||||
entries.append(entry)
|
entries.append(entry)
|
||||||
items.append(.topImage(representations, immediateThumbnailData))
|
items.append(.topImage(representations, videoRepresentations, immediateThumbnailData))
|
||||||
case let .image(id, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _):
|
case let .image(id, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _):
|
||||||
if image.0 == reference {
|
if image.0 == reference {
|
||||||
entries.insert(entry, at: 0)
|
entries.insert(entry, at: 0)
|
||||||
@ -798,9 +806,9 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
|||||||
let previousIndex = self.currentIndex
|
let previousIndex = self.currentIndex
|
||||||
for entry in self.galleryEntries {
|
for entry in self.galleryEntries {
|
||||||
switch entry {
|
switch entry {
|
||||||
case let .topImage(representations, _, immediateThumbnailData, _):
|
case let .topImage(representations, videoRepresentations, _, immediateThumbnailData, _):
|
||||||
entries.append(entry)
|
entries.append(entry)
|
||||||
items.append(.topImage(representations, immediateThumbnailData))
|
items.append(.topImage(representations, videoRepresentations, immediateThumbnailData))
|
||||||
case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _):
|
case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _):
|
||||||
if image.0 != reference {
|
if image.0 != reference {
|
||||||
entries.append(entry)
|
entries.append(entry)
|
||||||
@ -862,8 +870,8 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
|||||||
var items: [PeerInfoAvatarListItem] = []
|
var items: [PeerInfoAvatarListItem] = []
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
switch entry {
|
switch entry {
|
||||||
case let .topImage(representations, _, immediateThumbnailData, _):
|
case let .topImage(representations, videoRepresentations, _, immediateThumbnailData, _):
|
||||||
items.append(.topImage(representations, immediateThumbnailData))
|
items.append(.topImage(representations, videoRepresentations, immediateThumbnailData))
|
||||||
case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _):
|
case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _):
|
||||||
items.append(.image(reference, representations, videoRepresentations, immediateThumbnailData))
|
items.append(.image(reference, representations, videoRepresentations, immediateThumbnailData))
|
||||||
}
|
}
|
||||||
@ -1035,6 +1043,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
var tapped: (() -> Void)?
|
var tapped: (() -> Void)?
|
||||||
|
|
||||||
private var isFirstAvatarLoading = true
|
private var isFirstAvatarLoading = true
|
||||||
|
var item: PeerInfoAvatarListItem?
|
||||||
|
|
||||||
private let playbackStatusDisposable = MetaDisposable()
|
private let playbackStatusDisposable = MetaDisposable()
|
||||||
|
|
||||||
@ -1090,11 +1099,22 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var removedPhotoResourceIds = Set<String>()
|
||||||
func update(peer: Peer?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool) {
|
func update(peer: Peer?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool) {
|
||||||
if let peer = peer {
|
if let peer = peer {
|
||||||
|
let previousItem = self.item
|
||||||
|
self.item = item
|
||||||
|
|
||||||
var overrideImage: AvatarNodeImageOverride?
|
var overrideImage: AvatarNodeImageOverride?
|
||||||
if peer.isDeleted {
|
if peer.isDeleted {
|
||||||
overrideImage = .deletedIcon
|
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.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.isFirstAvatarLoading = false
|
||||||
@ -1102,52 +1122,77 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
self.avatarNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize))
|
self.avatarNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize))
|
||||||
self.avatarNode.font = avatarPlaceholderFont(size: floor(avatarSize * 16.0 / 37.0))
|
self.avatarNode.font = avatarPlaceholderFont(size: floor(avatarSize * 16.0 / 37.0))
|
||||||
|
|
||||||
if let item = item, case let .image(reference, representations, videoRepresentations, immediateThumbnailData) = item, let video = videoRepresentations.last, case let .cloud(imageId, _, _) = reference {
|
if let item = item {
|
||||||
let id = imageId
|
let representations: [ImageRepresentationWithReference]
|
||||||
let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
|
let videoRepresentations: [TelegramMediaImage.VideoRepresentation]
|
||||||
let videoContent = NativeVideoContent(id: .profileVideo(id, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, autoFetchFullSizeThumbnail: true, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
|
let immediateThumbnailData: Data?
|
||||||
if videoContent.id != self.videoContent?.id {
|
var id: Int64?
|
||||||
let mediaManager = self.context.sharedContext.mediaManager
|
switch item {
|
||||||
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded)
|
case let .topImage(topRepresentations, videoRepresentationsValue, immediateThumbnail):
|
||||||
videoNode.isUserInteractionEnabled = false
|
representations = topRepresentations
|
||||||
videoNode.isHidden = true
|
videoRepresentations = videoRepresentationsValue
|
||||||
|
immediateThumbnailData = immediateThumbnail
|
||||||
|
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 startTimestamp = video.startTimestamp {
|
if let video = videoRepresentations.last, let id = id {
|
||||||
self.videoStartTimestamp = startTimestamp
|
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: [])]))
|
||||||
self.playbackStatusDisposable.set((videoNode.status
|
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)
|
||||||
|> map { status -> Bool in
|
if videoContent.id != self.videoContent?.id {
|
||||||
if let status = status, case .playing = status.status {
|
let mediaManager = self.context.sharedContext.mediaManager
|
||||||
return true
|
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded)
|
||||||
} else {
|
videoNode.isUserInteractionEnabled = false
|
||||||
return false
|
videoNode.isHidden = true
|
||||||
}
|
|
||||||
}
|
if let startTimestamp = video.startTimestamp {
|
||||||
|> filter { playing in
|
self.videoStartTimestamp = startTimestamp
|
||||||
return playing
|
self.playbackStatusDisposable.set((videoNode.status
|
||||||
}
|
|> map { status -> Bool in
|
||||||
|> take(1)
|
if let status = status, case .playing = status.status {
|
||||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
return true
|
||||||
if let strongSelf = self {
|
} else {
|
||||||
Queue.mainQueue().after(0.1) {
|
return false
|
||||||
strongSelf.videoNode?.isHidden = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
|> filter { playing in
|
||||||
} else {
|
return playing
|
||||||
self.videoStartTimestamp = nil
|
}
|
||||||
self.playbackStatusDisposable.set(nil)
|
|> take(1)
|
||||||
videoNode.isHidden = false
|
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||||
|
if let strongSelf = self {
|
||||||
|
Queue.mainQueue().after(0.15) {
|
||||||
|
strongSelf.videoNode?.isHidden = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
self.videoStartTimestamp = nil
|
||||||
|
self.playbackStatusDisposable.set(nil)
|
||||||
|
videoNode.isHidden = false
|
||||||
|
}
|
||||||
|
|
||||||
|
self.videoContent = videoContent
|
||||||
|
self.videoNode = videoNode
|
||||||
|
|
||||||
|
let maskPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size))
|
||||||
|
let shape = CAShapeLayer()
|
||||||
|
shape.path = maskPath.cgPath
|
||||||
|
videoNode.layer.mask = shape
|
||||||
|
|
||||||
|
self.addSubnode(videoNode)
|
||||||
}
|
}
|
||||||
|
} else if let videoNode = self.videoNode {
|
||||||
|
self.videoContent = nil
|
||||||
|
self.videoNode = nil
|
||||||
|
|
||||||
self.videoContent = videoContent
|
videoNode.removeFromSupernode()
|
||||||
self.videoNode = videoNode
|
|
||||||
|
|
||||||
let maskPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size))
|
|
||||||
let shape = CAShapeLayer()
|
|
||||||
shape.path = maskPath.cgPath
|
|
||||||
videoNode.layer.mask = shape
|
|
||||||
|
|
||||||
self.addSubnode(videoNode)
|
|
||||||
}
|
}
|
||||||
} else if let videoNode = self.videoNode {
|
} else if let videoNode = self.videoNode {
|
||||||
self.videoContent = nil
|
self.videoContent = nil
|
||||||
@ -1206,13 +1251,14 @@ final class PeerInfoEditingAvatarOverlayNode: ASDisplayNode {
|
|||||||
self.updatingAvatarOverlay = ASImageNode()
|
self.updatingAvatarOverlay = ASImageNode()
|
||||||
self.updatingAvatarOverlay.displayWithoutProcessing = true
|
self.updatingAvatarOverlay.displayWithoutProcessing = true
|
||||||
self.updatingAvatarOverlay.displaysAsynchronously = false
|
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 = RadialStatusNode(backgroundNodeColor: UIColor(rgb: 0x000000, alpha: 0.6))
|
||||||
self.statusNode.isUserInteractionEnabled = false
|
self.statusNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
self.iconNode = ASImageNode()
|
self.iconNode = ASImageNode()
|
||||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Avatar/EditAvatarIconLarge"), color: .white)
|
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Avatar/EditAvatarIconLarge"), color: .white)
|
||||||
|
self.iconNode.alpha = 0.0
|
||||||
|
|
||||||
super.init()
|
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.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
|
self.updatingAvatarOverlay.frame = self.imageNode.frame
|
||||||
|
|
||||||
|
let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .linear)
|
||||||
|
|
||||||
if canEditPeerInfo(context: self.context, peer: peer) {
|
if canEditPeerInfo(context: self.context, peer: peer) {
|
||||||
var overlayHidden = false
|
var overlayHidden = false
|
||||||
var iconHidden = false
|
var iconHidden = false
|
||||||
@ -1248,7 +1296,7 @@ final class PeerInfoEditingAvatarOverlayNode: ASDisplayNode {
|
|||||||
overlayHidden = false
|
overlayHidden = false
|
||||||
iconHidden = true
|
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 case let .image(representation) = updatingAvatar {
|
||||||
if representation != self.currentRepresentation {
|
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 {
|
} else {
|
||||||
if isEditing {
|
if isEditing {
|
||||||
iconHidden = false
|
iconHidden = peer.profileImageRepresentations.isEmpty
|
||||||
|
overlayHidden = peer.profileImageRepresentations.isEmpty
|
||||||
} else {
|
} else {
|
||||||
iconHidden = true
|
iconHidden = true
|
||||||
overlayHidden = true
|
overlayHidden = true
|
||||||
}
|
}
|
||||||
Queue.mainQueue().after(0.1) { [weak self] in
|
Queue.mainQueue().after(0.3) { [weak self] in
|
||||||
self?.statusNode.transitionToState(.none)
|
guard let strongSelf = self else {
|
||||||
self?.currentRepresentation = nil
|
return
|
||||||
self?.imageNode.setSignal(.single(nil))
|
}
|
||||||
self?.iconNode.isHidden = iconHidden
|
strongSelf.statusNode.transitionToState(.none)
|
||||||
self?.updatingAvatarOverlay.isHidden = overlayHidden
|
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 {
|
if !overlayHidden && self.updatingAvatarOverlay.image == nil {
|
||||||
@ -1280,10 +1333,9 @@ final class PeerInfoEditingAvatarOverlayNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.statusNode.transitionToState(.none)
|
self.statusNode.transitionToState(.none)
|
||||||
self.iconNode.isHidden = true
|
|
||||||
self.updatingAvatarOverlay.isHidden = true
|
|
||||||
self.currentRepresentation = nil
|
self.currentRepresentation = nil
|
||||||
|
transition.updateAlpha(node: self.iconNode, alpha: 0.0)
|
||||||
|
transition.updateAlpha(node: self.updatingAvatarOverlay, alpha: 0.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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.setPeer(context: self.context, theme: theme, peer: peer, overrideImage: overrideImage, synchronousLoad: false, displayDimensions: CGSize(width: avatarSize, height: avatarSize))
|
||||||
self.avatarNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize))
|
self.avatarNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize))
|
||||||
|
|
||||||
if let item = item, case let .image(reference, representations, videoRepresentations, immediateThumbnailData) = item, let video = videoRepresentations.last, case let .cloud(imageId, _, _) = reference {
|
if let item = item {
|
||||||
let id = imageId
|
let representations: [ImageRepresentationWithReference]
|
||||||
let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
|
let videoRepresentations: [TelegramMediaImage.VideoRepresentation]
|
||||||
let videoContent = NativeVideoContent(id: .profileVideo(id, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, autoFetchFullSizeThumbnail: true, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
|
let immediateThumbnailData: Data?
|
||||||
if videoContent.id != self.videoContent?.id {
|
var id: Int64?
|
||||||
let mediaManager = self.context.sharedContext.mediaManager
|
switch item {
|
||||||
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .overlay)
|
case let .topImage(topRepresentations, videoRepresentationsValue, immediateThumbnail):
|
||||||
videoNode.isUserInteractionEnabled = false
|
representations = topRepresentations
|
||||||
videoNode.ownsContentNodeUpdated = { [weak self] owns in
|
videoRepresentations = videoRepresentationsValue
|
||||||
if let strongSelf = self {
|
immediateThumbnailData = immediateThumbnail
|
||||||
strongSelf.videoNode?.isHidden = !owns
|
id = 1
|
||||||
}
|
case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail):
|
||||||
|
representations = imageRepresentations
|
||||||
|
videoRepresentations = videoRepresentationsValue
|
||||||
|
immediateThumbnailData = immediateThumbnail
|
||||||
|
if case let .cloud(imageId, _, _) = reference {
|
||||||
|
id = imageId
|
||||||
}
|
}
|
||||||
self.videoContent = videoContent
|
}
|
||||||
self.videoNode = videoNode
|
|
||||||
|
|
||||||
let maskPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size))
|
if let video = videoRepresentations.last, let id = id {
|
||||||
let shape = CAShapeLayer()
|
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: [])]))
|
||||||
shape.path = maskPath.cgPath
|
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)
|
||||||
videoNode.layer.mask = shape
|
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
|
||||||
|
|
||||||
self.insertSubnode(videoNode, aboveSubnode: self.avatarNode)
|
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
|
||||||
|
|
||||||
|
videoNode.removeFromSupernode()
|
||||||
}
|
}
|
||||||
} else if let videoNode = self.videoNode {
|
} else if let videoNode = self.videoNode {
|
||||||
self.videoContent = nil
|
self.videoContent = nil
|
||||||
@ -1393,6 +1470,8 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
|
|||||||
var arguments: (Peer?, PresentationTheme, CGFloat, Bool)?
|
var arguments: (Peer?, PresentationTheme, CGFloat, Bool)?
|
||||||
var item: PeerInfoAvatarListItem?
|
var item: PeerInfoAvatarListItem?
|
||||||
|
|
||||||
|
var itemsUpdated: (([PeerInfoAvatarListItem]) -> Void)?
|
||||||
|
|
||||||
init(context: AccountContext, readyWhenGalleryLoads: Bool) {
|
init(context: AccountContext, readyWhenGalleryLoads: Bool) {
|
||||||
self.avatarContainerNode = PeerInfoAvatarTransformContainerNode(context: context)
|
self.avatarContainerNode = PeerInfoAvatarTransformContainerNode(context: context)
|
||||||
self.listContainerTransformNode = ASDisplayNode()
|
self.listContainerTransformNode = ASDisplayNode()
|
||||||
@ -1440,6 +1519,7 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
|
|||||||
self.listContainerNode.itemsUpdated = { [weak self] items in
|
self.listContainerNode.itemsUpdated = { [weak self] items in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.item = items.first
|
strongSelf.item = items.first
|
||||||
|
strongSelf.itemsUpdated?(items)
|
||||||
if let (peer, theme, avatarSize, isExpanded) = strongSelf.arguments {
|
if let (peer, theme, avatarSize, isExpanded) = strongSelf.arguments {
|
||||||
strongSelf.avatarContainerNode.update(peer: peer, item: strongSelf.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded)
|
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 {
|
final class PeerInfoHeaderNode: ASDisplayNode {
|
||||||
private var context: AccountContext
|
private var context: AccountContext
|
||||||
private var presentationData: PresentationData?
|
private var presentationData: PresentationData?
|
||||||
|
private var state: PeerInfoState?
|
||||||
|
private var peer: Peer?
|
||||||
|
private var avatarSize: CGFloat?
|
||||||
|
|
||||||
private let isOpenedFromChat: Bool
|
private let isOpenedFromChat: Bool
|
||||||
private let isSettings: Bool
|
private let isSettings: Bool
|
||||||
@ -2218,6 +2301,10 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
private let backgroundNode: ASDisplayNode
|
private let backgroundNode: ASDisplayNode
|
||||||
private let expandedBackgroundNode: ASDisplayNode
|
private let expandedBackgroundNode: ASDisplayNode
|
||||||
let separatorNode: ASDisplayNode
|
let separatorNode: ASDisplayNode
|
||||||
|
let navigationBackgroundNode: ASDisplayNode
|
||||||
|
var navigationTitle: String?
|
||||||
|
let navigationTitleNode: ImmediateTextNode
|
||||||
|
let navigationSeparatorNode: ASDisplayNode
|
||||||
let navigationButtonContainer: PeerInfoHeaderNavigationButtonContainerNode
|
let navigationButtonContainer: PeerInfoHeaderNavigationButtonContainerNode
|
||||||
|
|
||||||
var performButtonAction: ((PeerInfoHeaderButtonKey) -> Void)?
|
var performButtonAction: ((PeerInfoHeaderButtonKey) -> Void)?
|
||||||
@ -2272,6 +2359,13 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
|
|
||||||
self.avatarOverlayNode = PeerInfoEditingAvatarOverlayNode(context: context)
|
self.avatarOverlayNode = PeerInfoEditingAvatarOverlayNode(context: context)
|
||||||
|
|
||||||
|
self.navigationBackgroundNode = ASDisplayNode()
|
||||||
|
self.navigationBackgroundNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
self.navigationTitleNode = ImmediateTextNode()
|
||||||
|
|
||||||
|
self.navigationSeparatorNode = ASDisplayNode()
|
||||||
|
|
||||||
self.navigationButtonContainer = PeerInfoHeaderNavigationButtonContainerNode()
|
self.navigationButtonContainer = PeerInfoHeaderNavigationButtonContainerNode()
|
||||||
|
|
||||||
self.backgroundNode = ASDisplayNode()
|
self.backgroundNode = ASDisplayNode()
|
||||||
@ -2303,6 +2397,9 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
self.addSubnode(self.regularContentNode)
|
self.addSubnode(self.regularContentNode)
|
||||||
self.addSubnode(self.editingContentNode)
|
self.addSubnode(self.editingContentNode)
|
||||||
self.addSubnode(self.avatarOverlayNode)
|
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.navigationButtonContainer)
|
||||||
self.addSubnode(self.separatorNode)
|
self.addSubnode(self.separatorNode)
|
||||||
|
|
||||||
@ -2310,10 +2407,14 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
self?.initiateAvatarExpansion()
|
self?.initiateAvatarExpansion()
|
||||||
}
|
}
|
||||||
self.editingContentNode.avatarNode.tapped = { [weak self] confirm in
|
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
|
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 {
|
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
|
var contentOffset = contentOffset
|
||||||
|
|
||||||
if isMediaOnly {
|
if isMediaOnly {
|
||||||
@ -2446,7 +2553,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
self.avatarListNode.animateAvatarCollapse(transition: transition)
|
self.avatarListNode.animateAvatarCollapse(transition: transition)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let avatarSize: CGFloat = isModalOverlay ? 200.0 : 100.0
|
|
||||||
let backgroundTransitionFraction: CGFloat = max(0.0, min(1.0, contentOffset / (112.0 + avatarSize)))
|
let backgroundTransitionFraction: CGFloat = max(0.0, min(1.0, contentOffset / (112.0 + avatarSize)))
|
||||||
transition.updateAlpha(node: self.expandedBackgroundNode, alpha: backgroundTransitionFraction)
|
transition.updateAlpha(node: self.expandedBackgroundNode, alpha: backgroundTransitionFraction)
|
||||||
}
|
}
|
||||||
@ -2454,6 +2560,18 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
self.avatarListNode.avatarContainerNode.updateTransitionFraction(transitionFraction, transition: transition)
|
self.avatarListNode.avatarContainerNode.updateTransitionFraction(transitionFraction, transition: transition)
|
||||||
self.avatarListNode.listContainerNode.currentItemNode?.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
|
self.separatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||||
|
|
||||||
let defaultButtonSize: CGFloat = 40.0
|
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))
|
TitleNodeStateExpanded: MultiScaleTextState(attributedText: usernameString, constrainedSize: CGSize(width: width - titleNodeLayout[TitleNodeStateExpanded]!.size.width - 8.0, height: titleConstrainedSize.height))
|
||||||
], mainState: TitleNodeStateRegular)
|
], 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 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)
|
let avatarCenter = CGPoint(x: (1.0 - transitionFraction) * avatarFrame.midX + transitionFraction * transitionSourceAvatarFrame.midX, y: (1.0 - transitionFraction) * avatarFrame.midY + transitionFraction * transitionSourceAvatarFrame.midY)
|
||||||
|
|
||||||
|
@ -2086,7 +2086,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}
|
}
|
||||||
|
|
||||||
let entriesPromise = Promise<[AvatarGalleryEntry]>(entries)
|
let entriesPromise = Promise<[AvatarGalleryEntry]>(entries)
|
||||||
let galleryController = AvatarGalleryController(context: strongSelf.context, peer: peer, sourceHasRoundCorners: !strongSelf.headerNode.isAvatarExpanded, remoteEntries: entriesPromise, centralEntryIndex: centralEntry.flatMap { entries.firstIndex(of: $0) }, replaceRootController: { controller, ready in
|
let galleryController = AvatarGalleryController(context: strongSelf.context, peer: peer, sourceHasRoundCorners: !strongSelf.headerNode.isAvatarExpanded, remoteEntries: entriesPromise, skipInitial: true, centralEntryIndex: centralEntry.flatMap { entries.firstIndex(of: $0) }, replaceRootController: { controller, ready in
|
||||||
})
|
})
|
||||||
galleryController.openAvatarSetup = { [weak self] completion in
|
galleryController.openAvatarSetup = { [weak self] completion in
|
||||||
self?.openAvatarForEditing(hasRemove: false, completion: completion)
|
self?.openAvatarForEditing(hasRemove: false, completion: completion)
|
||||||
@ -2535,7 +2535,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.headerNode.avatarListNode.listContainerNode.currentIndexUpdated = { [weak self] in
|
self.headerNode.avatarListNode.listContainerNode.currentIndexUpdated = { [weak self] in
|
||||||
self?.updateNavigation(transition: .immediate, additive: false)
|
self?.updateNavigation(transition: .immediate, additive: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.dataDisposable = (screenData
|
self.dataDisposable = (screenData
|
||||||
@ -4067,6 +4067,10 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
|
account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let timestamp = videoStartTimestamp {
|
||||||
|
videoStartTimestamp = max(0.0, min(timestamp, result.duration))
|
||||||
|
}
|
||||||
|
|
||||||
var value = stat()
|
var value = stat()
|
||||||
if stat(result.fileURL.path, &value) == 0 {
|
if stat(result.fileURL.path, &value) == 0 {
|
||||||
if let data = try? Data(contentsOf: result.fileURL) {
|
if let data = try? Data(contentsOf: result.fileURL) {
|
||||||
@ -5415,11 +5419,19 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
guard let (_, navigationHeight) = self.validLayout else {
|
guard let (_, navigationHeight) = self.validLayout else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var height: CGFloat = self.isSettings ? 140.0 : 212.0
|
if self.state.isEditing && self.isSettings {
|
||||||
if self.headerNode.twoLineInfo {
|
if targetContentOffset.pointee.y < navigationHeight {
|
||||||
height += 17.0
|
if targetContentOffset.pointee.y < navigationHeight / 2.0 {
|
||||||
}
|
targetContentOffset.pointee.y = 0.0
|
||||||
if !self.state.isEditing {
|
} 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 {
|
||||||
if targetContentOffset.pointee.y < height / 2.0 {
|
if targetContentOffset.pointee.y < height / 2.0 {
|
||||||
targetContentOffset.pointee.y = 0.0
|
targetContentOffset.pointee.y = 0.0
|
||||||
|
@ -43,8 +43,12 @@ final class GenericEmbedImplementation: WebEmbedImplementation {
|
|||||||
self.onPlaybackStarted = onPlaybackStarted
|
self.onPlaybackStarted = onPlaybackStarted
|
||||||
updateStatus(self.status)
|
updateStatus(self.status)
|
||||||
|
|
||||||
let html = String(format: htmlTemplate, self.url)
|
if self.url.contains("player.twitch.tv/"), let url = URL(string: self.url) {
|
||||||
webView.loadHTMLString(html, baseURL: URL(string: "about:blank"))
|
webView.load(URLRequest(url: url))
|
||||||
|
} else {
|
||||||
|
let html = String(format: htmlTemplate, self.url)
|
||||||
|
webView.loadHTMLString(html, baseURL: URL(string: "about:blank"))
|
||||||
|
}
|
||||||
|
|
||||||
userContentController.addUserScript(WKUserScript(source: userScript, injectionTime: .atDocumentEnd, forMainFrameOnly: false))
|
userContentController.addUserScript(WKUserScript(source: userScript, injectionTime: .atDocumentEnd, forMainFrameOnly: false))
|
||||||
|
|
||||||
|
@ -32,11 +32,12 @@ public final class NativeVideoContent: UniversalVideoContent {
|
|||||||
let fetchAutomatically: Bool
|
let fetchAutomatically: Bool
|
||||||
let onlyFullSizeThumbnail: Bool
|
let onlyFullSizeThumbnail: Bool
|
||||||
let autoFetchFullSizeThumbnail: Bool
|
let autoFetchFullSizeThumbnail: Bool
|
||||||
|
let startTimestamp: Double?
|
||||||
let continuePlayingWithoutSoundOnLostAudioSession: Bool
|
let continuePlayingWithoutSoundOnLostAudioSession: Bool
|
||||||
let placeholderColor: UIColor
|
let placeholderColor: UIColor
|
||||||
let tempFilePath: String?
|
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.id = id
|
||||||
self.nativeId = id
|
self.nativeId = id
|
||||||
self.fileReference = fileReference
|
self.fileReference = fileReference
|
||||||
@ -62,13 +63,14 @@ public final class NativeVideoContent: UniversalVideoContent {
|
|||||||
self.fetchAutomatically = fetchAutomatically
|
self.fetchAutomatically = fetchAutomatically
|
||||||
self.onlyFullSizeThumbnail = onlyFullSizeThumbnail
|
self.onlyFullSizeThumbnail = onlyFullSizeThumbnail
|
||||||
self.autoFetchFullSizeThumbnail = autoFetchFullSizeThumbnail
|
self.autoFetchFullSizeThumbnail = autoFetchFullSizeThumbnail
|
||||||
|
self.startTimestamp = startTimestamp
|
||||||
self.continuePlayingWithoutSoundOnLostAudioSession = continuePlayingWithoutSoundOnLostAudioSession
|
self.continuePlayingWithoutSoundOnLostAudioSession = continuePlayingWithoutSoundOnLostAudioSession
|
||||||
self.placeholderColor = placeholderColor
|
self.placeholderColor = placeholderColor
|
||||||
self.tempFilePath = tempFilePath
|
self.tempFilePath = tempFilePath
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeContentNode(postbox: Postbox, audioSession: ManagedAudioSession) -> UniversalVideoContentNode & ASDisplayNode {
|
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 {
|
public func isEqual(to other: UniversalVideoContent) -> Bool {
|
||||||
@ -141,7 +143,7 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
|
|||||||
|
|
||||||
private var shouldPlay: Bool = false
|
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.postbox = postbox
|
||||||
self.fileReference = fileReference
|
self.fileReference = fileReference
|
||||||
self.placeholderColor = placeholderColor
|
self.placeholderColor = placeholderColor
|
||||||
@ -226,6 +228,10 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
|
|||||||
self.imageNode.imageUpdated = { [weak self] _ in
|
self.imageNode.imageUpdated = { [weak self] _ in
|
||||||
self?._ready.set(.single(Void()))
|
self?._ready.set(.single(Void()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let startTimestamp = startTimestamp {
|
||||||
|
self.seek(startTimestamp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user