UI improvements

This commit is contained in:
Ali 2020-06-05 17:15:40 +04:00
parent a5f9d41dbe
commit 4cbf51364e
30 changed files with 4926 additions and 4563 deletions

View File

@ -3,7 +3,7 @@
include Utils.makefile
APP_VERSION="6.2"
APP_VERSION="6.2.1"
CORE_COUNT=$(shell sysctl -n hw.logicalcpu)
CORE_COUNT_MINUS_ONE=$(shell expr ${CORE_COUNT} \- 1)

View File

@ -5524,3 +5524,8 @@ Any member of this group will be able to see messages in the channel.";
"Conversation.NoticeInvitedByInChannel" = "%@ invited you to this channel";
"Conversation.NoticeInvitedByInGroup" = "%@ invited you to this group";
"ChatList.MessagePhotos_1" = "1 Photo";
"ChatList.MessagePhotos_any" = "%@ Photos";
"ChatList.MessageVideos_1" = "1 Videos";
"ChatList.MessageVideos_any" = "%@ Videos";

View File

@ -330,7 +330,7 @@ public final class AvatarNode: ASDisplayNode {
if let peer = peer, let signal = peerAvatarImage(account: context.account, peerReference: PeerReference(peer), authorOfMessage: authorOfMessage, representation: representation, displayDimensions: displayDimensions, emptyColor: emptyColor, synchronousLoad: synchronousLoad, provideUnrounded: storeUnrounded) {
self.contents = nil
self.displaySuspended = true
self.imageReady.set(self.imageNode.ready)
self.imageReady.set(self.imageNode.contentReady)
self.imageNode.setSignal(signal |> beforeNext { [weak self] next in
Queue.mainQueue().async {
self?.unroundedImage = next?.1

View File

@ -200,7 +200,7 @@ private final class ChatListShimmerNode: ASDisplayNode {
}, present: { _ in })
let items = (0 ..< 2).map { _ -> ChatListItem in
return ChatListItem(presentationData: chatListPresentationData, context: context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex(id: MessageId(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp1, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "Text", attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []), peer: RenderedPeer(peer: peer1), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 0, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
return ChatListItem(presentationData: chatListPresentationData, context: context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex(id: MessageId(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp1, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "Text", attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])], peer: RenderedPeer(peer: peer1), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 0, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
}
var itemNodes: [ChatListItemNode] = []

View File

@ -485,7 +485,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
}
})
case let .message(message, peer, readState, presentationData):
return ChatListItem(presentationData: presentationData, context: context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: message.index), content: .peer(message: message, peer: peer, combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: true, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: enableHeaders ? ChatListSearchItemHeader(type: .messages, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) : nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
return ChatListItem(presentationData: presentationData, context: context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: message.index), content: .peer(messages: [message], peer: peer, combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: true, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: enableHeaders ? ChatListSearchItemHeader(type: .messages, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) : nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
case let .addContact(phoneNumber, theme, strings):
return ContactsAddItem(theme: theme, strings: strings, phoneNumber: phoneNumber, header: ChatListSearchItemHeader(type: .phoneNumber, theme: theme, strings: strings, actionTitle: nil, action: nil), action: {
interaction.addContact(phoneNumber)
@ -1385,8 +1385,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
bounds = selectedItemNode.bounds
}
switch item.content {
case let .peer(message, peer, _, _, _, _, _, _, _, _, _, _):
return (selectedItemNode.view, bounds, message?.id ?? peer.peerId)
case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _):
return (selectedItemNode.view, bounds, messages.last?.id ?? peer.peerId)
case let .groupReference(groupId, _, _, _, _):
return (selectedItemNode.view, bounds, groupId)
}

View File

@ -20,7 +20,7 @@ import ChatListSearchItemNode
import ContextUI
public enum ChatListItemContent {
case peer(message: Message?, peer: RenderedPeer, combinedReadState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, embeddedState: PeerChatListEmbeddedInterfaceState?, inputActivities: [(Peer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, ignoreUnreadBadge: Bool, displayAsMessage: Bool, hasFailedMessages: Bool)
case peer(messages: [Message], peer: RenderedPeer, combinedReadState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, embeddedState: PeerChatListEmbeddedInterfaceState?, inputActivities: [(Peer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, ignoreUnreadBadge: Bool, displayAsMessage: Bool, hasFailedMessages: Bool)
case groupReference(groupId: PeerGroupId, peers: [ChatListGroupReferencePeer], message: Message?, unreadState: PeerGroupUnreadCountersCombinedSummary, hiddenByDefault: Bool)
public var chatLocation: ChatLocation? {
@ -124,8 +124,8 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour {
public func selected(listView: ListView) {
switch self.content {
case let .peer(message, peer, _, _, _, _, _, _, promoInfo, _, _, _):
if let message = message, let peer = peer.peer {
case let .peer(messages, peer, _, _, _, _, _, _, promoInfo, _, _, _):
if let message = messages.last, let peer = peer.peer {
self.interaction.messageSelected(peer, message, promoInfo)
} else if let peer = peer.peer {
self.interaction.peerSelected(peer, promoInfo)
@ -328,6 +328,61 @@ private final class CachedChatListSearchResult {
}
}
private final class ChatListMediaPreviewNode: ASDisplayNode {
private let context: AccountContext
private let message: Message
private let media: Media
private let imageNode: TransformImageNode
private var requestedImage: Bool = false
private var disposable: Disposable?
init(context: AccountContext, message: Message, media: Media) {
self.context = context
self.message = message
self.media = media
self.imageNode = TransformImageNode()
super.init()
self.addSubnode(self.imageNode)
}
deinit {
self.disposable?.dispose()
}
func updateLayout(size: CGSize, synchronousLoads: Bool) {
var dimensions = CGSize(width: 100.0, height: 100.0)
if let image = self.media as? TelegramMediaImage {
if let largest = largestImageRepresentation(image.representations) {
dimensions = largest.dimensions.cgSize
if !self.requestedImage {
self.requestedImage = true
let signal = mediaGridMessagePhoto(account: self.context.account, photoReference: .message(message: MessageReference(self.message), media: image), synchronousLoad: synchronousLoads)
self.imageNode.setSignal(signal, attemptSynchronously: synchronousLoads)
}
}
} else if let file = self.media as? TelegramMediaFile {
if let mediaDimensions = file.dimensions {
dimensions = mediaDimensions.cgSize
if !self.requestedImage {
self.requestedImage = true
let signal = mediaGridMessageVideo(postbox: self.context.account.postbox, videoReference: .message(message: MessageReference(self.message), media: file), synchronousLoad: synchronousLoads, autoFetchFullSizeThumbnail: true)
self.imageNode.setSignal(signal, attemptSynchronously: synchronousLoads)
}
}
}
let makeLayout = self.imageNode.asyncLayout()
self.imageNode.frame = CGRect(origin: CGPoint(), size: size)
let apply = makeLayout(TransformImageArguments(corners: ImageCorners(radius: 2.0), imageSize: dimensions.aspectFilled(size), boundingSize: size, intrinsicInsets: UIEdgeInsets()))
apply()
}
}
class ChatListItemNode: ItemListRevealOptionsItemNode {
var item: ChatListItem?
@ -342,7 +397,6 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let measureNode: TextNode
private var currentItemHeight: CGFloat?
let textNode: TextNode
let contentImageNode: TransformImageNode
let inputActivitiesNode: ChatListInputActivitiesNode
let dateNode: TextNode
let separatorNode: ASDisplayNode
@ -355,6 +409,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var credibilityIconNode: ASImageNode?
let mutedIconNode: ASImageNode
private var currentTextLeftCutout: CGFloat = 0.0
private var currentMediaPreviewSpecs: [(message: Message, media: Media, size: CGSize)] = []
private var mediaPreviewNodes: [MediaId: ChatListMediaPreviewNode] = [:]
var selectableControlNode: ItemListSelectableControlNode?
var reorderControlNode: ItemListEditableReorderControlNode?
@ -364,7 +422,6 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
private var cachedChatListSearchResult: CachedChatListSearchResult?
var layoutParams: (ChatListItem, first: Bool, last: Bool, firstWithHeader: Bool, nextIsPinned: Bool, ListViewItemLayoutParams, countersSize: CGFloat)?
private var contentImageMedia: Media?
private var isHighlighted: Bool = false
private var skipFadeout: Bool = false
@ -423,14 +480,14 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
case .groupReference:
return nil
case let .peer(peer):
if let message = peer.message {
if let message = peer.messages.last {
var result = ""
if message.flags.contains(.Incoming) {
result += "Message"
} else {
result += "Outgoing message"
}
let (_, initialHideAuthor, messageText) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, message: peer.message, chatPeer: peer.peer, accountPeerId: item.context.account.peerId, isPeerGroup: false)
let (_, initialHideAuthor, messageText) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, messages: peer.messages, chatPeer: peer.peer, accountPeerId: item.context.account.peerId, isPeerGroup: false)
if message.flags.contains(.Incoming), !initialHideAuthor, let author = message.author, author is TelegramUser {
result += "\nFrom: \(author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder))"
}
@ -473,9 +530,6 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
self.textNode.isUserInteractionEnabled = false
self.textNode.displaysAsynchronously = true
self.contentImageNode = TransformImageNode()
self.contentImageNode.isHidden = true
self.inputActivitiesNode = ChatListInputActivitiesNode()
self.inputActivitiesNode.isUserInteractionEnabled = false
self.inputActivitiesNode.alpha = 0.0
@ -517,7 +571,6 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
self.contextContainer.addSubnode(self.titleNode)
self.contextContainer.addSubnode(self.authorNode)
self.contextContainer.addSubnode(self.textNode)
self.contextContainer.addSubnode(self.contentImageNode)
self.contextContainer.addSubnode(self.dateNode)
self.contextContainer.addSubnode(self.statusNode)
self.contextContainer.addSubnode(self.pinnedIconNode)
@ -548,9 +601,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var displayAsMessage = false
var enablePreview = true
switch item.content {
case let .peer(message, peerValue, _, _, _, _, _, _, _, _, displayAsMessageValue, _):
case let .peer(messages, peerValue, _, _, _, _, _, _, _, _, displayAsMessageValue, _):
displayAsMessage = displayAsMessageValue
if displayAsMessage, let author = message?.author as? TelegramUser {
if displayAsMessage, let author = messages.last?.author as? TelegramUser {
peer = author
} else {
peer = peerValue.chatMainPeer
@ -674,7 +727,6 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let reorderControlLayout = ItemListEditableReorderControlNode.asyncLayout(self.reorderControlNode)
let currentItem = self.layoutParams?.0
let currentContentImageMedia = self.contentImageMedia
let currentChatListText = self.cachedChatListText
let currentChatListSearchResult = self.cachedChatListSearchResult
@ -685,7 +737,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let badgeFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 14.0 / 17.0))
let account = item.context.account
var message: Message?
var messages: [Message]
enum ContentPeer {
case chat(RenderedPeer)
case group([ChatListGroupReferencePeer])
@ -706,8 +758,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var groupHiddenByDefault = false
switch item.content {
case let .peer(messageValue, peerValue, combinedReadStateValue, isRemovedFromTotalUnreadCountValue, peerPresenceValue, summaryInfoValue, embeddedStateValue, inputActivitiesValue, promoInfoValue, ignoreUnreadBadge, displayAsMessageValue, hasFailedMessagesValue):
message = messageValue
case let .peer(messagesValue, peerValue, combinedReadStateValue, isRemovedFromTotalUnreadCountValue, peerPresenceValue, summaryInfoValue, embeddedStateValue, inputActivitiesValue, promoInfoValue, ignoreUnreadBadge, displayAsMessageValue, hasFailedMessagesValue):
messages = messagesValue
contentPeer = .chat(peerValue)
combinedReadState = combinedReadStateValue
if let combinedReadState = combinedReadState, promoInfoValue == nil && !ignoreUnreadBadge {
@ -729,14 +781,18 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
isPeerGroup = false
promoInfo = promoInfoValue
displayAsMessage = displayAsMessageValue
hasFailedMessages = messageValue?.flags.contains(.Failed) ?? false // hasFailedMessagesValue
hasFailedMessages = messagesValue.last?.flags.contains(.Failed) ?? false // hasFailedMessagesValue
case let .groupReference(_, peers, messageValue, unreadState, hiddenByDefault):
if let _ = messageValue, !peers.isEmpty {
contentPeer = .chat(peers[0].peer)
} else {
contentPeer = .group(peers)
}
message = messageValue
if let message = messageValue {
messages = [message]
} else {
messages = []
}
combinedReadState = nil
isRemovedFromTotalUnreadCount = false
embeddedState = nil
@ -752,10 +808,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
hasFailedMessages = false
}
if let messageValue = message {
if let messageValue = messages.last {
for media in messageValue.media {
if let media = media as? TelegramMediaAction, case .historyCleared = media.action {
message = nil
messages = []
}
}
}
@ -824,7 +880,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var hideAuthor = false
switch contentPeer {
case let .chat(itemPeer):
var (peer, initialHideAuthor, messageText) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, message: message, chatPeer: itemPeer, accountPeerId: item.context.account.peerId, enableMediaEmoji: !enableChatListPhotos, isPeerGroup: isPeerGroup)
var (peer, initialHideAuthor, messageText) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, messages: messages, chatPeer: itemPeer, accountPeerId: item.context.account.peerId, enableMediaEmoji: !enableChatListPhotos, isPeerGroup: isPeerGroup)
if case let .psa(_, maybePsaText) = promoInfo, let psaText = maybePsaText {
initialHideAuthor = true
@ -843,19 +899,23 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var inlineAuthorPrefix: String?
if case .groupReference = item.content {
if let author = message?.author as? TelegramUser {
if let author = messages.last?.author as? TelegramUser {
if author.id == item.context.account.peerId {
inlineAuthorPrefix = item.presentationData.strings.DialogList_You
} else if message?.id.peerId.namespace != Namespaces.Peer.CloudUser && message?.id.peerId.namespace != Namespaces.Peer.SecretChat {
} else if messages.last?.id.peerId.namespace != Namespaces.Peer.CloudUser && messages.last?.id.peerId.namespace != Namespaces.Peer.SecretChat {
inlineAuthorPrefix = author.compactDisplayTitle
}
}
}
var contentImageMedia: Media?
var chatListText: (String, String)?
var chatListSearchResult: CachedChatListSearchResult?
let contentImageSize = CGSize(width: 18.0, height: 18.0)
let contentImageSpacing: CGFloat = 2.0
let contentImageTrailingSpace: CGFloat = 5.0
var contentImageSpecs: [(message: Message, media: Media, size: CGSize)] = []
switch contentData {
case let .chat(itemPeer, _, _, text):
var peerText: String?
@ -863,7 +923,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
if let messagePeer = itemPeer.chatMainPeer {
peerText = messagePeer.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
}
} else if let message = message, let author = message.author as? TelegramUser, let peer = itemPeer.chatMainPeer, !(peer is TelegramUser) {
} else if let message = messages.last, let author = message.author as? TelegramUser, let peer = itemPeer.chatMainPeer, !(peer is TelegramUser) {
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
} else if !displayAsMessage {
peerText = author.id == account.peerId ? item.presentationData.strings.DialogList_You : author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
@ -884,7 +944,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
authorAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_Draft, font: textFont, textColor: theme.messageDraftTextColor)
attributedText = NSAttributedString(string: foldLineBreaks(embeddedState.text.string.replacingOccurrences(of: "\n\n", with: " ")), font: textFont, textColor: theme.messageTextColor)
} else if let message = message {
} else if let message = messages.last {
let composedString: NSMutableAttributedString
if let inlineAuthorPrefix = inlineAuthorPrefix {
composedString = NSMutableAttributedString()
@ -920,28 +980,49 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
authorAttributedString = NSAttributedString(string: peerText, font: textFont, textColor: theme.authorNameColor)
}
if enableChatListPhotos && !message.containsSecretMedia {
for media in message.media {
if let image = media as? TelegramMediaImage {
textLeftCutout += 26.0
contentImageMedia = image
break
} else if let file = media as? TelegramMediaFile {
if file.isVideo && !file.isInstantVideo {
textLeftCutout += 26.0
contentImageMedia = file
break
}
} else if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
if let image = content.image {
textLeftCutout += 26.0
contentImageMedia = image
break
} else if let file = content.file {
if file.isVideo && !file.isInstantVideo {
textLeftCutout += 26.0
contentImageMedia = file
break
var displayMediaPreviews = true
if message.containsSecretMedia {
displayMediaPreviews = false
} else if let _ = message.peers[message.id.peerId] as? TelegramSecretChat {
displayMediaPreviews = false
}
if displayMediaPreviews {
let contentImageFillSize = CGSize(width: 8.0, height: contentImageSize.height)
_ = contentImageFillSize
for message in messages {
inner: for media in message.media {
if let image = media as? TelegramMediaImage {
if let _ = largestImageRepresentation(image.representations) {
//let imageSize = largest.dimensions.cgSize
//let fitSize = imageSize.aspectFilled(contentImageFillSize)
let fitSize = contentImageSize
contentImageSpecs.append((message, image, fitSize))
}
break inner
} else if let file = media as? TelegramMediaFile {
if file.isVideo, !file.isInstantVideo, let _ = file.dimensions {
//let imageSize = dimensions.cgSize
//let fitSize = imageSize.aspectFilled(contentImageFillSize)
let fitSize = contentImageSize
contentImageSpecs.append((message, file, fitSize))
}
break inner
} else if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
let imageTypes = ["photo", "video", "embed", "gif", "document", "telegram_album"]
if let image = content.image, let type = content.type, imageTypes.contains(type) {
if let _ = largestImageRepresentation(image.representations) {
//let imageSize = largest.dimensions.cgSize
let fitSize = contentImageSize
contentImageSpecs.append((message, image, fitSize))
}
break inner
} else if let file = content.file {
if file.isVideo, !file.isInstantVideo, let _ = file.dimensions {
//let imageSize = dimensions.cgSize
let fitSize = contentImageSize
contentImageSpecs.append((message, file, fitSize))
}
break inner
}
}
}
@ -980,9 +1061,19 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
attributedText = textString
}
for i in 0 ..< contentImageSpecs.count {
if i != 0 {
textLeftCutout += contentImageSpacing
}
textLeftCutout += contentImageSpecs[i].size.width
if i == contentImageSpecs.count - 1 {
textLeftCutout += contentImageTrailingSpace
}
}
switch contentData {
case let .chat(itemPeer, _, _, _):
if let message = message, let author = message.author as? TelegramUser, displayAsMessage {
if let message = messages.last, let author = message.author as? TelegramUser, displayAsMessage {
titleAttributedString = NSAttributedString(string: author.id == account.peerId ? item.presentationData.strings.DialogList_You : author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder), font: titleFont, textColor: theme.titleColor)
} else if isPeerGroup {
titleAttributedString = NSAttributedString(string: item.presentationData.strings.ChatList_ArchivedChatsTitle, font: titleFont, textColor: theme.titleColor)
@ -1024,7 +1115,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
dateAttributedString = NSAttributedString(string: dateText, font: dateFont, textColor: theme.dateTextColor)
}
if !isPeerGroup, let message = message, message.author?.id == account.peerId && !hasDraft {
if !isPeerGroup, let message = messages.last, message.author?.id == account.peerId && !hasDraft {
if message.flags.isSending && !message.isSentOrAcknowledged {
statusState = .clock(PresentationResourcesChatList.clockFrameImage(item.presentationData.theme), PresentationResourcesChatList.clockMinImage(item.presentationData.theme))
} else if message.id.peerId != account.peerId {
@ -1041,7 +1132,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
if unreadCount.unread {
if !isPeerGroup, let message = message, message.tags.contains(.unseenPersonalMessage), unreadCount.count == 1 {
if !isPeerGroup, let message = messages.last, message.tags.contains(.unseenPersonalMessage), unreadCount.count == 1 {
} else {
let badgeTextColor: UIColor
if unreadCount.muted {
@ -1111,8 +1202,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var credibilityIconOffset: CGFloat = 0.0
if displayAsMessage {
switch item.content {
case let .peer(message, _, _, _, _, _, _, _, _, _, _, _):
if let peer = message?.author {
case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _):
if let peer = messages.last?.author {
if peer.isScam {
currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(item.presentationData.theme, type: .regular)
credibilityIconOffset = 2.0
@ -1184,7 +1275,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
if !textLeftCutout.isZero {
textCutout = TextNodeCutout(topLeft: CGSize(width: textLeftCutout, height: 10.0), topRight: nil, bottomRight: nil)
}
let (textLayout, textApply) = textLayout(TextNodeLayoutArguments(attributedString: textAttributedString, backgroundColor: nil, maximumNumberOfLines: authorAttributedString == nil ? 2 : 1, truncationType: .end, constrainedSize: CGSize(width: rawContentWidth - badgeSize - textLeftCutout, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: textCutout, insets: UIEdgeInsets(top: 2.0, left: 1.0, bottom: 2.0, right: 1.0)))
let (textLayout, textApply) = textLayout(TextNodeLayoutArguments(attributedString: textAttributedString, backgroundColor: nil, maximumNumberOfLines: authorAttributedString == nil ? 2 : 1, truncationType: .end, constrainedSize: CGSize(width: rawContentWidth - badgeSize, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: textCutout, insets: UIEdgeInsets(top: 2.0, left: 1.0, bottom: 2.0, right: 1.0)))
let titleRectWidth = rawContentWidth - dateLayout.size.width - 10.0 - statusWidth - titleIconsWidth
let (titleLayout, titleApply) = titleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: titleRectWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
@ -1241,20 +1332,6 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
peerLeftRevealOptions = []
}
var updateImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?
if let contentImageMedia = contentImageMedia {
if let currentContentImageMedia = currentContentImageMedia, contentImageMedia.isSemanticallyEqual(to: currentContentImageMedia) {
} else {
if let message = message {
if let image = contentImageMedia as? TelegramMediaImage {
updateImageSignal = mediaGridMessagePhoto(account: item.context.account, photoReference: .message(message: MessageReference(message), media: image))
} else if let file = contentImageMedia as? TelegramMediaFile {
updateImageSignal = mediaGridMessageVideo(postbox: item.context.account.postbox, videoReference: .message(message: MessageReference(message), media: file), autoFetchFullSizeThumbnail: true)
}
}
}
}
let (onlineLayout, onlineApply) = onlineLayout(online)
var animateContent = false
if let currentItem = currentItem, currentItem.content.chatLocation == item.content.chatLocation {
@ -1287,8 +1364,6 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
let layout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: max(0.0, itemHeight + heightOffset)), insets: insets)
let contentImageSize = CGSize(width: 18.0, height: 18.0)
var customActions: [ChatListItemAccessibilityCustomAction] = []
for option in peerLeftRevealOptions {
customActions.append(ChatListItemAccessibilityCustomAction(name: option.title, target: nil, selector: #selector(ChatListItemNode.performLocalAccessibilityCustomAction(_:)), key: option.key))
@ -1301,42 +1376,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
if let strongSelf = self {
strongSelf.layoutParams = (item, first, last, firstWithHeader, nextIsPinned, params, countersSize)
strongSelf.currentItemHeight = itemHeight
strongSelf.contentImageMedia = contentImageMedia
strongSelf.cachedChatListText = chatListText
strongSelf.cachedChatListSearchResult = chatListSearchResult
strongSelf.contextContainer.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
var dimensions: CGSize?
if let contentImageMedia = contentImageMedia as? TelegramMediaImage {
dimensions = largestRepresentationForPhoto(contentImageMedia)?.dimensions.cgSize
} else if let contentImageMedia = contentImageMedia as? TelegramMediaFile {
dimensions = contentImageMedia.dimensions?.cgSize
}
var contentImageNodeAppeared = false
if let dimensions = dimensions {
let makeImageLayout = strongSelf.contentImageNode.asyncLayout()
let imageSize = contentImageSize
let applyImageLayout = makeImageLayout(TransformImageArguments(corners: ImageCorners(radius: 2.0), imageSize: dimensions.aspectFilled(imageSize), boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))
applyImageLayout()
if let updateImageSignal = updateImageSignal {
strongSelf.contentImageNode.setSignal(updateImageSignal)
if currentContentImageMedia == nil {
strongSelf.contentImageNode.isHidden = false
contentImageNodeAppeared = true
}
}
} else {
if currentContentImageMedia != nil {
strongSelf.contentImageNode.removeFromSupernode()
strongSelf.contentImageNode.setSignal(.single({ _ in nil }))
strongSelf.contentImageNode.isHidden = true
}
}
if case .groupReference = item.content {
strongSelf.layer.sublayerTransform = CATransform3DMakeTranslation(0.0, layout.contentSize.height - itemHeight, 0.0)
}
@ -1541,12 +1585,6 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
strongSelf.authorNode.frame = authorNodeFrame
let textNodeFrame = CGRect(origin: CGPoint(x: contentRect.origin.x, y: contentRect.minY + titleLayout.size.height - 1.0 + UIScreenPixel + (authorLayout.size.height.isZero ? 0.0 : (authorLayout.size.height - 3.0))), size: textLayout.size)
strongSelf.textNode.frame = textNodeFrame
let contentImageFrame = CGRect(origin: textNodeFrame.origin.offsetBy(dx: 1.0, dy: 2.0), size: contentImageSize)
if contentImageNodeAppeared {
strongSelf.contentImageNode.frame = contentImageFrame
} else {
transition.updateFrame(node: strongSelf.contentImageNode, frame: contentImageFrame)
}
var animateInputActivitiesFrame = false
if let inputActivities = inputActivities, !inputActivities.isEmpty {
@ -1586,7 +1624,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
}
if let inputActivitiesSize = inputActivitiesSize {
let inputActivitiesFrame = CGRect(origin: CGPoint(x: authorNodeFrame.minX + 1.0, y: authorNodeFrame.minY + UIScreenPixel), size: inputActivitiesSize)
let inputActivitiesFrame = CGRect(origin: CGPoint(x: contentRect.minX, y: authorNodeFrame.minY + UIScreenPixel), size: inputActivitiesSize)
if animateInputActivitiesFrame {
transition.updateFrame(node: strongSelf.inputActivitiesNode, frame: inputActivitiesFrame)
} else {
@ -1595,6 +1633,43 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
inputActivitiesApply?()
var mediaPreviewOffset = textNodeFrame.origin.offsetBy(dx: 1.0, dy: 2.0)
var validMediaIds: [MediaId] = []
for (message, media, mediaSize) in contentImageSpecs {
guard let mediaId = media.id else {
continue
}
validMediaIds.append(mediaId)
let previewNode: ChatListMediaPreviewNode
var previewNodeTransition = transition
var previewNodeAlphaTransition: ContainedViewLayoutTransition = .animated(duration: 0.15, curve: .easeInOut)
if let current = strongSelf.mediaPreviewNodes[mediaId] {
previewNode = current
} else {
previewNodeTransition = .immediate
previewNodeAlphaTransition = .immediate
previewNode = ChatListMediaPreviewNode(context: item.context, message: message, media: media)
strongSelf.mediaPreviewNodes[mediaId] = previewNode
strongSelf.contextContainer.addSubnode(previewNode)
}
previewNode.updateLayout(size: mediaSize, synchronousLoads: synchronousLoads)
previewNodeAlphaTransition.updateAlpha(node: previewNode, alpha: strongSelf.inputActivitiesNode.alpha.isZero ? 1.0 : 0.0)
previewNodeTransition.updateFrame(node: previewNode, frame: CGRect(origin: mediaPreviewOffset, size: mediaSize))
mediaPreviewOffset.x += mediaSize.width + contentImageSpacing
}
var removeMediaIds: [MediaId] = []
for (mediaId, itemNode) in strongSelf.mediaPreviewNodes {
if !validMediaIds.contains(mediaId) {
removeMediaIds.append(mediaId)
itemNode.removeFromSupernode()
}
}
for mediaId in removeMediaIds {
strongSelf.mediaPreviewNodes.removeValue(forKey: mediaId)
}
strongSelf.currentMediaPreviewSpecs = contentImageSpecs
strongSelf.currentTextLeftCutout = textLeftCutout
if !contentDelta.x.isZero || !contentDelta.y.isZero {
let titlePosition = strongSelf.titleNode.position
transition.animatePosition(node: strongSelf.titleNode, from: CGPoint(x: titlePosition.x - contentDelta.x, y: titlePosition.y - contentDelta.y))
@ -1749,9 +1824,17 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
textFrame.origin.x = contentRect.origin.x
transition.updateFrameAdditive(node: self.textNode, frame: textFrame)
var contentImageFrame = self.contentImageNode.frame
contentImageFrame.origin = textFrame.origin.offsetBy(dx: 1.0, dy: 2.0)
transition.updateFrame(node: self.contentImageNode, frame: contentImageFrame)
var mediaPreviewOffset = textFrame.origin.offsetBy(dx: 1.0, dy: 2.0)
let contentImageSpacing: CGFloat = 2.0
for (_, media, mediaSize) in self.currentMediaPreviewSpecs {
guard let mediaId = media.id else {
continue
}
if let previewNode = self.mediaPreviewNodes[mediaId] {
transition.updateFrame(node: previewNode, frame: CGRect(origin: mediaPreviewOffset, size: mediaSize))
}
mediaPreviewOffset.x += mediaSize.width + contentImageSpacing
}
let dateFrame = self.dateNode.frame
transition.updateFrame(node: self.dateNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x + contentRect.size.width - dateFrame.size.width, y: dateFrame.minY), size: dateFrame.size))

View File

@ -7,9 +7,44 @@ import TelegramUIPreferences
import TelegramStringFormatting
import LocalizedPeerData
public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, message: Message?, chatPeer: RenderedPeer, accountPeerId: PeerId, enableMediaEmoji: Bool = true, isPeerGroup: Bool = false) -> (peer: Peer?, hideAuthor: Bool, messageText: String) {
private enum MessageGroupType {
case photos
case videos
case generic
}
private func singleMessageType(message: Message) -> MessageGroupType {
for media in message.media {
if let _ = media as? TelegramMediaImage {
return .photos
} else if let file = media as? TelegramMediaFile {
if file.isVideo && !file.isInstantVideo {
return .videos
}
}
}
return .generic
}
private func messageGroupType(messages: [Message]) -> MessageGroupType {
if messages.isEmpty {
return .generic
}
let currentType = singleMessageType(message: messages[0])
for i in 1 ..< messages.count {
let nextType = singleMessageType(message: messages[i])
if nextType != currentType {
return .generic
}
}
return currentType
}
public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, messages: [Message], chatPeer: RenderedPeer, accountPeerId: PeerId, enableMediaEmoji: Bool = true, isPeerGroup: Bool = false) -> (peer: Peer?, hideAuthor: Bool, messageText: String) {
let peer: Peer?
let message = messages.last
var hideAuthor = false
var messageText: String
if let message = message {
@ -19,151 +54,178 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder:
peer = chatPeer.chatMainPeer
}
messageText = message.text
messageText = messages[0].text
for media in message.media {
switch media {
case _ as TelegramMediaImage:
if message.text.isEmpty {
messageText = strings.Message_Photo
} else if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
if enableMediaEmoji {
messageText = "🖼 \(messageText)"
var textIsReady = false
if messages.count > 1 {
let groupType = messageGroupType(messages: messages)
switch groupType {
case .photos:
if !messageText.isEmpty {
textIsReady = true
} else {
messageText = strings.ChatList_MessagePhotos(Int32(messages.count))
textIsReady = true
}
case .videos:
if !messageText.isEmpty {
textIsReady = true
} else {
messageText = strings.ChatList_MessageVideos(Int32(messages.count))
textIsReady = true
}
case .generic:
break
}
}
if !textIsReady {
for media in message.media {
switch media {
case _ as TelegramMediaImage:
if message.text.isEmpty {
messageText = strings.Message_Photo
} else if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
if enableMediaEmoji {
messageText = "🖼 \(messageText)"
}
}
}
case let fileMedia as TelegramMediaFile:
var processed = false
inner: for attribute in fileMedia.attributes {
switch attribute {
case .Animated:
messageText = strings.Message_Animation
processed = true
break inner
case let .Audio(isVoice, _, title, performer, _):
if !message.text.isEmpty {
messageText = "🎤 \(messageText)"
} else if isVoice {
if message.text.isEmpty {
messageText = strings.Message_Audio
} else {
case let fileMedia as TelegramMediaFile:
var processed = false
inner: for attribute in fileMedia.attributes {
switch attribute {
case .Animated:
messageText = strings.Message_Animation
processed = true
break inner
case let .Audio(isVoice, _, title, performer, _):
if !message.text.isEmpty {
messageText = "🎤 \(messageText)"
}
processed = true
break inner
} else {
let descriptionString: String
if let title = title, let performer = performer, !title.isEmpty, !performer.isEmpty {
descriptionString = title + "" + performer
} else if let title = title, !title.isEmpty {
descriptionString = title
} else if let performer = performer, !performer.isEmpty {
descriptionString = performer
} else if let fileName = fileMedia.fileName {
descriptionString = fileName
} else {
descriptionString = strings.Message_Audio
}
messageText = descriptionString
processed = true
break inner
}
case let .Sticker(displayText, _, _):
if displayText.isEmpty {
messageText = strings.Message_Sticker
processed = true
break inner
} else {
messageText = strings.Message_StickerText(displayText).0
processed = true
break inner
}
case let .Video(_, _, flags):
if flags.contains(.instantRoundVideo) {
messageText = strings.Message_VideoMessage
processed = true
break inner
} else {
if message.text.isEmpty {
messageText = strings.Message_Video
processed = true
} else if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
if !fileMedia.isAnimated && enableMediaEmoji {
messageText = "📹 \(messageText)"
processed = true
} else if isVoice {
if message.text.isEmpty {
messageText = strings.Message_Audio
} else {
messageText = "🎤 \(messageText)"
}
processed = true
break inner
} else {
let descriptionString: String
if let title = title, let performer = performer, !title.isEmpty, !performer.isEmpty {
descriptionString = title + "" + performer
} else if let title = title, !title.isEmpty {
descriptionString = title
} else if let performer = performer, !performer.isEmpty {
descriptionString = performer
} else if let fileName = fileMedia.fileName {
descriptionString = fileName
} else {
descriptionString = strings.Message_Audio
}
messageText = descriptionString
processed = true
break inner
}
case let .Sticker(displayText, _, _):
if displayText.isEmpty {
messageText = strings.Message_Sticker
processed = true
break inner
} else {
messageText = strings.Message_StickerText(displayText).0
processed = true
break inner
}
case let .Video(_, _, flags):
if flags.contains(.instantRoundVideo) {
messageText = strings.Message_VideoMessage
processed = true
break inner
} else {
if message.text.isEmpty {
messageText = strings.Message_Video
processed = true
} else if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
if enableMediaEmoji {
if !fileMedia.isAnimated {
messageText = "📹 \(messageText)"
}
}
processed = true
break inner
}
}
default:
break
}
}
if !processed {
if !message.text.isEmpty {
messageText = "📎 \(messageText)"
} else {
if fileMedia.isAnimatedSticker {
messageText = strings.Message_Sticker
} else {
if let fileName = fileMedia.fileName {
messageText = fileName
} else {
messageText = strings.Message_File
}
}
}
}
case let location as TelegramMediaMap:
if location.liveBroadcastingTimeout != nil {
messageText = strings.Message_LiveLocation
} else {
messageText = strings.Message_Location
}
case _ as TelegramMediaContact:
messageText = strings.Message_Contact
case let game as TelegramMediaGame:
messageText = "🎮 \(game.title)"
case let invoice as TelegramMediaInvoice:
messageText = invoice.title
case let action as TelegramMediaAction:
switch action.action {
case let .phoneCall(_, discardReason, _):
hideAuthor = !isPeerGroup
let incoming = message.flags.contains(.Incoming)
if let discardReason = discardReason {
switch discardReason {
case .busy, .disconnect:
messageText = strings.Notification_CallCanceled
case .missed:
messageText = incoming ? strings.Notification_CallMissed : strings.Notification_CallCanceled
case .hangup:
break
}
}
if messageText.isEmpty {
if incoming {
messageText = strings.Notification_CallIncoming
} else {
messageText = strings.Notification_CallOutgoing
}
}
default:
break
hideAuthor = true
if let text = plainServiceMessageString(strings: strings, nameDisplayOrder: nameDisplayOrder, message: message, accountPeerId: accountPeerId) {
messageText = text
}
}
}
if !processed {
if !message.text.isEmpty {
messageText = "📎 \(messageText)"
} else {
if fileMedia.isAnimatedSticker {
messageText = strings.Message_Sticker
} else {
if let fileName = fileMedia.fileName {
messageText = fileName
} else {
messageText = strings.Message_File
}
}
case _ as TelegramMediaExpiredContent:
if let text = plainServiceMessageString(strings: strings, nameDisplayOrder: nameDisplayOrder, message: message, accountPeerId: accountPeerId) {
messageText = text
}
}
case let location as TelegramMediaMap:
if location.liveBroadcastingTimeout != nil {
messageText = strings.Message_LiveLocation
} else {
messageText = strings.Message_Location
}
case _ as TelegramMediaContact:
messageText = strings.Message_Contact
case let game as TelegramMediaGame:
messageText = "🎮 \(game.title)"
case let invoice as TelegramMediaInvoice:
messageText = invoice.title
case let action as TelegramMediaAction:
switch action.action {
case let .phoneCall(_, discardReason, _):
hideAuthor = !isPeerGroup
let incoming = message.flags.contains(.Incoming)
if let discardReason = discardReason {
switch discardReason {
case .busy, .disconnect:
messageText = strings.Notification_CallCanceled
case .missed:
messageText = incoming ? strings.Notification_CallMissed : strings.Notification_CallCanceled
case .hangup:
break
}
}
if messageText.isEmpty {
if incoming {
messageText = strings.Notification_CallIncoming
} else {
messageText = strings.Notification_CallOutgoing
}
}
default:
hideAuthor = true
if let text = plainServiceMessageString(strings: strings, nameDisplayOrder: nameDisplayOrder, message: message, accountPeerId: accountPeerId) {
messageText = text
}
}
case _ as TelegramMediaExpiredContent:
if let text = plainServiceMessageString(strings: strings, nameDisplayOrder: nameDisplayOrder, message: message, accountPeerId: accountPeerId) {
messageText = text
}
case let poll as TelegramMediaPoll:
messageText = "📊 \(poll.text)"
case let dice as TelegramMediaDice:
messageText = dice.emoji
default:
break
case let poll as TelegramMediaPoll:
messageText = "📊 \(poll.text)"
case let dice as TelegramMediaDice:
messageText = dice.emoji
default:
break
}
}
}
} else {

View File

@ -176,10 +176,10 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
nodeInteraction.additionalCategorySelected(id)
}
), directionHint: entry.directionHint)
case let .PeerEntry(index, presentationData, message, combinedReadState, isRemovedFromTotalUnreadCount, embeddedState, peer, presence, summaryInfo, editing, hasActiveRevealControls, selected, inputActivities, promoInfo, hasFailedMessages, isContact):
case let .PeerEntry(index, presentationData, messages, combinedReadState, isRemovedFromTotalUnreadCount, embeddedState, peer, presence, summaryInfo, editing, hasActiveRevealControls, selected, inputActivities, promoInfo, hasFailedMessages, isContact):
switch mode {
case .chatList:
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, context: context, peerGroupId: peerGroupId, filterData: filterData, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, presence: presence, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, promoInfo: promoInfo, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: hasFailedMessages), editing: editing, hasActiveRevealControls: hasActiveRevealControls, selected: selected, header: nil, enableContextActions: true, hiddenOffset: false, interaction: nodeInteraction), directionHint: entry.directionHint)
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, context: context, peerGroupId: peerGroupId, filterData: filterData, index: index, content: .peer(messages: messages, peer: peer, combinedReadState: combinedReadState, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, presence: presence, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, promoInfo: promoInfo, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: hasFailedMessages), editing: editing, hasActiveRevealControls: hasActiveRevealControls, selected: selected, header: nil, enableContextActions: true, hiddenOffset: false, interaction: nodeInteraction), directionHint: entry.directionHint)
case let .peers(filter, isSelecting, _, filters):
let itemPeer = peer.chatMainPeer
var chatPeer: Peer?
@ -290,10 +290,10 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatListNodeInteraction, peerGroupId: PeerGroupId, filterData: ChatListItemFilterData?, mode: ChatListNodeMode, entries: [ChatListNodeViewTransitionUpdateEntry]) -> [ListViewUpdateItem] {
return entries.map { entry -> ListViewUpdateItem in
switch entry.entry {
case let .PeerEntry(index, presentationData, message, combinedReadState, isRemovedFromTotalUnreadCount, embeddedState, peer, presence, summaryInfo, editing, hasActiveRevealControls, selected, inputActivities, promoInfo, hasFailedMessages, isContact):
case let .PeerEntry(index, presentationData, messages, combinedReadState, isRemovedFromTotalUnreadCount, embeddedState, peer, presence, summaryInfo, editing, hasActiveRevealControls, selected, inputActivities, promoInfo, hasFailedMessages, isContact):
switch mode {
case .chatList:
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, context: context, peerGroupId: peerGroupId, filterData: filterData, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, presence: presence, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, promoInfo: promoInfo, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: hasFailedMessages), editing: editing, hasActiveRevealControls: hasActiveRevealControls, selected: selected, header: nil, enableContextActions: true, hiddenOffset: false, interaction: nodeInteraction), directionHint: entry.directionHint)
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, context: context, peerGroupId: peerGroupId, filterData: filterData, index: index, content: .peer(messages: messages, peer: peer, combinedReadState: combinedReadState, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, presence: presence, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, promoInfo: promoInfo, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: hasFailedMessages), editing: editing, hasActiveRevealControls: hasActiveRevealControls, selected: selected, header: nil, enableContextActions: true, hiddenOffset: false, interaction: nodeInteraction), directionHint: entry.directionHint)
case let .peers(filter, isSelecting, _, filters):
let itemPeer = peer.chatMainPeer
var chatPeer: Peer?

View File

@ -46,7 +46,7 @@ public enum ChatListNodeEntryPromoInfo: Equatable {
enum ChatListNodeEntry: Comparable, Identifiable {
case HeaderEntry
case PeerEntry(index: ChatListIndex, presentationData: ChatListPresentationData, message: Message?, readState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: PeerChatListEmbeddedInterfaceState?, peer: RenderedPeer, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, inputActivities: [(Peer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, hasFailedMessages: Bool, isContact: Bool)
case PeerEntry(index: ChatListIndex, presentationData: ChatListPresentationData, messages: [Message], readState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: PeerChatListEmbeddedInterfaceState?, peer: RenderedPeer, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, inputActivities: [(Peer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, hasFailedMessages: Bool, isContact: Bool)
case HoleEntry(ChatListHole, theme: PresentationTheme)
case GroupReferenceEntry(index: ChatListIndex, presentationData: ChatListPresentationData, groupId: PeerGroupId, peers: [ChatListGroupReferencePeer], message: Message?, editing: Bool, unreadState: PeerGroupUnreadCountersCombinedSummary, revealed: Bool, hiddenByDefault: Bool)
case ArchiveIntro(presentationData: ChatListPresentationData)
@ -98,27 +98,33 @@ enum ChatListNodeEntry: Comparable, Identifiable {
} else {
return false
}
case let .PeerEntry(lhsIndex, lhsPresentationData, lhsMessage, lhsUnreadCount, lhsIsRemovedFromTotalUnreadCount, lhsEmbeddedState, lhsPeer, lhsPresence, lhsSummaryInfo, lhsEditing, lhsHasRevealControls, lhsSelected, lhsInputActivities, lhsAd, lhsHasFailedMessages, lhsIsContact):
case let .PeerEntry(lhsIndex, lhsPresentationData, lhsMessages, lhsUnreadCount, lhsIsRemovedFromTotalUnreadCount, lhsEmbeddedState, lhsPeer, lhsPresence, lhsSummaryInfo, lhsEditing, lhsHasRevealControls, lhsSelected, lhsInputActivities, lhsAd, lhsHasFailedMessages, lhsIsContact):
switch rhs {
case let .PeerEntry(rhsIndex, rhsPresentationData, rhsMessage, rhsUnreadCount, rhsIsRemovedFromTotalUnreadCount, rhsEmbeddedState, rhsPeer, rhsPresence, rhsSummaryInfo, rhsEditing, rhsHasRevealControls, rhsSelected, rhsInputActivities, rhsAd, rhsHasFailedMessages, rhsIsContact):
case let .PeerEntry(rhsIndex, rhsPresentationData, rhsMessages, rhsUnreadCount, rhsIsRemovedFromTotalUnreadCount, rhsEmbeddedState, rhsPeer, rhsPresence, rhsSummaryInfo, rhsEditing, rhsHasRevealControls, rhsSelected, rhsInputActivities, rhsAd, rhsHasFailedMessages, rhsIsContact):
if lhsIndex != rhsIndex {
return false
}
if lhsPresentationData !== rhsPresentationData {
return false
}
if lhsMessage?.stableVersion != rhsMessage?.stableVersion {
if lhsUnreadCount != rhsUnreadCount {
return false
}
if lhsMessage?.id != rhsMessage?.id || lhsMessage?.flags != rhsMessage?.flags || lhsUnreadCount != rhsUnreadCount {
if lhsMessages.count != rhsMessages.count {
return false
}
if let lhsMessage = lhsMessage, let rhsMessage = rhsMessage {
if lhsMessage.associatedMessages.count != rhsMessage.associatedMessages.count {
for i in 0 ..< lhsMessages.count {
if lhsMessages[i].stableVersion != rhsMessages[i].stableVersion {
return false
}
for (id, message) in lhsMessage.associatedMessages {
if let otherMessage = rhsMessage.associatedMessages[id] {
if lhsMessages[i].id != rhsMessages[i].id {
return false
}
if lhsMessages[i].associatedMessages.count != rhsMessages[i].associatedMessages.count {
return false
}
for (id, message) in lhsMessages[i].associatedMessages {
if let otherMessage = rhsMessages[i].associatedMessages[id] {
if message.stableVersion != otherMessage.stableVersion {
return false
}
@ -295,20 +301,20 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState,
var filterAfterHole = false
loop: for entry in view.entries {
switch entry {
case let .MessageEntry(index, message, combinedReadState, isRemovedFromTotalUnreadCount, embeddedState, peer, peerPresence, summaryInfo, hasFailed, isContact):
case let .MessageEntry(index, messages, combinedReadState, isRemovedFromTotalUnreadCount, embeddedState, peer, peerPresence, summaryInfo, hasFailed, isContact):
if let savedMessagesPeer = savedMessagesPeer, savedMessagesPeer.id == index.messageIndex.id.peerId {
continue loop
}
if state.pendingRemovalPeerIds.contains(index.messageIndex.id.peerId) {
continue loop
}
var updatedMessage = message
var updatedMessages = messages
var updatedCombinedReadState = combinedReadState
if state.pendingClearHistoryPeerIds.contains(index.messageIndex.id.peerId) {
updatedMessage = nil
updatedMessages = []
updatedCombinedReadState = nil
}
result.append(.PeerEntry(index: offsetPinnedIndex(index, offset: pinnedIndexOffset), presentationData: state.presentationData, message: updatedMessage, readState: updatedCombinedReadState, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: embeddedState, peer: peer, presence: peerPresence, summaryInfo: summaryInfo, editing: state.editing, hasActiveRevealControls: index.messageIndex.id.peerId == state.peerIdWithRevealedOptions, selected: state.selectedPeerIds.contains(index.messageIndex.id.peerId), inputActivities: state.peerInputActivities?.activities[index.messageIndex.id.peerId], promoInfo: nil, hasFailedMessages: hasFailed, isContact: isContact))
result.append(.PeerEntry(index: offsetPinnedIndex(index, offset: pinnedIndexOffset), presentationData: state.presentationData, messages: updatedMessages, readState: updatedCombinedReadState, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: embeddedState, peer: peer, presence: peerPresence, summaryInfo: summaryInfo, editing: state.editing, hasActiveRevealControls: index.messageIndex.id.peerId == state.peerIdWithRevealedOptions, selected: state.selectedPeerIds.contains(index.messageIndex.id.peerId), inputActivities: state.peerInputActivities?.activities[index.messageIndex.id.peerId], promoInfo: nil, hasFailedMessages: hasFailed, isContact: isContact))
case let .HoleEntry(hole):
if hole.index.timestamp == Int32.max - 1 {
//return ([.HeaderEntry], true)
@ -321,7 +327,7 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState,
var pinningIndex: UInt16 = UInt16(pinnedIndexOffset == 0 ? 0 : (pinnedIndexOffset - 1))
if let savedMessagesPeer = savedMessagesPeer {
result.append(.PeerEntry(index: ChatListIndex.absoluteUpperBound.predecessor, presentationData: state.presentationData, message: nil, readState: nil, isRemovedFromTotalUnreadCount: false, embeddedInterfaceState: nil, peer: RenderedPeer(peerId: savedMessagesPeer.id, peers: SimpleDictionary([savedMessagesPeer.id: savedMessagesPeer])), presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), editing: state.editing, hasActiveRevealControls: false, selected: false, inputActivities: nil, promoInfo: nil, hasFailedMessages: false, isContact: false))
result.append(.PeerEntry(index: ChatListIndex.absoluteUpperBound.predecessor, presentationData: state.presentationData, messages: [], readState: nil, isRemovedFromTotalUnreadCount: false, embeddedInterfaceState: nil, peer: RenderedPeer(peerId: savedMessagesPeer.id, peers: SimpleDictionary([savedMessagesPeer.id: savedMessagesPeer])), presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), editing: state.editing, hasActiveRevealControls: false, selected: false, inputActivities: nil, promoInfo: nil, hasFailedMessages: false, isContact: false))
} else {
if !filteredAdditionalItemEntries.isEmpty {
for item in filteredAdditionalItemEntries.reversed() {
@ -336,8 +342,8 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState,
promoInfo = .psa(type: type, message: message)
}
switch item.entry {
case let .MessageEntry(index, message, combinedReadState, isRemovedFromTotalUnreadCount, embeddedState, peer, peerPresence, summaryInfo, hasFailed, isContact):
result.append(.PeerEntry(index: ChatListIndex(pinningIndex: pinningIndex, messageIndex: index.messageIndex), presentationData: state.presentationData, message: message, readState: combinedReadState, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: embeddedState, peer: peer, presence: peerPresence, summaryInfo: summaryInfo, editing: state.editing, hasActiveRevealControls: index.messageIndex.id.peerId == state.peerIdWithRevealedOptions, selected: state.selectedPeerIds.contains(index.messageIndex.id.peerId), inputActivities: state.peerInputActivities?.activities[index.messageIndex.id.peerId], promoInfo: promoInfo, hasFailedMessages: hasFailed, isContact: isContact))
case let .MessageEntry(index, messages, combinedReadState, isRemovedFromTotalUnreadCount, embeddedState, peer, peerPresence, summaryInfo, hasFailed, isContact):
result.append(.PeerEntry(index: ChatListIndex(pinningIndex: pinningIndex, messageIndex: index.messageIndex), presentationData: state.presentationData, messages: messages, readState: combinedReadState, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: embeddedState, peer: peer, presence: peerPresence, summaryInfo: summaryInfo, editing: state.editing, hasActiveRevealControls: index.messageIndex.id.peerId == state.peerIdWithRevealedOptions, selected: state.selectedPeerIds.contains(index.messageIndex.id.peerId), inputActivities: state.peerInputActivities?.activities[index.messageIndex.id.peerId], promoInfo: promoInfo, hasFailedMessages: hasFailed, isContact: isContact))
if pinningIndex != 0 {
pinningIndex -= 1
}

View File

@ -127,6 +127,12 @@ public class ImageNode: ASDisplayNode {
private var first = true
private let enableEmpty: Bool
private let _contentReady = Promise<Bool>()
private var didSetReady: Bool = false
public var contentReady: Signal<Bool, NoError> {
return self._contentReady.get()
}
public var ready: Signal<Bool, NoError> {
if let hasImage = self.hasImage {
return hasImage.get()
@ -171,6 +177,10 @@ public class ImageNode: ASDisplayNode {
hasImage.set(true)
}
}
if !strongSelf.didSetReady {
strongSelf.didSetReady = true
strongSelf._contentReady.set(.single(true))
}
}
}
}))

View File

@ -905,6 +905,7 @@ public class TextNode: ASDisplayNode {
var strikethroughs: [TextNodeStrikethrough] = []
var lineConstrainedWidth = constrainedSize.width
var lineConstrainedWidthDelta: CGFloat = 0.0
var lineOriginY = floorToScreenPixels(layoutSize.height + fontAscent)
if !first {
lineOriginY += fontLineSpacing
@ -915,6 +916,7 @@ public class TextNode: ASDisplayNode {
if cutoutEnabled {
if lineOriginY - fontLineHeight < cutoutMaxY && lineOriginY + fontLineHeight > cutoutMinY {
lineConstrainedWidth = max(1.0, lineConstrainedWidth - cutoutWidth)
lineConstrainedWidthDelta = -cutoutWidth
lineCutoutOffset = cutoutOffset
lineAdditionalWidth = cutoutWidth
}
@ -945,6 +947,7 @@ public class TextNode: ASDisplayNode {
let originalLine = CTTypesetterCreateLineWithOffset(typesetter, lineRange, 0.0)
var lineConstrainedSize = constrainedSize
lineConstrainedSize.width += lineConstrainedWidthDelta
if bottomCutoutEnabled {
lineConstrainedSize.width -= bottomCutoutSize.width
}

View File

@ -710,12 +710,13 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
} else if let _ = self.scrubbingTimestampValue {
needsAnimation = false
} else if let statusValue = self.statusValue {
if case .buffering(true, _) = statusValue.status {
switch statusValue.status {
case .buffering:
needsAnimation = false
} else if Double(0.0).isLess(than: statusValue.duration) {
case .paused:
needsAnimation = false
case .playing:
needsAnimation = true
} else {
needsAnimation = false
}
} else {
needsAnimation = false

View File

@ -10,7 +10,6 @@ private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: Unsa
let data: Signal<(Data, Bool), NoError>
let resourceSize: Int = context.size
let readCount = min(256 * 1024, Int(bufferSize))
let requestRange: Range<Int> = context.readingOffset ..< (context.readingOffset + readCount)
@ -22,7 +21,7 @@ private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: Unsa
let requiredDataIsNotLocallyAvailable = context.requiredDataIsNotLocallyAvailable
var fetchedData: Data?
let fetchDisposable = MetaDisposable()
let isInitialized = context.videoStream != nil
let isInitialized = context.videoStream != nil || context.automaticallyFetchHeader
let mediaBox = context.mediaBox
let reference = context.fileReference.resourceReference(context.fileReference.media.resource)
let disposable = data.start(next: { result in
@ -94,6 +93,7 @@ private final class UniversalSoftwareVideoSourceImpl {
fileprivate let mediaBox: MediaBox
fileprivate let fileReference: FileMediaReference
fileprivate let size: Int
fileprivate let automaticallyFetchHeader: Bool
fileprivate let state: ValuePromise<UniversalSoftwareVideoSourceState>
@ -108,7 +108,7 @@ private final class UniversalSoftwareVideoSourceImpl {
fileprivate var currentNumberOfReads: Int = 0
fileprivate var currentReadBytes: Int = 0
init?(mediaBox: MediaBox, fileReference: FileMediaReference, state: ValuePromise<UniversalSoftwareVideoSourceState>, cancelInitialization: Signal<Bool, NoError>) {
init?(mediaBox: MediaBox, fileReference: FileMediaReference, state: ValuePromise<UniversalSoftwareVideoSourceState>, cancelInitialization: Signal<Bool, NoError>, automaticallyFetchHeader: Bool) {
guard let size = fileReference.media.size else {
return nil
}
@ -116,6 +116,7 @@ private final class UniversalSoftwareVideoSourceImpl {
self.mediaBox = mediaBox
self.fileReference = fileReference
self.size = size
self.automaticallyFetchHeader = automaticallyFetchHeader
self.state = state
state.set(.initializing)
@ -140,6 +141,9 @@ private final class UniversalSoftwareVideoSourceImpl {
return nil
}
print("Header read in \(self.currentReadBytes) bytes")
self.currentReadBytes = 0
self.avFormatContext = avFormatContext
var videoStream: SoftwareVideoStream?
@ -251,15 +255,17 @@ private final class UniversalSoftwareVideoSourceImpl {
self.currentNumberOfReads = 0
self.currentReadBytes = 0
for _ in 0 ..< 10 {
for i in 0 ..< 10 {
let _ = i
let (decodableFrame, loop) = self.readDecodableFrame()
if let decodableFrame = decodableFrame {
if let renderedFrame = videoStream.decoder.render(frame: decodableFrame) {
//print("Frame rendered in \(self.currentNumberOfReads) reads, \(self.currentReadBytes) bytes, total frames read: \(i + 1)")
print("Frame rendered in \(self.currentNumberOfReads) reads, \(self.currentReadBytes) bytes, total frames read: \(i + 1)")
return (renderedFrame, CGFloat(videoStream.rotationAngle), CGFloat(videoStream.aspect), loop)
}
}
}
print("No frame in \(self.currentReadBytes / 1024) KB")
return (nil, CGFloat(videoStream.rotationAngle), CGFloat(videoStream.aspect), true)
}
}
@ -276,12 +282,20 @@ private final class UniversalSoftwareVideoSourceThreadParams: NSObject {
let fileReference: FileMediaReference
let state: ValuePromise<UniversalSoftwareVideoSourceState>
let cancelInitialization: Signal<Bool, NoError>
let automaticallyFetchHeader: Bool
init(mediaBox: MediaBox, fileReference: FileMediaReference, state: ValuePromise<UniversalSoftwareVideoSourceState>, cancelInitialization: Signal<Bool, NoError>) {
init(
mediaBox: MediaBox,
fileReference: FileMediaReference,
state: ValuePromise<UniversalSoftwareVideoSourceState>,
cancelInitialization: Signal<Bool, NoError>,
automaticallyFetchHeader: Bool
) {
self.mediaBox = mediaBox
self.fileReference = fileReference
self.state = state
self.cancelInitialization = cancelInitialization
self.automaticallyFetchHeader = automaticallyFetchHeader
}
}
@ -306,7 +320,7 @@ private final class UniversalSoftwareVideoSourceThread: NSObject {
let timer = Timer(fireAt: .distantFuture, interval: 0.0, target: UniversalSoftwareVideoSourceThread.self, selector: #selector(UniversalSoftwareVideoSourceThread.none), userInfo: nil, repeats: false)
runLoop.add(timer, forMode: .common)
let source = UniversalSoftwareVideoSourceImpl(mediaBox: params.mediaBox, fileReference: params.fileReference, state: params.state, cancelInitialization: params.cancelInitialization)
let source = UniversalSoftwareVideoSourceImpl(mediaBox: params.mediaBox, fileReference: params.fileReference, state: params.state, cancelInitialization: params.cancelInitialization, automaticallyFetchHeader: params.automaticallyFetchHeader)
Thread.current.threadDictionary["source"] = source
while true {
@ -342,17 +356,17 @@ private final class UniversalSoftwareVideoSourceThread: NSObject {
}
}
enum UniversalSoftwareVideoSourceTakeFrameResult {
public enum UniversalSoftwareVideoSourceTakeFrameResult {
case waitingForData
case image(UIImage?)
}
final class UniversalSoftwareVideoSource {
public final class UniversalSoftwareVideoSource {
private let thread: Thread
private let stateValue: ValuePromise<UniversalSoftwareVideoSourceState> = ValuePromise(.initializing, ignoreRepeated: true)
private let cancelInitialization: ValuePromise<Bool> = ValuePromise(false)
var ready: Signal<Bool, NoError> {
public var ready: Signal<Bool, NoError> {
return self.stateValue.get()
|> map { value -> Bool in
switch value {
@ -364,8 +378,8 @@ final class UniversalSoftwareVideoSource {
}
}
init(mediaBox: MediaBox, fileReference: FileMediaReference) {
self.thread = Thread(target: UniversalSoftwareVideoSourceThread.self, selector: #selector(UniversalSoftwareVideoSourceThread.entryPoint(_:)), object: UniversalSoftwareVideoSourceThreadParams(mediaBox: mediaBox, fileReference: fileReference, state: self.stateValue, cancelInitialization: self.cancelInitialization.get()))
public init(mediaBox: MediaBox, fileReference: FileMediaReference, automaticallyFetchHeader: Bool = false) {
self.thread = Thread(target: UniversalSoftwareVideoSourceThread.self, selector: #selector(UniversalSoftwareVideoSourceThread.entryPoint(_:)), object: UniversalSoftwareVideoSourceThreadParams(mediaBox: mediaBox, fileReference: fileReference, state: self.stateValue, cancelInitialization: self.cancelInitialization.get(), automaticallyFetchHeader: automaticallyFetchHeader))
self.thread.name = "UniversalSoftwareVideoSource"
self.thread.start()
}

View File

@ -2129,7 +2129,7 @@ private func avatarGalleryPhotoDatas(account: Account, fileReference: FileMediaR
}
}
}
} |> filter({ $0._0 != nil || $0._1 != nil })
}
return signal
} else {

View File

@ -88,7 +88,7 @@ public struct ChatListGroupReferenceEntry: Equatable {
}
public enum ChatListEntry: Comparable {
case MessageEntry(index: ChatListIndex, message: Message?, readState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: PeerChatListEmbeddedInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, hasFailed: Bool, isContact: Bool)
case MessageEntry(index: ChatListIndex, messages: [Message], readState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: PeerChatListEmbeddedInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, hasFailed: Bool, isContact: Bool)
case HoleEntry(ChatListHole)
public var index: ChatListIndex {
@ -102,18 +102,23 @@ public enum ChatListEntry: Comparable {
public static func ==(lhs: ChatListEntry, rhs: ChatListEntry) -> Bool {
switch lhs {
case let .MessageEntry(lhsIndex, lhsMessage, lhsReadState, lhsIsRemovedFromTotalUnreadCount, lhsEmbeddedState, lhsPeer, lhsPresence, lhsInfo, lhsHasFailed, lhsIsContact):
case let .MessageEntry(lhsIndex, lhsMessages, lhsReadState, lhsIsRemovedFromTotalUnreadCount, lhsEmbeddedState, lhsPeer, lhsPresence, lhsInfo, lhsHasFailed, lhsIsContact):
switch rhs {
case let .MessageEntry(rhsIndex, rhsMessage, rhsReadState, rhsIsRemovedFromTotalUnreadCount, rhsEmbeddedState, rhsPeer, rhsPresence, rhsInfo, rhsHasFailed, rhsIsContact):
case let .MessageEntry(rhsIndex, rhsMessages, rhsReadState, rhsIsRemovedFromTotalUnreadCount, rhsEmbeddedState, rhsPeer, rhsPresence, rhsInfo, rhsHasFailed, rhsIsContact):
if lhsIndex != rhsIndex {
return false
}
if lhsReadState != rhsReadState {
return false
}
if lhsMessage?.stableVersion != rhsMessage?.stableVersion {
if lhsMessages.count != rhsMessages.count {
return false
}
for i in 0 ..< lhsMessages.count {
if lhsMessages[i].stableVersion != rhsMessages[i].stableVersion {
return false
}
}
if lhsIsRemovedFromTotalUnreadCount != rhsIsRemovedFromTotalUnreadCount {
return false
}
@ -180,7 +185,7 @@ public enum ChatListEntry: Comparable {
enum MutableChatListEntry: Equatable {
case IntermediateMessageEntry(index: ChatListIndex, messageIndex: MessageIndex?)
case MessageEntry(index: ChatListIndex, message: Message?, readState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: PeerChatListEmbeddedInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, tagSummaryInfo: ChatListMessageTagSummaryInfo, hasFailedMessages: Bool, isContact: Bool)
case MessageEntry(index: ChatListIndex, messages: [Message], readState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: PeerChatListEmbeddedInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, tagSummaryInfo: ChatListMessageTagSummaryInfo, hasFailedMessages: Bool, isContact: Bool)
case HoleEntry(ChatListHole)
init(_ intermediateEntry: ChatListIntermediateEntry, cachedDataTable: CachedPeerDataTable, readStateTable: MessageHistoryReadStateTable, messageHistoryTable: MessageHistoryTable) {
@ -557,11 +562,11 @@ final class MutableChatListView {
private func renderEntry(_ entry: MutableChatListEntry, postbox: Postbox, renderMessage: (IntermediateMessage) -> Message, getPeer: (PeerId) -> Peer?, getPeerNotificationSettings: (PeerId) -> PeerNotificationSettings?, getPeerPresence: (PeerId) -> PeerPresence?) -> MutableChatListEntry? {
switch entry {
case let .IntermediateMessageEntry(index, messageIndex):
let renderedMessage: Message?
var renderedMessages: [Message] = []
if let messageIndex = messageIndex {
renderedMessage = postbox.messageHistoryTable.getMessage(messageIndex).flatMap(renderMessage)
} else {
renderedMessage = nil
if let messageGroup = postbox.messageHistoryTable.getMessageGroup(at: messageIndex, limit: 10) {
renderedMessages.append(contentsOf: messageGroup.compactMap(renderMessage))
}
}
var peers = SimpleDictionary<PeerId, Peer>()
var notificationSettings: PeerNotificationSettings?
@ -586,7 +591,7 @@ final class MutableChatListView {
let tagSummaryCount: Int32? = nil
let actionsSummaryCount: Int32? = nil
return .MessageEntry(index: index, message: renderedMessage, readState: postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId), notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: false, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId)?.chatListEmbeddedState, renderedPeer: RenderedPeer(peerId: index.messageIndex.id.peerId, peers: peers), presence: presence, tagSummaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: tagSummaryCount, actionsSummaryCount: actionsSummaryCount), hasFailedMessages: postbox.messageHistoryFailedTable.contains(peerId: index.messageIndex.id.peerId), isContact: isContact)
return .MessageEntry(index: index, messages: renderedMessages, readState: postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId), notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: false, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId)?.chatListEmbeddedState, renderedPeer: RenderedPeer(peerId: index.messageIndex.id.peerId, peers: peers), presence: presence, tagSummaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: tagSummaryCount, actionsSummaryCount: actionsSummaryCount), hasFailedMessages: postbox.messageHistoryFailedTable.contains(peerId: index.messageIndex.id.peerId), isContact: isContact)
default:
return nil
}
@ -615,8 +620,8 @@ public final class ChatListView {
var entries: [ChatListEntry] = []
for entry in mutableView.sampledState.entries {
switch entry {
case let .MessageEntry(index, message, combinedReadState, _, isRemovedFromTotalUnreadCount, embeddedState, peer, peerPresence, summaryInfo, hasFailed, isContact):
entries.append(.MessageEntry(index: index, message: message, readState: combinedReadState, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: embeddedState, renderedPeer: peer, presence: peerPresence, summaryInfo: summaryInfo, hasFailed: hasFailed, isContact: isContact))
case let .MessageEntry(index, messages, combinedReadState, _, isRemovedFromTotalUnreadCount, embeddedState, peer, peerPresence, summaryInfo, hasFailed, isContact):
entries.append(.MessageEntry(index: index, messages: messages, readState: combinedReadState, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: embeddedState, renderedPeer: peer, presence: peerPresence, summaryInfo: summaryInfo, hasFailed: hasFailed, isContact: isContact))
case let .HoleEntry(hole):
entries.append(.HoleEntry(hole))
case .IntermediateMessageEntry:
@ -633,9 +638,9 @@ public final class ChatListView {
var additionalItemEntries: [ChatListAdditionalItemEntry] = []
for entry in mutableView.additionalItemEntries {
switch entry.entry {
case let .MessageEntry(index, message, combinedReadState, _, isExcludedFromUnreadCount, embeddedState, peer, peerPresence, summaryInfo, hasFailed, isContact):
case let .MessageEntry(index, messages, combinedReadState, _, isExcludedFromUnreadCount, embeddedState, peer, peerPresence, summaryInfo, hasFailed, isContact):
additionalItemEntries.append(ChatListAdditionalItemEntry(
entry: .MessageEntry(index: index, message: message, readState: combinedReadState, isRemovedFromTotalUnreadCount: isExcludedFromUnreadCount, embeddedInterfaceState: embeddedState, renderedPeer: peer, presence: peerPresence, summaryInfo: summaryInfo, hasFailed: hasFailed, isContact: isContact),
entry: .MessageEntry(index: index, messages: messages, readState: combinedReadState, isRemovedFromTotalUnreadCount: isExcludedFromUnreadCount, embeddedInterfaceState: embeddedState, renderedPeer: peer, presence: peerPresence, summaryInfo: summaryInfo, hasFailed: hasFailed, isContact: isContact),
info: entry.info
))
case .HoleEntry:

View File

@ -601,13 +601,13 @@ private final class ChatListViewSpaceState {
if self.orderedEntries.mutableScan({ entry in
switch entry {
case let .MessageEntry(messageEntry):
if let peer = messageEntry.renderedPeer.peer {
case let .MessageEntry(index, messages, readState, notificationSettings, isRemovedFromTotalUnreadCount, embeddedInterfaceState, renderedPeer, presence, tagSummaryInfo, hasFailedMessages, isContact):
if let peer = renderedPeer.peer {
let notificationsPeerId = peer.notificationSettingsPeerId ?? peer.id
if let (_, updated) = transaction.currentUpdatedPeerNotificationSettings[notificationsPeerId] {
let isRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettings, peer: peer, peerSettings: updated)
return .MessageEntry(index: messageEntry.index, message: messageEntry.message, readState: messageEntry.readState, notificationSettings: updated, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: messageEntry.embeddedInterfaceState, renderedPeer: messageEntry.renderedPeer, presence: messageEntry.presence, tagSummaryInfo: messageEntry.tagSummaryInfo, hasFailedMessages: messageEntry.hasFailedMessages, isContact: messageEntry.isContact)
return .MessageEntry(index: index, messages: messages, readState: readState, notificationSettings: updated, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: embeddedInterfaceState, renderedPeer: renderedPeer, presence: presence, tagSummaryInfo: tagSummaryInfo, hasFailedMessages: hasFailedMessages, isContact: isContact)
} else {
return nil
}
@ -627,7 +627,7 @@ private final class ChatListViewSpaceState {
switch entry {
case let .MessageEntry(messageEntry):
if transaction.updatedFailedMessagePeerIds.contains(messageEntry.index.messageIndex.id.peerId) {
return .MessageEntry(index: messageEntry.index, message: messageEntry.message, readState: messageEntry.readState, notificationSettings: messageEntry.notificationSettings, isRemovedFromTotalUnreadCount: messageEntry.isRemovedFromTotalUnreadCount, embeddedInterfaceState: messageEntry.embeddedInterfaceState, renderedPeer: messageEntry.renderedPeer, presence: messageEntry.presence, tagSummaryInfo: messageEntry.tagSummaryInfo, hasFailedMessages: postbox.messageHistoryFailedTable.contains(peerId: messageEntry.index.messageIndex.id.peerId), isContact: messageEntry.isContact)
return .MessageEntry(index: messageEntry.index, messages: messageEntry.messages, readState: messageEntry.readState, notificationSettings: messageEntry.notificationSettings, isRemovedFromTotalUnreadCount: messageEntry.isRemovedFromTotalUnreadCount, embeddedInterfaceState: messageEntry.embeddedInterfaceState, renderedPeer: messageEntry.renderedPeer, presence: messageEntry.presence, tagSummaryInfo: messageEntry.tagSummaryInfo, hasFailedMessages: postbox.messageHistoryFailedTable.contains(peerId: messageEntry.index.messageIndex.id.peerId), isContact: messageEntry.isContact)
} else {
return nil
}
@ -643,14 +643,18 @@ private final class ChatListViewSpaceState {
if self.orderedEntries.mutableScan({ entry in
switch entry {
case let .MessageEntry(messageEntry):
var updatedMessage: Message?
if let message = messageEntry.message {
updatedMessage = updateMessagePeers(message, updatedPeers: transaction.currentUpdatedPeers)
var updatedMessages: [Message] = messageEntry.messages
var hasUpdatedMessages = false
for i in 0 ..< updatedMessages.count {
if let updatedMessage = updateMessagePeers(updatedMessages[i], updatedPeers: transaction.currentUpdatedPeers) {
updatedMessages[i] = updatedMessage
hasUpdatedMessages = true
}
}
let renderedPeer = updatedRenderedPeer(messageEntry.renderedPeer, updatedPeers: transaction.currentUpdatedPeers)
if updatedMessage != nil || renderedPeer != nil {
return .MessageEntry(index: messageEntry.index, message: updatedMessage ?? messageEntry.message, readState: messageEntry.readState, notificationSettings: messageEntry.notificationSettings, isRemovedFromTotalUnreadCount: messageEntry.isRemovedFromTotalUnreadCount, embeddedInterfaceState: messageEntry.embeddedInterfaceState, renderedPeer: renderedPeer ?? messageEntry.renderedPeer, presence: messageEntry.presence, tagSummaryInfo: messageEntry.tagSummaryInfo, hasFailedMessages: messageEntry.hasFailedMessages, isContact: messageEntry.isContact)
if hasUpdatedMessages || renderedPeer != nil {
return .MessageEntry(index: messageEntry.index, messages: updatedMessages, readState: messageEntry.readState, notificationSettings: messageEntry.notificationSettings, isRemovedFromTotalUnreadCount: messageEntry.isRemovedFromTotalUnreadCount, embeddedInterfaceState: messageEntry.embeddedInterfaceState, renderedPeer: renderedPeer ?? messageEntry.renderedPeer, presence: messageEntry.presence, tagSummaryInfo: messageEntry.tagSummaryInfo, hasFailedMessages: messageEntry.hasFailedMessages, isContact: messageEntry.isContact)
} else {
return nil
}
@ -671,7 +675,7 @@ private final class ChatListViewSpaceState {
presencePeerId = associatedPeerId
}
if let presence = transaction.currentUpdatedPeerPresences[presencePeerId] {
return .MessageEntry(index: messageEntry.index, message: messageEntry.message, readState: messageEntry.readState, notificationSettings: messageEntry.notificationSettings, isRemovedFromTotalUnreadCount: messageEntry.isRemovedFromTotalUnreadCount, embeddedInterfaceState: messageEntry.embeddedInterfaceState, renderedPeer: messageEntry.renderedPeer, presence: presence, tagSummaryInfo: messageEntry.tagSummaryInfo, hasFailedMessages: messageEntry.hasFailedMessages, isContact: messageEntry.isContact)
return .MessageEntry(index: messageEntry.index, messages: messageEntry.messages, readState: messageEntry.readState, notificationSettings: messageEntry.notificationSettings, isRemovedFromTotalUnreadCount: messageEntry.isRemovedFromTotalUnreadCount, embeddedInterfaceState: messageEntry.embeddedInterfaceState, renderedPeer: messageEntry.renderedPeer, presence: presence, tagSummaryInfo: messageEntry.tagSummaryInfo, hasFailedMessages: messageEntry.hasFailedMessages, isContact: messageEntry.isContact)
} else {
return nil
}
@ -821,7 +825,7 @@ private final class ChatListViewSpaceState {
if updatedTagSummaryCount != nil || updatedActionsSummaryCount != nil {
let summaryInfo = ChatListMessageTagSummaryInfo(tagSummaryCount: updatedTagSummaryCount ?? messageEntry.tagSummaryInfo.tagSummaryCount, actionsSummaryCount: updatedActionsSummaryCount ?? messageEntry.tagSummaryInfo.actionsSummaryCount)
return .MessageEntry(index: messageEntry.index, message: messageEntry.message, readState: messageEntry.readState, notificationSettings: messageEntry.notificationSettings, isRemovedFromTotalUnreadCount: messageEntry.isRemovedFromTotalUnreadCount, embeddedInterfaceState: messageEntry.embeddedInterfaceState, renderedPeer: messageEntry.renderedPeer, presence: messageEntry.presence, tagSummaryInfo: summaryInfo, hasFailedMessages: messageEntry.hasFailedMessages, isContact: messageEntry.isContact)
return .MessageEntry(index: messageEntry.index, messages: messageEntry.messages, readState: messageEntry.readState, notificationSettings: messageEntry.notificationSettings, isRemovedFromTotalUnreadCount: messageEntry.isRemovedFromTotalUnreadCount, embeddedInterfaceState: messageEntry.embeddedInterfaceState, renderedPeer: messageEntry.renderedPeer, presence: messageEntry.presence, tagSummaryInfo: summaryInfo, hasFailedMessages: messageEntry.hasFailedMessages, isContact: messageEntry.isContact)
} else {
return nil
}
@ -1376,7 +1380,14 @@ struct ChatListViewState {
isRemovedFromTotalUnreadCount = false
}
let updatedEntry: MutableChatListEntry = .MessageEntry(index: index, message: messageIndex.flatMap(postbox.messageHistoryTable.getMessage).flatMap(postbox.renderIntermediateMessage), readState: postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId), notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId)?.chatListEmbeddedState, renderedPeer: renderedPeer, presence: postbox.peerPresenceTable.get(index.messageIndex.id.peerId), tagSummaryInfo: tagSummaryInfo, hasFailedMessages: false, isContact: postbox.contactsTable.isContact(peerId: index.messageIndex.id.peerId))
var renderedMessages: [Message] = []
if let messageIndex = messageIndex {
if let messageGroup = postbox.messageHistoryTable.getMessageGroup(at: messageIndex, limit: 10) {
renderedMessages.append(contentsOf: messageGroup.compactMap(postbox.renderIntermediateMessage))
}
}
let updatedEntry: MutableChatListEntry = .MessageEntry(index: index, messages: renderedMessages, readState: postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId), notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId)?.chatListEmbeddedState, renderedPeer: renderedPeer, presence: postbox.peerPresenceTable.get(index.messageIndex.id.peerId), tagSummaryInfo: tagSummaryInfo, hasFailedMessages: false, isContact: postbox.contactsTable.isContact(peerId: index.messageIndex.id.peerId))
if directionIndex == 0 {
self.stateBySpace[space]!.orderedEntries.setLowerOrAtAnchorAtArrayIndex(listIndex, to: updatedEntry)
} else {

View File

@ -235,22 +235,22 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
let timestamp = self.referenceTimestamp
let timestamp1 = timestamp + 120
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex(id: MessageId(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp1, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: selfPeer, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer1), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 0, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex(id: MessageId(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp1, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: selfPeer, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer1), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 0, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
let presenceTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 + 60 * 60)
let timestamp2 = timestamp + 3660
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer2.id, namespace: 0, id: 0), timestamp: timestamp2)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer2.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp2, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer2, text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer2), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: TelegramUserPresence(status: .present(until: presenceTimestamp), lastActivity: presenceTimestamp), summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: [(peer2, .typingText)], promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer2.id, namespace: 0, id: 0), timestamp: timestamp2)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer2.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp2, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer2, text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer2), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: TelegramUserPresence(status: .present(until: presenceTimestamp), lastActivity: presenceTimestamp), summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: [(peer2, .typingText)], promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
let timestamp3 = timestamp + 3200
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer3.id, namespace: 0, id: 0), timestamp: timestamp3)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer3.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp3, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer3Author, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer3), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer3.id, namespace: 0, id: 0), timestamp: timestamp3)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer3.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp3, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer3Author, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer3), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
let timestamp4 = timestamp + 3000
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer4.id, namespace: 0, id: 0), timestamp: timestamp4)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp4, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer4, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer4), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer4.id, namespace: 0, id: 0), timestamp: timestamp4)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp4, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer4, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer4), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
let timestamp5 = timestamp + 1000
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer5.id, namespace: 0, id: 0), timestamp: timestamp5)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp5, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer5, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer5), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer5.id, namespace: 0, id: 0), timestamp: timestamp5)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp5, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer5, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer5), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer6.id, namespace: 0, id: 0), timestamp: timestamp - 360)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer6.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp - 360, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer6, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer6), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 1, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer6.id, namespace: 0, id: 0), timestamp: timestamp - 360)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer6.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp - 360, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer6, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer6), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 1, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
let width: CGFloat
if case .regular = layout.metrics.widthClass {

View File

@ -786,17 +786,17 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
let timestamp = self.referenceTimestamp
let timestamp1 = timestamp + 120
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex(id: MessageId(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp1, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: selfPeer, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer1), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 0, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex(id: MessageId(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp1, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: selfPeer, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer1), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 0, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
let presenceTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 + 60 * 60)
let timestamp2 = timestamp + 3660
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer2.id, namespace: 0, id: 0), timestamp: timestamp2)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer2.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp2, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer2, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer2), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 1, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: TelegramUserPresence(status: .present(until: presenceTimestamp), lastActivity: presenceTimestamp), summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer2.id, namespace: 0, id: 0), timestamp: timestamp2)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer2.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp2, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer2, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer2), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 1, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: TelegramUserPresence(status: .present(until: presenceTimestamp), lastActivity: presenceTimestamp), summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
let timestamp3 = timestamp + 3200
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer3.id, namespace: 0, id: 0), timestamp: timestamp3)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer3.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp3, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer3Author, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer3), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer3.id, namespace: 0, id: 0), timestamp: timestamp3)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer3.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp3, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer3Author, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer3), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
let timestamp4 = timestamp + 3000
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer4.id, namespace: 0, id: 0), timestamp: timestamp4)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp4, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer4, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer4), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer4.id, namespace: 0, id: 0), timestamp: timestamp4)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp4, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer4, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer4), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
if let chatNodes = self.chatNodes {

View File

@ -373,24 +373,24 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
let timestamp = self.referenceTimestamp
let timestamp1 = timestamp + 120
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex(id: MessageId(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp1, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: selfPeer, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer1), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 0, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex(id: MessageId(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp1, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: selfPeer, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer1), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 0, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
let presenceTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 + 60 * 60)
let timestamp2 = timestamp + 3660
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer2.id, namespace: 0, id: 0), timestamp: timestamp2)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer2.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp2, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer2, text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer2), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: TelegramUserPresence(status: .present(until: presenceTimestamp), lastActivity: presenceTimestamp), summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: [(peer2, .typingText)], promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer2.id, namespace: 0, id: 0), timestamp: timestamp2)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer2.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp2, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer2, text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer2), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: TelegramUserPresence(status: .present(until: presenceTimestamp), lastActivity: presenceTimestamp), summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: [(peer2, .typingText)], promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
let timestamp3 = timestamp + 3200
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer3.id, namespace: 0, id: 0), timestamp: timestamp3)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer3.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp3, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer3Author, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer3), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer3.id, namespace: 0, id: 0), timestamp: timestamp3)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer3.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp3, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer3Author, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer3), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
let timestamp4 = timestamp + 3000
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer4.id, namespace: 0, id: 0), timestamp: timestamp4)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp4, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer4, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer4), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer4.id, namespace: 0, id: 0), timestamp: timestamp4)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp4, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer4, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer4), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
let timestamp5 = timestamp + 1000
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer5.id, namespace: 0, id: 0), timestamp: timestamp5)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp5, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer5, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer5), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer5.id, namespace: 0, id: 0), timestamp: timestamp5)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp5, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer5, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer5), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer6.id, namespace: 0, id: 0), timestamp: timestamp - 360)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer6.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp - 360, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer6, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer6), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 1, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer6.id, namespace: 0, id: 0), timestamp: timestamp - 360)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer6.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp - 360, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer6, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer6), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 1, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer7.id, namespace: 0, id: 0), timestamp: timestamp - 420)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer7.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp - 420, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer6, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer7), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer7.id, namespace: 0, id: 0), timestamp: timestamp - 420)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer7.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp - 420, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer6, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer7), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
let width: CGFloat
if case .regular = layout.metrics.widthClass {

View File

@ -23,7 +23,9 @@ final class AudioWaveformNode: ASDisplayNode {
var progress: CGFloat? {
didSet {
self.setNeedsDisplay()
if self.progress != oldValue {
self.setNeedsDisplay()
}
}
}

View File

@ -3680,6 +3680,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let strongSelf = self, let (searchResult, searchState, searchLocation) = searchResult {
let controller = ChatSearchResultsController(context: strongSelf.context, location: searchLocation, searchQuery: searchData.query, searchResult: searchResult, searchState: searchState, navigateToMessageIndex: { index in
guard let strongSelf = self else {
return
}
strongSelf.interfaceInteraction?.navigateMessageSearch(.index(index))
}, resultsUpdated: { results, state in
guard let strongSelf = self else {

View File

@ -206,7 +206,7 @@ final class ChatMessageAccessibilityData {
let authorName = item.message.author?.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
if let chatPeer = item.message.peers[item.message.id.peerId] {
let (_, _, messageText) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, message: item.message, chatPeer: RenderedPeer(peer: chatPeer), accountPeerId: item.context.account.peerId)
let (_, _, messageText) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, messages: [item.message], chatPeer: RenderedPeer(peer: chatPeer), accountPeerId: item.context.account.peerId)
var text = messageText

View File

@ -78,7 +78,7 @@ private enum ChatListSearchEntry: Comparable, Identifiable {
public func item(context: AccountContext, interaction: ChatListNodeInteraction) -> ListViewItem {
switch self {
case let .message(message, peer, readState, presentationData):
return ChatListItem(presentationData: presentationData, context: context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: message.index), content: .peer(message: message, peer: peer, combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: true, displayAsMessage: true, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
return ChatListItem(presentationData: presentationData, context: context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: message.index), content: .peer(messages: [message], peer: peer, combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: true, displayAsMessage: true, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
}
}
}
@ -195,7 +195,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
}
switch item.content {
case let .peer(peer):
if let message = peer.message {
if let message = peer.messages.first {
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.peer.peerId), subject: .message(message.id), botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single([]), reactionItems: [], gesture: gesture)

View File

@ -11,6 +11,102 @@ import PhotoResources
import RadialStatusNode
import TelegramStringFormatting
import GridMessageSelectionNode
import UniversalMediaPlayer
private final class FrameSequenceThumbnailNode: ASDisplayNode {
private let context: AccountContext
private let file: FileMediaReference
private let imageNode: ASImageNode
private var isPlaying: Bool = false
private var isPlayingInternal: Bool = false
private var frames: [Int: UIImage] = [:]
private var frameTimes: [Double] = []
private var sources: [UniversalSoftwareVideoSource] = []
private var disposables: [Int: Disposable] = [:]
private var currentFrameIndex: Int = 0
private var timer: SwiftSignalKit.Timer?
init(
context: AccountContext,
file: FileMediaReference
) {
self.context = context
self.file = file
self.imageNode = ASImageNode()
self.imageNode.isUserInteractionEnabled = false
self.imageNode.contentMode = .scaleAspectFill
self.imageNode.clipsToBounds = true
if let duration = file.media.duration {
let frameCount = 5
let frameInterval: Double = Double(duration) / Double(frameCount)
for i in 0 ..< frameCount {
self.frameTimes.append(Double(i) * frameInterval)
}
}
super.init()
self.addSubnode(self.imageNode)
for i in 0 ..< self.frameTimes.count {
let framePts = self.frameTimes[i]
let index = i
let source = UniversalSoftwareVideoSource(
mediaBox: self.context.account.postbox.mediaBox,
fileReference: self.file,
automaticallyFetchHeader: true
)
self.sources.append(source)
self.disposables[index] = (source.takeFrame(at: framePts)
|> deliverOnMainQueue).start(next: { [weak self] result in
guard let strongSelf = self else {
return
}
if case let .image(image) = result {
if let image = image {
strongSelf.frames[index] = image
}
}
})
}
}
deinit {
for (_, disposable) in self.disposables {
disposable.dispose()
}
self.timer?.invalidate()
}
func updateIsPlaying(_ isPlaying: Bool) {
if self.isPlaying == isPlaying {
return
}
self.isPlaying = isPlaying
}
func updateLayout(size: CGSize) {
self.imageNode.frame = CGRect(origin: CGPoint(), size: size)
}
func tick() {
let isPlayingInternal = self.isPlaying && self.frames.count == self.frameTimes.count
if isPlayingInternal {
self.currentFrameIndex = (self.currentFrameIndex + 1) % self.frames.count
if self.currentFrameIndex < self.frames.count {
self.imageNode.image = self.frames[self.currentFrameIndex]
}
}
}
}
private let mediaBadgeBackgroundColor = UIColor(white: 0.0, alpha: 0.6)
private let mediaBadgeTextColor = UIColor.white
@ -43,6 +139,8 @@ private final class VisualMediaItemNode: ASDisplayNode {
private var displayLink: ConstantDisplayLinkAnimator?
private var displayLinkTimestamp: Double = 0.0
private var frameSequenceThumbnailNode: FrameSequenceThumbnailNode?
private let containerNode: ContextControllerSourceNode
private let imageNode: TransformImageNode
private var statusNode: RadialStatusNode
@ -351,6 +449,37 @@ private final class VisualMediaItemNode: ASDisplayNode {
}
}
self.displayLink?.isPaused = !self.hasVisibility || self.isHidden
if isVisible {
if let item = self.item?.0, let file = self.item?.1 as? TelegramMediaFile, !file.isAnimated {
if self.frameSequenceThumbnailNode == nil {
let frameSequenceThumbnailNode = FrameSequenceThumbnailNode(context: context, file: .message(message: MessageReference(item.message), media: file))
self.frameSequenceThumbnailNode = frameSequenceThumbnailNode
self.imageNode.addSubnode(frameSequenceThumbnailNode)
}
if let frameSequenceThumbnailNode = self.frameSequenceThumbnailNode {
let size = self.bounds.size
frameSequenceThumbnailNode.frame = CGRect(origin: CGPoint(), size: size)
frameSequenceThumbnailNode.updateLayout(size: size)
}
} else {
if let frameSequenceThumbnailNode = self.frameSequenceThumbnailNode {
self.frameSequenceThumbnailNode = nil
frameSequenceThumbnailNode.removeFromSupernode()
}
}
} else {
if let frameSequenceThumbnailNode = self.frameSequenceThumbnailNode {
self.frameSequenceThumbnailNode = nil
frameSequenceThumbnailNode.removeFromSupernode()
}
}
self.frameSequenceThumbnailNode?.updateIsPlaying(isVisible)
}
func tick() {
self.frameSequenceThumbnailNode?.tick()
}
func updateSelectionState(animated: Bool) {
@ -666,6 +795,8 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
private var decelerationAnimator: ConstantDisplayLinkAnimator?
private var animationTimer: SwiftSignalKit.Timer?
init(context: AccountContext, chatControllerInteraction: ChatControllerInteraction, peerId: PeerId, contentType: ContentType) {
self.context = context
self.peerId = peerId
@ -720,11 +851,23 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
itemNode.updateHiddenMedia()
}
})
let animationTimer = SwiftSignalKit.Timer(timeout: 0.3, repeat: true, completion: { [weak self] in
guard let strongSelf = self else {
return
}
for (_, itemNode) in strongSelf.visibleMediaItems {
itemNode.tick()
}
}, queue: .mainQueue())
self.animationTimer = animationTimer
animationTimer.start()
}
deinit {
self.listDisposable.dispose()
self.hiddenMediaDisposable?.dispose()
self.animationTimer?.invalidate()
}
private func requestHistoryAroundVisiblePosition() {

View File

@ -814,14 +814,16 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
self.listContainerTransformNode.addSubnode(self.listContainerNode)
self.addSubnode(self.listContainerTransformNode)
let avatarReady = self.avatarContainerNode.avatarNode.ready
let avatarReady = (self.avatarContainerNode.avatarNode.ready
|> mapToSignal { _ -> Signal<Bool, NoError> in
return .complete()
}
|> then(.single(true))
|> then(.single(true)))
let galleryReady = self.listContainerNode.isReady.get()
|> filter { $0 }
|> filter { value in
return value
}
|> take(1)
let combinedSignal: Signal<Bool, NoError>
@ -838,7 +840,9 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
}
self.isReady.set(combinedSignal
|> filter { $0 }
|> filter { value in
return value
}
|> take(1))
}

View File

@ -449,12 +449,12 @@ public final class WalletStrings: Equatable {
public var Wallet_SecureStorageReset_Title: String { return self._s[219]! }
public var Wallet_Receive_CommentHeader: String { return self._s[220]! }
public var Wallet_Info_ReceiveGrams: String { return self._s[221]! }
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, stringValue)
}
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue)

View File

@ -319,10 +319,11 @@ func makeBridgeMedia(message: Message, strings: PresentationStrings, chatPeer: P
}
func makeBridgeChat(_ entry: ChatListEntry, strings: PresentationStrings) -> (TGBridgeChat, [Int64 : TGBridgeUser])? {
if case let .MessageEntry(index, message, readState, _, _, renderedPeer, _, _, hasFailed, _) = entry {
if case let .MessageEntry(index, messages, readState, _, _, renderedPeer, _, _, hasFailed, _) = entry {
guard index.messageIndex.id.peerId.namespace != Namespaces.Peer.SecretChat else {
return nil
}
let message = messages.last
let (bridgeChat, participants) = makeBridgeChat(renderedPeer.peer)
bridgeChat.date = TimeInterval(index.messageIndex.timestamp)
if let message = message {