Merge branch 'todo' of gitlab.com:peter-iakovlev/telegram-ios into todo

This commit is contained in:
Ilya Laktyushin 2025-06-06 09:56:11 +02:00
commit 562c621ec4
21 changed files with 267 additions and 86 deletions

View File

@ -14383,7 +14383,7 @@ Sorry for the inconvenience.";
"ChannelMessages.PriceSectionTitle" = "PRICE FOR EACH MESSAGE"; "ChannelMessages.PriceSectionTitle" = "PRICE FOR EACH MESSAGE";
"ChannelMessages.PriceSectionFooter" = "You will receive 85% of the selected fee for each incoming message."; "ChannelMessages.PriceSectionFooter" = "You will receive 85% of the selected fee for each incoming message.";
"ChatList.MonoforumLabel" = "MESSAGES"; "ChatList.MonoforumLabel" = "DIRECT";
"ChatList.MonoforumEmptyText" = "No messages here yet..."; "ChatList.MonoforumEmptyText" = "No messages here yet...";
"Chat.InlineTopicMenu.Reorder" = "Reorder"; "Chat.InlineTopicMenu.Reorder" = "Reorder";
@ -14400,6 +14400,7 @@ Sorry for the inconvenience.";
"Chat.EmptyStateMonoforum.Text" = "Send a direct message to the administrator of **%@**."; "Chat.EmptyStateMonoforum.Text" = "Send a direct message to the administrator of **%@**.";
"Chat.EmptyStateMonoforumPaid.Text" = "**%1$@** charges **%2$@**\nper message to its admin."; "Chat.EmptyStateMonoforumPaid.Text" = "**%1$@** charges **%2$@**\nper message to its admin.";
"Chat.Monoforum.Subtitle" = "Direct messages";
"Monoforum.NameFormat" = "%@ Messages"; "Monoforum.NameFormat" = "%@ Messages";
@ -14409,3 +14410,5 @@ Sorry for the inconvenience.";
"Stars.SendMessage.AdjustmentSectionFooterValue" = "You will receive **%@ Stars**."; "Stars.SendMessage.AdjustmentSectionFooterValue" = "You will receive **%@ Stars**.";
"Stars.SendMessage.AdjustmentSectionFooterEmpty" = "You will receive **80%**."; "Stars.SendMessage.AdjustmentSectionFooterEmpty" = "You will receive **80%**.";
"Stars.SendMessage.AdjustmentAction" = "OK"; "Stars.SendMessage.AdjustmentAction" = "OK";
"Stars.SendMessage.PriceFree" = "Free";

View File

@ -97,22 +97,14 @@ open class TransformImageNode: ASDisplayNode {
self.disposable.set((result |> deliverOnMainQueue).start(next: { [weak self] next in self.disposable.set((result |> deliverOnMainQueue).start(next: { [weak self] next in
let apply: () -> Void = { let apply: () -> Void = {
if let strongSelf = self { if let strongSelf = self {
var animateFromContents: Any?
if strongSelf.contents == nil { if strongSelf.contents == nil {
if strongSelf.contentAnimations.contains(.firstUpdate) && !attemptSynchronously { if strongSelf.contentAnimations.contains(.firstUpdate) && !attemptSynchronously {
strongSelf.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) strongSelf.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
} }
} else if strongSelf.contentAnimations.contains(.subsequentUpdates) { } else if strongSelf.contentAnimations.contains(.subsequentUpdates) {
let tempLayer = CALayer() animateFromContents = strongSelf.contents
if strongSelf.captureProtected {
setLayerDisableScreenshots(tempLayer, strongSelf.captureProtected)
}
tempLayer.frame = strongSelf.bounds
tempLayer.contentsGravity = strongSelf.layer.contentsGravity
tempLayer.contents = strongSelf.contents
strongSelf.layer.addSublayer(tempLayer)
tempLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak tempLayer] _ in
tempLayer?.removeFromSuperlayer()
})
} }
var imageUpdate: UIImage? var imageUpdate: UIImage?
@ -129,6 +121,23 @@ open class TransformImageNode: ASDisplayNode {
if let imageUpdated = strongSelf.imageUpdated { if let imageUpdated = strongSelf.imageUpdated {
imageUpdated(imageUpdate) imageUpdated(imageUpdate)
} }
if let animateFromContents {
let transition: ContainedViewLayoutTransition = .animated(duration: 0.2, curve: .linear)
transition.animateContents(layer: strongSelf.layer, from: animateFromContents)
/*let tempLayer = CALayer()
if strongSelf.captureProtected {
setLayerDisableScreenshots(tempLayer, strongSelf.captureProtected)
}
tempLayer.frame = strongSelf.bounds
tempLayer.contentsGravity = strongSelf.layer.contentsGravity
tempLayer.contents = animateFromContents
strongSelf.layer.addSublayer(tempLayer)
tempLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak tempLayer] _ in
tempLayer?.removeFromSuperlayer()
})*/
}
} }
} }
if dispatchOnDisplayLink && !attemptSynchronously { if dispatchOnDisplayLink && !attemptSynchronously {
@ -168,6 +177,34 @@ open class TransformImageNode: ASDisplayNode {
} }
} }
public func asyncLayoutWithAnimation() -> (TransformImageArguments) -> ((ListViewItemUpdateAnimation) -> Void) {
let currentTransform = self.currentTransform
let currentArguments = self.currentArguments
return { [weak self] arguments in
let updatedImage: UIImage?
if currentArguments != arguments {
updatedImage = currentTransform?(arguments)?.generateImage()
} else {
updatedImage = nil
}
return { animation in
guard let self else {
return
}
if let image = updatedImage {
self.contents = image.cgImage
self.image = image
self.currentArguments = arguments
if let _ = self.overlayColor {
self.applyOverlayColor(animated: false)
}
}
self.argumentsPromise.set(arguments)
}
}
}
public class func asyncLayout(_ maybeNode: TransformImageNode?) -> (TransformImageArguments) -> (() -> TransformImageNode) { public class func asyncLayout(_ maybeNode: TransformImageNode?) -> (TransformImageArguments) -> (() -> TransformImageNode) {
return { arguments in return { arguments in
let node: TransformImageNode let node: TransformImageNode

View File

@ -402,7 +402,7 @@ public final class MediaPlayerNode: ASDisplayNode {
} }
} }
private func updateLayout() { public func updateLayout() {
let bounds = self.bounds let bounds = self.bounds
if bounds.isEmpty { if bounds.isEmpty {
return return

View File

@ -313,6 +313,7 @@ final class MutableMessageHistoryView: MutablePostboxView {
private let clipHoles: Bool private let clipHoles: Bool
private let trackHoles: Bool private let trackHoles: Bool
private let anchor: HistoryViewInputAnchor private let anchor: HistoryViewInputAnchor
fileprivate var mapReadStatesFromThreads: Bool = false
fileprivate var combinedReadStates: MessageHistoryViewReadState? fileprivate var combinedReadStates: MessageHistoryViewReadState?
fileprivate var transientReadStates: MessageHistoryViewReadState? fileprivate var transientReadStates: MessageHistoryViewReadState?
@ -322,6 +323,7 @@ final class MutableMessageHistoryView: MutablePostboxView {
fileprivate var topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?] fileprivate var topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?]
fileprivate var additionalDatas: [AdditionalMessageHistoryViewDataEntry] fileprivate var additionalDatas: [AdditionalMessageHistoryViewDataEntry]
fileprivate var threadMaxOutgoingReadId: [Int64: Int32] = [:]
fileprivate(set) var sampledState: HistoryViewSample fileprivate(set) var sampledState: HistoryViewSample
@ -373,8 +375,14 @@ final class MutableMessageHistoryView: MutablePostboxView {
switch peerIds { switch peerIds {
case let .associated(peerId, _): case let .associated(peerId, _):
self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: peerId) != nil self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: peerId) != nil
case let .single(peerId, _): case let .single(peerId, threadId):
self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: peerId) != nil self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: peerId) != nil
if threadId == nil, let peer = postbox.peerTable.get(peerId) {
let value = postbox.seedConfiguration.peerSummaryIsThreadBased(peer, peer.associatedPeerId.flatMap(postbox.peerTable.get))
if value.value && value.threadsArePeers {
self.mapReadStatesFromThreads = true
}
}
case let .external(input): case let .external(input):
switch input.content { switch input.content {
case let .thread(peerId, _, _): case let .thread(peerId, _, _):
@ -401,6 +409,8 @@ final class MutableMessageHistoryView: MutablePostboxView {
self.render(postbox: postbox) self.render(postbox: postbox)
let _ = self.updateStoryStats(postbox: postbox) let _ = self.updateStoryStats(postbox: postbox)
let _ = self.updateThreadInfos(postbox: postbox, updatedIds: nil)
} }
private func reset(postbox: PostboxImpl) { private func reset(postbox: PostboxImpl) {
@ -1023,9 +1033,81 @@ final class MutableMessageHistoryView: MutablePostboxView {
} }
} }
if !transaction.updatedPeerThreadInfos.isEmpty {
if self.updateThreadInfos(postbox: postbox, updatedIds: transaction.updatedPeerThreadInfos) {
hasChanges = true
}
}
return hasChanges return hasChanges
} }
private func updateThreadInfos(postbox: PostboxImpl, updatedIds: Set<MessageHistoryThreadsTable.ItemId>?) -> Bool {
if self.mapReadStatesFromThreads, case let .single(peerId, peerThreadId) = self.peerIds, peerThreadId == nil {
switch self.sampledState {
case .loading:
if !self.threadMaxOutgoingReadId.isEmpty {
self.threadMaxOutgoingReadId.removeAll()
return true
} else {
return false
}
case let .loaded(loaded):
//TODO:release
let currentIds = Set<Int64>(self.threadMaxOutgoingReadId.keys)
var threadIds: Set<Int64> = []
for entry in loaded.entries {
if let threadId = entry.message.threadId {
threadIds.insert(threadId)
}
}
var updated = false
if currentIds != threadIds {
updated = true
for id in currentIds.subtracting(threadIds) {
self.threadMaxOutgoingReadId.removeValue(forKey: id)
}
}
for threadId in threadIds {
var fetch = false
if let updatedIds {
if updatedIds.contains(MessageHistoryThreadsTable.ItemId(peerId: peerId, threadId: threadId)) {
fetch = true
}
} else {
fetch = self.threadMaxOutgoingReadId[threadId] == nil
}
if fetch {
var maxOutgoingReadId: Int32?
if let threadData = postbox.messageHistoryThreadIndexTable.get(peerId: peerId, threadId: threadId) {
maxOutgoingReadId = threadData.summary.maxOutgoingReadId
}
let current = self.threadMaxOutgoingReadId[threadId]
if let maxOutgoingReadId {
if current != maxOutgoingReadId {
self.threadMaxOutgoingReadId[threadId] = maxOutgoingReadId
updated = true
}
} else if current != nil {
self.threadMaxOutgoingReadId.removeValue(forKey: threadId)
updated = true
}
}
}
return updated
}
} else {
return false
}
}
private func render(postbox: PostboxImpl) { private func render(postbox: PostboxImpl) {
for namespace in self.topTaggedMessages.keys { for namespace in self.topTaggedMessages.keys {
if let entry = self.topTaggedMessages[namespace]!, case let .intermediate(message) = entry { if let entry = self.topTaggedMessages[namespace]!, case let .intermediate(message) = entry {
@ -1166,7 +1248,20 @@ public final class MessageHistoryView: PostboxView {
} }
self.isLoadingEarlier = isLoadingEarlier self.isLoadingEarlier = isLoadingEarlier
entries = [] entries = []
if let transientReadStates = mutableView.transientReadStates, case let .peer(states) = transientReadStates { if mutableView.mapReadStatesFromThreads {
for entry in state.entries {
if mutableView.namespaces.contains(entry.message.id.namespace) {
var read = false
if let threadId = entry.message.threadId {
if let maxId = mutableView.threadMaxOutgoingReadId[threadId] {
read = entry.message.id.id <= maxId
}
}
entries.append(MessageHistoryEntry(message: entry.message, isRead: read, location: entry.location, monthLocation: entry.monthLocation, attributes: entry.attributes))
}
}
} else if let transientReadStates = mutableView.transientReadStates, case let .peer(states) = transientReadStates {
for entry in state.entries { for entry in state.entries {
if mutableView.namespaces.contains(entry.message.id.namespace) { if mutableView.namespaces.contains(entry.message.id.namespace) {
let read: Bool let read: Bool

View File

@ -1308,6 +1308,7 @@ final class HistoryViewLoadedState {
let halfLimit: Int let halfLimit: Int
let seedConfiguration: SeedConfiguration let seedConfiguration: SeedConfiguration
var orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHistoryViewEntries] var orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHistoryViewEntries]
var threadSummaries: [Int64: Int32]
var holes: HistoryViewHoles var holes: HistoryViewHoles
var spacesWithRemovals = Set<PeerIdAndNamespace>() var spacesWithRemovals = Set<PeerIdAndNamespace>()
@ -1321,6 +1322,7 @@ final class HistoryViewLoadedState {
self.halfLimit = halfLimit self.halfLimit = halfLimit
self.seedConfiguration = postbox.seedConfiguration self.seedConfiguration = postbox.seedConfiguration
self.orderedEntriesBySpace = [:] self.orderedEntriesBySpace = [:]
self.threadSummaries = [:]
self.holes = holes self.holes = holes
var peerIds: [PeerId] = [] var peerIds: [PeerId] = []
@ -1854,6 +1856,7 @@ final class HistoryViewLoadedState {
var holesToLower = false var holesToLower = false
var holesToHigher = false var holesToHigher = false
var result: [MessageHistoryMessageEntry] = [] var result: [MessageHistoryMessageEntry] = []
if combinedSpacesAndIndicesByDirection.lowerOrAtAnchor.isEmpty && combinedSpacesAndIndicesByDirection.higherThanAnchor.isEmpty { if combinedSpacesAndIndicesByDirection.lowerOrAtAnchor.isEmpty && combinedSpacesAndIndicesByDirection.higherThanAnchor.isEmpty {
if !clipRanges.isEmpty { if !clipRanges.isEmpty {
holesToLower = true holesToLower = true

View File

@ -559,7 +559,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-808853502] = { return Api.MediaAreaCoordinates.parse_mediaAreaCoordinates($0) } dict[-808853502] = { return Api.MediaAreaCoordinates.parse_mediaAreaCoordinates($0) }
dict[-356721331] = { return Api.Message.parse_message($0) } dict[-356721331] = { return Api.Message.parse_message($0) }
dict[-1868117372] = { return Api.Message.parse_messageEmpty($0) } dict[-1868117372] = { return Api.Message.parse_messageEmpty($0) }
dict[-741178048] = { return Api.Message.parse_messageService($0) } dict[2055212554] = { return Api.Message.parse_messageService($0) }
dict[-872240531] = { return Api.MessageAction.parse_messageActionBoostApply($0) } dict[-872240531] = { return Api.MessageAction.parse_messageActionBoostApply($0) }
dict[-988359047] = { return Api.MessageAction.parse_messageActionBotAllowed($0) } dict[-988359047] = { return Api.MessageAction.parse_messageActionBotAllowed($0) }
dict[-1781355374] = { return Api.MessageAction.parse_messageActionChannelCreate($0) } dict[-1781355374] = { return Api.MessageAction.parse_messageActionChannelCreate($0) }

View File

@ -62,7 +62,7 @@ public extension Api {
indirect enum Message: TypeConstructorDescription { indirect enum Message: TypeConstructorDescription {
case message(flags: Int32, flags2: Int32, id: Int32, fromId: Api.Peer?, fromBoostsApplied: Int32?, peerId: Api.Peer, savedPeerId: Api.Peer?, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int64?, viaBusinessBotId: Int64?, replyTo: Api.MessageReplyHeader?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, forwards: Int32?, replies: Api.MessageReplies?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, reactions: Api.MessageReactions?, restrictionReason: [Api.RestrictionReason]?, ttlPeriod: Int32?, quickReplyShortcutId: Int32?, effect: Int64?, factcheck: Api.FactCheck?, reportDeliveryUntilDate: Int32?, paidMessageStars: Int64?) case message(flags: Int32, flags2: Int32, id: Int32, fromId: Api.Peer?, fromBoostsApplied: Int32?, peerId: Api.Peer, savedPeerId: Api.Peer?, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int64?, viaBusinessBotId: Int64?, replyTo: Api.MessageReplyHeader?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, forwards: Int32?, replies: Api.MessageReplies?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, reactions: Api.MessageReactions?, restrictionReason: [Api.RestrictionReason]?, ttlPeriod: Int32?, quickReplyShortcutId: Int32?, effect: Int64?, factcheck: Api.FactCheck?, reportDeliveryUntilDate: Int32?, paidMessageStars: Int64?)
case messageEmpty(flags: Int32, id: Int32, peerId: Api.Peer?) case messageEmpty(flags: Int32, id: Int32, peerId: Api.Peer?)
case messageService(flags: Int32, id: Int32, fromId: Api.Peer?, peerId: Api.Peer, replyTo: Api.MessageReplyHeader?, date: Int32, action: Api.MessageAction, reactions: Api.MessageReactions?, ttlPeriod: Int32?) case messageService(flags: Int32, id: Int32, fromId: Api.Peer?, peerId: Api.Peer, savedPeerId: Api.Peer?, replyTo: Api.MessageReplyHeader?, date: Int32, action: Api.MessageAction, reactions: Api.MessageReactions?, ttlPeriod: Int32?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
@ -117,14 +117,15 @@ public extension Api {
serializeInt32(id, buffer: buffer, boxed: false) serializeInt32(id, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {peerId!.serialize(buffer, true)} if Int(flags) & Int(1 << 0) != 0 {peerId!.serialize(buffer, true)}
break break
case .messageService(let flags, let id, let fromId, let peerId, let replyTo, let date, let action, let reactions, let ttlPeriod): case .messageService(let flags, let id, let fromId, let peerId, let savedPeerId, let replyTo, let date, let action, let reactions, let ttlPeriod):
if boxed { if boxed {
buffer.appendInt32(-741178048) buffer.appendInt32(2055212554)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false) serializeInt32(id, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 8) != 0 {fromId!.serialize(buffer, true)} if Int(flags) & Int(1 << 8) != 0 {fromId!.serialize(buffer, true)}
peerId.serialize(buffer, true) peerId.serialize(buffer, true)
if Int(flags) & Int(1 << 28) != 0 {savedPeerId!.serialize(buffer, true)}
if Int(flags) & Int(1 << 3) != 0 {replyTo!.serialize(buffer, true)} if Int(flags) & Int(1 << 3) != 0 {replyTo!.serialize(buffer, true)}
serializeInt32(date, buffer: buffer, boxed: false) serializeInt32(date, buffer: buffer, boxed: false)
action.serialize(buffer, true) action.serialize(buffer, true)
@ -140,8 +141,8 @@ public extension Api {
return ("message", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("fromId", fromId as Any), ("fromBoostsApplied", fromBoostsApplied as Any), ("peerId", peerId as Any), ("savedPeerId", savedPeerId as Any), ("fwdFrom", fwdFrom as Any), ("viaBotId", viaBotId as Any), ("viaBusinessBotId", viaBusinessBotId as Any), ("replyTo", replyTo as Any), ("date", date as Any), ("message", message as Any), ("media", media as Any), ("replyMarkup", replyMarkup as Any), ("entities", entities as Any), ("views", views as Any), ("forwards", forwards as Any), ("replies", replies as Any), ("editDate", editDate as Any), ("postAuthor", postAuthor as Any), ("groupedId", groupedId as Any), ("reactions", reactions as Any), ("restrictionReason", restrictionReason as Any), ("ttlPeriod", ttlPeriod as Any), ("quickReplyShortcutId", quickReplyShortcutId as Any), ("effect", effect as Any), ("factcheck", factcheck as Any), ("reportDeliveryUntilDate", reportDeliveryUntilDate as Any), ("paidMessageStars", paidMessageStars as Any)]) return ("message", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("fromId", fromId as Any), ("fromBoostsApplied", fromBoostsApplied as Any), ("peerId", peerId as Any), ("savedPeerId", savedPeerId as Any), ("fwdFrom", fwdFrom as Any), ("viaBotId", viaBotId as Any), ("viaBusinessBotId", viaBusinessBotId as Any), ("replyTo", replyTo as Any), ("date", date as Any), ("message", message as Any), ("media", media as Any), ("replyMarkup", replyMarkup as Any), ("entities", entities as Any), ("views", views as Any), ("forwards", forwards as Any), ("replies", replies as Any), ("editDate", editDate as Any), ("postAuthor", postAuthor as Any), ("groupedId", groupedId as Any), ("reactions", reactions as Any), ("restrictionReason", restrictionReason as Any), ("ttlPeriod", ttlPeriod as Any), ("quickReplyShortcutId", quickReplyShortcutId as Any), ("effect", effect as Any), ("factcheck", factcheck as Any), ("reportDeliveryUntilDate", reportDeliveryUntilDate as Any), ("paidMessageStars", paidMessageStars as Any)])
case .messageEmpty(let flags, let id, let peerId): case .messageEmpty(let flags, let id, let peerId):
return ("messageEmpty", [("flags", flags as Any), ("id", id as Any), ("peerId", peerId as Any)]) return ("messageEmpty", [("flags", flags as Any), ("id", id as Any), ("peerId", peerId as Any)])
case .messageService(let flags, let id, let fromId, let peerId, let replyTo, let date, let action, let reactions, let ttlPeriod): case .messageService(let flags, let id, let fromId, let peerId, let savedPeerId, let replyTo, let date, let action, let reactions, let ttlPeriod):
return ("messageService", [("flags", flags as Any), ("id", id as Any), ("fromId", fromId as Any), ("peerId", peerId as Any), ("replyTo", replyTo as Any), ("date", date as Any), ("action", action as Any), ("reactions", reactions as Any), ("ttlPeriod", ttlPeriod as Any)]) return ("messageService", [("flags", flags as Any), ("id", id as Any), ("fromId", fromId as Any), ("peerId", peerId as Any), ("savedPeerId", savedPeerId as Any), ("replyTo", replyTo as Any), ("date", date as Any), ("action", action as Any), ("reactions", reactions as Any), ("ttlPeriod", ttlPeriod as Any)])
} }
} }
@ -299,33 +300,38 @@ public extension Api {
if let signature = reader.readInt32() { if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.Peer _4 = Api.parse(reader, signature: signature) as? Api.Peer
} }
var _5: Api.MessageReplyHeader? var _5: Api.Peer?
if Int(_1!) & Int(1 << 28) != 0 {if let signature = reader.readInt32() {
_5 = Api.parse(reader, signature: signature) as? Api.Peer
} }
var _6: Api.MessageReplyHeader?
if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() {
_5 = Api.parse(reader, signature: signature) as? Api.MessageReplyHeader _6 = Api.parse(reader, signature: signature) as? Api.MessageReplyHeader
} } } }
var _6: Int32? var _7: Int32?
_6 = reader.readInt32() _7 = reader.readInt32()
var _7: Api.MessageAction? var _8: Api.MessageAction?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {
_7 = Api.parse(reader, signature: signature) as? Api.MessageAction _8 = Api.parse(reader, signature: signature) as? Api.MessageAction
} }
var _8: Api.MessageReactions? var _9: Api.MessageReactions?
if Int(_1!) & Int(1 << 20) != 0 {if let signature = reader.readInt32() { if Int(_1!) & Int(1 << 20) != 0 {if let signature = reader.readInt32() {
_8 = Api.parse(reader, signature: signature) as? Api.MessageReactions _9 = Api.parse(reader, signature: signature) as? Api.MessageReactions
} } } }
var _9: Int32? var _10: Int32?
if Int(_1!) & Int(1 << 25) != 0 {_9 = reader.readInt32() } if Int(_1!) & Int(1 << 25) != 0 {_10 = reader.readInt32() }
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 8) == 0) || _3 != nil let _c3 = (Int(_1!) & Int(1 << 8) == 0) || _3 != nil
let _c4 = _4 != nil let _c4 = _4 != nil
let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil let _c5 = (Int(_1!) & Int(1 << 28) == 0) || _5 != nil
let _c6 = _6 != nil let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil
let _c7 = _7 != nil let _c7 = _7 != nil
let _c8 = (Int(_1!) & Int(1 << 20) == 0) || _8 != nil let _c8 = _8 != nil
let _c9 = (Int(_1!) & Int(1 << 25) == 0) || _9 != nil let _c9 = (Int(_1!) & Int(1 << 20) == 0) || _9 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { let _c10 = (Int(_1!) & Int(1 << 25) == 0) || _10 != nil
return Api.Message.messageService(flags: _1!, id: _2!, fromId: _3, peerId: _4!, replyTo: _5, date: _6!, action: _7!, reactions: _8, ttlPeriod: _9) if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 {
return Api.Message.messageService(flags: _1!, id: _2!, fromId: _3, peerId: _4!, savedPeerId: _5, replyTo: _6, date: _7!, action: _8!, reactions: _9, ttlPeriod: _10)
} }
else { else {
return nil return nil

View File

@ -137,7 +137,7 @@ func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? {
} else { } else {
return nil return nil
} }
case let .messageService(_, _, _, chatPeerId, _, _, _, _, _): case let .messageService(_, _, _, chatPeerId, _, _, _, _, _, _):
return chatPeerId.peerId return chatPeerId.peerId
} }
} }
@ -218,7 +218,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
return result return result
case .messageEmpty: case .messageEmpty:
return [] return []
case let .messageService(_, _, fromId, chatPeerId, _, _, action, _, _): case let .messageService(_, _, fromId, chatPeerId, savedPeerId, _, _, action, _, _):
let peerId: PeerId = chatPeerId.peerId let peerId: PeerId = chatPeerId.peerId
var result = [peerId] var result = [peerId]
@ -227,6 +227,9 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
if resolvedFromId != peerId { if resolvedFromId != peerId {
result.append(resolvedFromId) result.append(resolvedFromId)
} }
if let savedPeerId, resolvedFromId != savedPeerId.peerId {
result.append(savedPeerId.peerId)
}
switch action { switch action {
case .messageActionChannelCreate, .messageActionChatDeletePhoto, .messageActionChatEditPhoto, .messageActionChatEditTitle, .messageActionEmpty, .messageActionPinMessage, .messageActionHistoryClear, .messageActionGameScore, .messageActionPaymentSent, .messageActionPaymentSentMe, .messageActionPhoneCall, .messageActionScreenshotTaken, .messageActionCustomAction, .messageActionBotAllowed, .messageActionSecureValuesSent, .messageActionSecureValuesSentMe, .messageActionContactSignUp, .messageActionGroupCall, .messageActionSetMessagesTTL, .messageActionGroupCallScheduled, .messageActionSetChatTheme, .messageActionChatJoinedByRequest, .messageActionWebViewDataSent, .messageActionWebViewDataSentMe, .messageActionGiftPremium, .messageActionGiftStars, .messageActionTopicCreate, .messageActionTopicEdit, .messageActionSuggestProfilePhoto, .messageActionSetChatWallPaper, .messageActionGiveawayLaunch, .messageActionGiveawayResults, .messageActionBoostApply, .messageActionRequestedPeerSentMe, .messageActionStarGift, .messageActionStarGiftUnique, .messageActionPaidMessagesRefunded, .messageActionPaidMessagesPrice, .messageActionTodoCompletions, .messageActionTodoAppendTasks: case .messageActionChannelCreate, .messageActionChatDeletePhoto, .messageActionChatEditPhoto, .messageActionChatEditTitle, .messageActionEmpty, .messageActionPinMessage, .messageActionHistoryClear, .messageActionGameScore, .messageActionPaymentSent, .messageActionPaymentSentMe, .messageActionPhoneCall, .messageActionScreenshotTaken, .messageActionCustomAction, .messageActionBotAllowed, .messageActionSecureValuesSent, .messageActionSecureValuesSentMe, .messageActionContactSignUp, .messageActionGroupCall, .messageActionSetMessagesTTL, .messageActionGroupCallScheduled, .messageActionSetChatTheme, .messageActionChatJoinedByRequest, .messageActionWebViewDataSent, .messageActionWebViewDataSentMe, .messageActionGiftPremium, .messageActionGiftStars, .messageActionTopicCreate, .messageActionTopicEdit, .messageActionSuggestProfilePhoto, .messageActionSetChatWallPaper, .messageActionGiveawayLaunch, .messageActionGiveawayResults, .messageActionBoostApply, .messageActionRequestedPeerSentMe, .messageActionStarGift, .messageActionStarGiftUnique, .messageActionPaidMessagesRefunded, .messageActionPaidMessagesPrice, .messageActionTodoCompletions, .messageActionTodoAppendTasks:
@ -301,7 +304,7 @@ func apiMessageAssociatedMessageIds(_ message: Api.Message) -> (replyIds: Refere
} }
case .messageEmpty: case .messageEmpty:
break break
case let .messageService(_, id, _, chatPeerId, replyHeader, _, _, _, _): case let .messageService(_, id, _, chatPeerId, _, replyHeader, _, _, _, _):
if let replyHeader = replyHeader { if let replyHeader = replyHeader {
switch replyHeader { switch replyHeader {
case let .messageReplyHeader(_, replyToMsgId, replyToPeerId, replyHeader, replyMedia, replyToTopId, quoteText, quoteEntities, quoteOffset): case let .messageReplyHeader(_, replyToMsgId, replyToPeerId, replyHeader, replyMedia, replyToTopId, quoteText, quoteEntities, quoteOffset):
@ -1061,14 +1064,16 @@ extension StoreMessage {
self.init(id: MessageId(peerId: peerId, namespace: namespace, id: id), globallyUniqueId: nil, groupingKey: groupingId, threadId: threadId, timestamp: date, flags: storeFlags, tags: tags, globalTags: globalTags, localTags: [], forwardInfo: forwardInfo, authorId: authorId, text: messageText, attributes: attributes, media: medias) self.init(id: MessageId(peerId: peerId, namespace: namespace, id: id), globallyUniqueId: nil, groupingKey: groupingId, threadId: threadId, timestamp: date, flags: storeFlags, tags: tags, globalTags: globalTags, localTags: [], forwardInfo: forwardInfo, authorId: authorId, text: messageText, attributes: attributes, media: medias)
case .messageEmpty: case .messageEmpty:
return nil return nil
case let .messageService(flags, id, fromId, chatPeerId, replyTo, date, action, reactions, ttlPeriod): case let .messageService(flags, id, fromId, chatPeerId, savedPeerId, replyTo, date, action, reactions, ttlPeriod):
let peerId: PeerId = chatPeerId.peerId let peerId: PeerId = chatPeerId.peerId
let authorId: PeerId? = fromId?.peerId ?? chatPeerId.peerId let authorId: PeerId? = fromId?.peerId ?? chatPeerId.peerId
var attributes: [MessageAttribute] = [] var attributes: [MessageAttribute] = []
var threadId: Int64? var threadId: Int64?
if let replyTo = replyTo { if let savedPeerId {
threadId = savedPeerId.peerId.toInt64()
} else if let replyTo = replyTo {
var threadMessageId: MessageId? var threadMessageId: MessageId?
switch replyTo { switch replyTo {
case let .messageReplyHeader(innerFlags, replyToMsgId, replyToPeerId, replyHeader, replyMedia, replyToTopId, quoteText, quoteEntities, quoteOffset): case let .messageReplyHeader(innerFlags, replyToMsgId, replyToPeerId, replyHeader, replyMedia, replyToTopId, quoteText, quoteEntities, quoteOffset):

View File

@ -108,7 +108,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
updatedTimestamp = date updatedTimestamp = date
case .messageEmpty: case .messageEmpty:
break break
case let .messageService(_, _, _, _, _, date, _, _, _): case let .messageService(_, _, _, _, _, _, date, _, _, _):
updatedTimestamp = date updatedTimestamp = date
} }
} else { } else {

View File

@ -108,7 +108,7 @@ extension Api.Message {
return id return id
case let .messageEmpty(_, id, _): case let .messageEmpty(_, id, _):
return id return id
case let .messageService(_, id, _, _, _, _, _, _, _): case let .messageService(_, id, _, _, _, _, _, _, _, _):
return id return id
} }
} }
@ -128,7 +128,7 @@ extension Api.Message {
} else { } else {
return nil return nil
} }
case let .messageService(_, id, _, chatPeerId, _, _, _, _, _): case let .messageService(_, id, _, chatPeerId, _, _, _, _, _, _):
let peerId: PeerId = chatPeerId.peerId let peerId: PeerId = chatPeerId.peerId
return MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: id) return MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: id)
} }
@ -141,7 +141,7 @@ extension Api.Message {
return peerId return peerId
case let .messageEmpty(_, _, peerId): case let .messageEmpty(_, _, peerId):
return peerId?.peerId return peerId?.peerId
case let .messageService(_, _, _, chatPeerId, _, _, _, _, _): case let .messageService(_, _, _, chatPeerId, _, _, _, _, _, _):
let peerId: PeerId = chatPeerId.peerId let peerId: PeerId = chatPeerId.peerId
return peerId return peerId
} }
@ -151,7 +151,7 @@ extension Api.Message {
switch self { switch self {
case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
return date return date
case let .messageService(_, _, _, _, _, date, _, _, _): case let .messageService(_, _, _, _, _, _, date, _, _, _):
return date return date
case .messageEmpty: case .messageEmpty:
return nil return nil

View File

@ -1288,6 +1288,11 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
} }
case .unknown: case .unknown:
attributedString = nil attributedString = nil
case let .todoCompletions(completed, incompleted):
//TODO:release
let _ = completed
let _ = incompleted
attributedString = nil
} }
break break
} else if let expiredMedia = media as? TelegramMediaExpiredContent { } else if let expiredMedia = media as? TelegramMediaExpiredContent {

View File

@ -858,7 +858,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
public func asyncLayout() -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ dateTimeFormat: PresentationDateTimeFormat, _ message: Message, _ associatedData: ChatMessageItemAssociatedData, _ attributes: ChatMessageEntryAttributes, _ media: Media, _ mediaIndex: Int?, _ dateAndStatus: ChatMessageDateAndStatus?, _ automaticDownload: InteractiveMediaNodeAutodownloadMode, _ peerType: MediaAutoDownloadPeerType, _ peerId: EnginePeer.Id?, _ sizeCalculation: InteractiveMediaNodeSizeCalculation, _ layoutConstants: ChatMessageItemLayoutConstants, _ contentMode: InteractiveMediaNodeContentMode, _ presentationContext: ChatPresentationContext) -> (CGSize, CGFloat, (CGSize, Bool, Bool, ImageCorners) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool) -> Void))) { public func asyncLayout() -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ dateTimeFormat: PresentationDateTimeFormat, _ message: Message, _ associatedData: ChatMessageItemAssociatedData, _ attributes: ChatMessageEntryAttributes, _ media: Media, _ mediaIndex: Int?, _ dateAndStatus: ChatMessageDateAndStatus?, _ automaticDownload: InteractiveMediaNodeAutodownloadMode, _ peerType: MediaAutoDownloadPeerType, _ peerId: EnginePeer.Id?, _ sizeCalculation: InteractiveMediaNodeSizeCalculation, _ layoutConstants: ChatMessageItemLayoutConstants, _ contentMode: InteractiveMediaNodeContentMode, _ presentationContext: ChatPresentationContext) -> (CGSize, CGFloat, (CGSize, Bool, Bool, ImageCorners) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool) -> Void))) {
let currentMessage = self.message let currentMessage = self.message
let currentMedia = self.media let currentMedia = self.media
let imageLayout = self.imageNode.asyncLayout() let imageLayout = self.imageNode.asyncLayoutWithAnimation()
let statusLayout = self.dateAndStatusNode.asyncLayout() let statusLayout = self.dateAndStatusNode.asyncLayout()
let currentVideoNode = self.videoNode let currentVideoNode = self.videoNode
@ -1894,7 +1894,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
timestampMaskView.image = strongSelf.generateTimestampMaskImage(corners: arguments.corners) timestampMaskView.image = strongSelf.generateTimestampMaskImage(corners: arguments.corners)
} }
strongSelf.currentImageArguments = arguments strongSelf.currentImageArguments = arguments
imageApply() imageApply(transition)
if let statusApply = statusApply { if let statusApply = statusApply {
let dateAndStatusFrame = CGRect(origin: CGPoint(x: cleanImageFrame.width - layoutConstants.image.statusInsets.right - statusSize.width, y: cleanImageFrame.height - layoutConstants.image.statusInsets.bottom - statusSize.height), size: statusSize) let dateAndStatusFrame = CGRect(origin: CGPoint(x: cleanImageFrame.width - layoutConstants.image.statusInsets.right - statusSize.width, y: cleanImageFrame.height - layoutConstants.image.statusInsets.bottom - statusSize.height), size: statusSize)
@ -2084,8 +2084,13 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
strongSelf.imageNode.cornerRadius = 0.0 strongSelf.imageNode.cornerRadius = 0.0
} }
videoNode.updateLayout(size: arguments.drawingSize, transition: .immediate) if videoNode.bounds.isEmpty {
videoNode.frame = CGRect(origin: CGPoint(), size: imageFrame.size) videoNode.updateLayout(size: arguments.drawingSize, transition: .immediate)
videoNode.frame = CGRect(origin: CGPoint(), size: imageFrame.size)
} else {
videoNode.updateLayout(size: arguments.drawingSize, transition: transition.transition)
transition.animator.updateFrame(layer: videoNode.layer, frame: CGRect(origin: CGPoint(), size: imageFrame.size), completion: nil)
}
if strongSelf.visibility && strongSelf.internallyVisible && !presentationData.isPreview { if strongSelf.visibility && strongSelf.internallyVisible && !presentationData.isPreview {
if !videoNode.canAttachContent { if !videoNode.canAttachContent {

View File

@ -1359,7 +1359,7 @@ public final class ChatSideTopicsPanel: Component {
maximumNumberOfLines: 2 maximumNumberOfLines: 2
)), )),
environment: {}, environment: {},
containerSize: CGSize(width: 200.0, height: 200.0) containerSize: CGSize(width: 400.0, height: 200.0)
) )
let contentSize: CGFloat = leftInset + rightInset + titleSize.height let contentSize: CGFloat = leftInset + rightInset + titleSize.height

View File

@ -68,6 +68,8 @@ public enum ChatTitleContent: Equatable {
return false return false
} }
if lhs.peerPresences.count != rhs.peerPresences.count { if lhs.peerPresences.count != rhs.peerPresences.count {
return false
} else {
for (key, value) in lhs.peerPresences { for (key, value) in lhs.peerPresences {
if let rhsValue = rhs.peerPresences[key] { if let rhsValue = rhs.peerPresences[key] {
if !value.isEqual(to: rhsValue) { if !value.isEqual(to: rhsValue) {

View File

@ -368,8 +368,7 @@ private class MessagePriceItemNode: ListViewItemNode {
strongSelf.leftTextNode.attributedText = NSAttributedString(string: "\(item.minValue)", font: Font.regular(13.0), textColor: item.theme.list.itemSecondaryTextColor) strongSelf.leftTextNode.attributedText = NSAttributedString(string: "\(item.minValue)", font: Font.regular(13.0), textColor: item.theme.list.itemSecondaryTextColor)
strongSelf.rightTextNode.attributedText = NSAttributedString(string: "\(item.maxValue)", font: Font.regular(13.0), textColor: item.theme.list.itemSecondaryTextColor) strongSelf.rightTextNode.attributedText = NSAttributedString(string: "\(item.maxValue)", font: Font.regular(13.0), textColor: item.theme.list.itemSecondaryTextColor)
//TODO:localize let centralLeftText = item.value == 0 ? item.strings.Stars_SendMessage_PriceFree : item.strings.Privacy_Messages_Stars(Int32(item.value))
let centralLeftText = item.value == 0 ? "Free" : item.strings.Privacy_Messages_Stars(Int32(item.value))
strongSelf.centerLeftTextNode.attributedText = NSAttributedString(string: centralLeftText, font: textFont, textColor: item.openSetCustom != nil ? item.theme.list.itemAccentColor : item.theme.list.itemPrimaryTextColor) strongSelf.centerLeftTextNode.attributedText = NSAttributedString(string: centralLeftText, font: textFont, textColor: item.openSetCustom != nil ? item.theme.list.itemAccentColor : item.theme.list.itemPrimaryTextColor)
strongSelf.centerRightTextNode.attributedText = NSAttributedString(string: item.price, font: smallTextFont, textColor: item.openSetCustom != nil ? item.theme.list.itemAccentColor.withMultipliedAlpha(0.5) : item.theme.list.itemSecondaryTextColor) strongSelf.centerRightTextNode.attributedText = NSAttributedString(string: item.price, font: smallTextFont, textColor: item.openSetCustom != nil ? item.theme.list.itemAccentColor.withMultipliedAlpha(0.5) : item.theme.list.itemSecondaryTextColor)

View File

@ -397,21 +397,30 @@ extension ChatControllerImpl {
self.navigationActionDisposable.set((peerView.get() self.navigationActionDisposable.set((peerView.get()
|> take(1) |> take(1)
|> deliverOnMainQueue).startStrict(next: { [weak self] peerView in |> deliverOnMainQueue).startStrict(next: { [weak self] peerView in
if let strongSelf = self, let peer = peerView.peers[peerView.peerId], peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil && !strongSelf.presentationInterfaceState.isNotAccessible { guard let self else {
return
if peer.id == strongSelf.context.account.peerId { }
if let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer, let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: true, requestsContext: nil) { guard var peer = peerView.peers[peerView.peerId] else {
strongSelf.effectiveNavigationController?.pushViewController(infoController) return
}
if let channel = peer as? TelegramChannel, channel.isMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainPeer = peerView.peers[linkedMonoforumId] {
peer = mainPeer
}
if peer.restrictionText(platform: "ios", contentSettings: self.context.currentContentSettings.with { $0 }) == nil && !self.presentationInterfaceState.isNotAccessible {
if peer.id == self.context.account.peerId {
if let peer = self.presentationInterfaceState.renderedPeer?.chatMainPeer, let infoController = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: true, requestsContext: nil) {
self.effectiveNavigationController?.pushViewController(infoController)
} }
} else { } else {
var expandAvatar = expandAvatar var expandAvatar = expandAvatar
if peer.smallProfileImage == nil { if peer.smallProfileImage == nil {
expandAvatar = false expandAvatar = false
} }
if let validLayout = strongSelf.validLayout, validLayout.deviceMetrics.type == .tablet { if let validLayout = self.validLayout, validLayout.deviceMetrics.type == .tablet {
expandAvatar = false expandAvatar = false
} }
let mode: PeerInfoControllerMode let mode: PeerInfoControllerMode
switch section { switch section {
case .groupsInCommon: case .groupsInCommon:
mode = .groupsInCommon mode = .groupsInCommon
@ -420,12 +429,12 @@ extension ChatControllerImpl {
default: default:
mode = .generic mode = .generic
} }
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: peer, mode: mode, avatarInitiallyExpanded: expandAvatar, fromChat: true, requestsContext: strongSelf.contentData?.inviteRequestsContext) { if let infoController = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: peer, mode: mode, avatarInitiallyExpanded: expandAvatar, fromChat: true, requestsContext: self.contentData?.inviteRequestsContext) {
strongSelf.effectiveNavigationController?.pushViewController(infoController) self.effectiveNavigationController?.pushViewController(infoController)
} }
} }
let _ = strongSelf.dismissPreviewing?(false) let _ = self.dismissPreviewing?(false)
} }
})) }))
case .replyThread: case .replyThread:

View File

@ -548,9 +548,9 @@ extension ChatControllerImpl {
strongSelf.state.chatTitleContent = .custom(strings.Chat_TitlePinnedMessages(Int32(displayedCount ?? 1)), nil, false) strongSelf.state.chatTitleContent = .custom(strings.Chat_TitlePinnedMessages(Int32(displayedCount ?? 1)), nil, false)
} else if let channel = peer as? TelegramChannel, channel.isMonoForum { } else if let channel = peer as? TelegramChannel, channel.isMonoForum {
if let linkedMonoforumId = channel.linkedMonoforumId, let mainPeer = peerView.peers[linkedMonoforumId] { if let linkedMonoforumId = channel.linkedMonoforumId, let mainPeer = peerView.peers[linkedMonoforumId] {
strongSelf.state.chatTitleContent = .custom(mainPeer.debugDisplayTitle, "Direct messages", false) strongSelf.state.chatTitleContent = .custom(mainPeer.debugDisplayTitle, strings.Chat_Monoforum_Subtitle, true)
} else { } else {
strongSelf.state.chatTitleContent = .custom(channel.debugDisplayTitle, nil, false) strongSelf.state.chatTitleContent = .custom(channel.debugDisplayTitle, nil, true)
} }
} else { } else {
strongSelf.state.chatTitleContent = .peer(peerView: ChatTitleContent.PeerData(peerView: peerView), customTitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: isScheduledMessages, isMuted: nil, customMessageCount: nil, isEnabled: hasPeerInfo) strongSelf.state.chatTitleContent = .peer(peerView: ChatTitleContent.PeerData(peerView: peerView), customTitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: isScheduledMessages, isMuted: nil, customMessageCount: nil, isEnabled: hasPeerInfo)

View File

@ -2552,17 +2552,9 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
if self.leftPanel != nil || dismissedLeftPanel != nil { if self.leftPanel != nil || dismissedLeftPanel != nil {
titleTopicsAccessoryPanelNode.updateGlobalOffset(globalOffset: -titleTopicsAccessoryPanelFrame.height, transition: .immediate) titleTopicsAccessoryPanelNode.updateGlobalOffset(globalOffset: -titleTopicsAccessoryPanelFrame.height, transition: .immediate)
} }
let topPanelTransition = ComponentTransition(transition)
/*switch topPanelTransition.animation {
case let .curve(duration, _):
topPanelTransition = topPanelTransition.withAnimation(.curve(duration: duration, curve: ComponentTransition.Animation.Curve(ChatMessageTransitionNodeImpl.verticalAnimationCurve)))
default:
break
}*/
topPanelTransition.setFrame(view: titleTopicsAccessoryPanelNode.view, frame: titleTopicsAccessoryPanelFrame) ComponentTransition(transition).setFrame(view: titleTopicsAccessoryPanelNode.view, frame: titleTopicsAccessoryPanelFrame)
titleTopicsAccessoryPanelNode.updateGlobalOffset(globalOffset: 0.0, transition: topPanelTransition) titleTopicsAccessoryPanelNode.updateGlobalOffset(globalOffset: 0.0, transition: ComponentTransition(transition))
} else { } else {
let previousFrame = titleTopicsAccessoryPanelNode.frame let previousFrame = titleTopicsAccessoryPanelNode.frame
titleTopicsAccessoryPanelNode.frame = titleTopicsAccessoryPanelFrame titleTopicsAccessoryPanelNode.frame = titleTopicsAccessoryPanelFrame

View File

@ -681,6 +681,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
private var loadedMessagesFromCachedDataDisposable: Disposable? private var loadedMessagesFromCachedDataDisposable: Disposable?
private var isSettingTopReplyThreadMessageShown: Bool = false
let isTopReplyThreadMessageShown = ValuePromise<Bool>(false, ignoreRepeated: true) let isTopReplyThreadMessageShown = ValuePromise<Bool>(false, ignoreRepeated: true)
private var topVisibleMessageRangeValueInitialized: Bool = false private var topVisibleMessageRangeValueInitialized: Bool = false
@ -3178,8 +3179,15 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
} }
} }
if !self.isSettingTopReplyThreadMessageShown {
self.isTopReplyThreadMessageShown.set(isTopReplyThreadMessageShownValue) self.isSettingTopReplyThreadMessageShown = true
self.isTopReplyThreadMessageShown.set(isTopReplyThreadMessageShownValue)
self.isSettingTopReplyThreadMessageShown = false
} else {
#if DEBUG
print("Ignore repeated isTopReplyThreadMessageShown update")
#endif
}
self.updateTopVisibleMessageRange(topVisibleMessageRange) self.updateTopVisibleMessageRange(topVisibleMessageRange)
let _ = self.visibleMessageRange.swap(topVisibleMessageRange.flatMap { range in let _ = self.visibleMessageRange.swap(topVisibleMessageRange.flatMap { range in
return VisibleMessageRange(lowerBound: range.lowerBound, upperBound: range.upperBound) return VisibleMessageRange(lowerBound: range.lowerBound, upperBound: range.upperBound)

View File

@ -1591,7 +1591,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
} }
} }
if let message = messages.first, message.id.namespace == Namespaces.Message.Cloud, let channel = message.peers[message.id.peerId] as? TelegramChannel, !(message.media.first is TelegramMediaAction), !isReplyThreadHead, !isMigrated { if let message = messages.first, message.id.namespace == Namespaces.Message.Cloud, let channel = message.peers[message.id.peerId] as? TelegramChannel, !channel.isMonoForum, !(message.media.first is TelegramMediaAction), !isReplyThreadHead, !isMigrated {
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuCopyLink, icon: { theme in actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuCopyLink, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.actionSheet.primaryTextColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in }, action: { _, f in
@ -1893,8 +1893,8 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
var canViewStats = false var canViewStats = false
var canViewAuthor = false var canViewAuthor = false
if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isMonoForum { if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isMonoForum, let associatedPeerId = channel.associatedPeerId {
if message.effectivelyIncoming(context.account.peerId) { if message.effectivelyIncoming(context.account.peerId), message.author?.id == associatedPeerId {
canViewAuthor = true canViewAuthor = true
} }
} else if let messageReadStatsAreHidden = infoSummaryData.messageReadStatsAreHidden, !messageReadStatsAreHidden { } else if let messageReadStatsAreHidden = infoSummaryData.messageReadStatsAreHidden, !messageReadStatsAreHidden {

View File

@ -690,22 +690,34 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
if let dimensions = self.dimensions { if let dimensions = self.dimensions {
let imageSize = CGSize(width: floor(dimensions.width / 2.0), height: floor(dimensions.height / 2.0)) let imageSize = CGSize(width: floor(dimensions.width / 2.0), height: floor(dimensions.height / 2.0))
let makeLayout = self.imageNode.asyncLayout() let makeLayout = self.imageNode.asyncLayoutWithAnimation()
let applyLayout = makeLayout(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: self.fileReference.media.isInstantVideo ? .clear : self.placeholderColor)) let applyLayout = makeLayout(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: self.fileReference.media.isInstantVideo ? .clear : self.placeholderColor))
applyLayout() let mappedAnimation: ListViewItemUpdateAnimation
if case let .animated(duration, curve) = transition {
mappedAnimation = .System(duration: duration, transition: ControlledTransition(duration: duration, curve: curve, interactive: false))
} else {
mappedAnimation = .None
}
applyLayout(mappedAnimation)
} }
transition.updateFrame(node: self.imageNode, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(node: self.imageNode, frame: CGRect(origin: CGPoint(), size: size))
let fromFrame = self.playerNode.frame let fromFrame = self.playerNode.frame
let toFrame = CGRect(origin: CGPoint(), size: size).insetBy(dx: -1.0, dy: -1.0) let toFrame = CGRect(origin: CGPoint(), size: size).insetBy(dx: -1.0, dy: -1.0)
if case let .animated(duration, curve) = transition, fromFrame != toFrame, !fromFrame.width.isZero, !fromFrame.height.isZero, !toFrame.width.isZero, !toFrame.height.isZero { if case let .animated(duration, curve) = transition, fromFrame != toFrame, !fromFrame.width.isZero, !fromFrame.height.isZero, !toFrame.width.isZero, !toFrame.height.isZero {
self.playerNode.frame = toFrame let _ = duration
transition.animatePosition(node: self.playerNode, from: CGPoint(x: fromFrame.center.x - toFrame.center.x, y: fromFrame.center.y - toFrame.center.y)) let _ = curve
self.playerNode.position = toFrame.center
self.playerNode.bounds = CGRect(origin: CGPoint(), size: toFrame.size)
self.playerNode.updateLayout()
transition.animatePosition(node: self.playerNode, from: CGPoint(x: fromFrame.center.x, y: fromFrame.center.y))
let transform = CATransform3DScale(CATransform3DIdentity, fromFrame.width / toFrame.width, fromFrame.height / toFrame.height, 1.0) let transform = CATransform3DScale(CATransform3DIdentity, fromFrame.width / toFrame.width, fromFrame.height / toFrame.height, 1.0)
self.playerNode.layer.animate(from: NSValue(caTransform3D: transform), to: NSValue(caTransform3D: CATransform3DIdentity), keyPath: "transform", timingFunction: curve.timingFunction, duration: duration) self.playerNode.layer.animate(from: NSValue(caTransform3D: transform), to: NSValue(caTransform3D: CATransform3DIdentity), keyPath: "transform", timingFunction: curve.timingFunction, duration: duration)
} else { } else {
transition.updateFrame(node: self.playerNode, frame: toFrame) transition.updatePosition(node: self.playerNode, position: toFrame.center)
transition.updateBounds(node: self.playerNode, bounds: CGRect(origin: CGPoint(), size: toFrame.size))
self.playerNode.updateLayout()
} }
if let thumbnailNode = self.thumbnailNode { if let thumbnailNode = self.thumbnailNode {
transition.updateFrame(node: thumbnailNode, frame: CGRect(origin: CGPoint(), size: size).insetBy(dx: -1.0, dy: -1.0)) transition.updateFrame(node: thumbnailNode, frame: CGRect(origin: CGPoint(), size: size).insetBy(dx: -1.0, dy: -1.0))