Display text selection tip in message context menu

This commit is contained in:
Ali 2019-12-13 14:27:49 +04:00
parent f1f4827f95
commit bc9b0785f1
31 changed files with 2464 additions and 2009 deletions

View File

@ -37,7 +37,7 @@ func unreadMessages(account: Account) -> Signal<[INMessage], NoError> {
|> mapToSignal { view -> Signal<[INMessage], NoError> in
var signals: [Signal<[INMessage], NoError>] = []
for entry in view.0.entries {
if case let .MessageEntry(index, _, readState, notificationSettings, _, _, _, _) = entry {
if case let .MessageEntry(index, _, readState, notificationSettings, _, _, _, _, _) = entry {
if index.messageIndex.id.peerId.namespace != Namespaces.Peer.CloudUser {
continue
}

View File

@ -5169,3 +5169,5 @@ Any member of this group will be able to see messages in the channel.";
"AuthSessions.OtherDevices" = "The official Telegram App is available for iPhone, iPad, Android, macOS, Windows and Linux. [Learn More]()";
"AuthSessions.AddDevice.UrlLoginHint" = "This code can be used to allow someone to log in to your Telegram account.\n\nTo confirm Telegram login, please go to Settings > Devices > Scan QR and scan the code.";
"ChatContextMenu.TextSelectionTip" = "Hold a word, then move cursor to select more| text to copy.";

View File

@ -1053,7 +1053,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
var sourceRect = selectedNode.view.superview!.convert(selectedNode.frame, to: sourceView)
sourceRect.size.height -= UIScreenPixel
switch item.content {
case let .peer(_, peer, _, _, _, _, _, _, _, _, _):
case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _):
if peer.peerId.namespace != Namespaces.Peer.SecretChat {
let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(peer.peerId), subject: nil, botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)

View File

@ -444,7 +444,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
}
})
case let .message(message, peer, readState, presentationData):
return ChatListItem(presentationData: presentationData, context: context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: message.index), content: .peer(message: message, peer: peer, combinedReadState: readState, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: true, displayAsMessage: 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, index: ChatListIndex(pinningIndex: nil, messageIndex: message.index), content: .peer(message: message, peer: peer, combinedReadState: readState, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, isAd: false, 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)
@ -1213,7 +1213,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
bounds = selectedItemNode.bounds
}
switch item.content {
case let .peer(message, peer, _, _, _, _, _, _, _, _, _):
case let .peer(message, peer, _, _, _, _, _, _, _, _, _, _):
return (selectedItemNode.view, bounds, message?.id ?? peer.peerId)
case let .groupReference(groupId, _, _, _, _):
return (selectedItemNode.view, bounds, groupId)

View File

@ -20,12 +20,12 @@ import ChatListSearchItemNode
import ContextUI
public enum ChatListItemContent {
case peer(message: Message?, peer: RenderedPeer, combinedReadState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, embeddedState: PeerChatListEmbeddedInterfaceState?, inputActivities: [(Peer, PeerInputActivity)]?, isAd: Bool, ignoreUnreadBadge: Bool, displayAsMessage: Bool)
case peer(message: Message?, peer: RenderedPeer, combinedReadState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, embeddedState: PeerChatListEmbeddedInterfaceState?, inputActivities: [(Peer, PeerInputActivity)]?, isAd: Bool, ignoreUnreadBadge: Bool, displayAsMessage: Bool, hasFailedMessages: Bool)
case groupReference(groupId: PeerGroupId, peers: [ChatListGroupReferencePeer], message: Message?, unreadState: PeerGroupUnreadCountersCombinedSummary, hiddenByDefault: Bool)
public var chatLocation: ChatLocation? {
switch self {
case let .peer(_, peer, _, _, _, _, _, _, _, _, _):
case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _):
return .peer(peer.peerId)
case .groupReference:
return nil
@ -38,7 +38,7 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour {
let context: AccountContext
let peerGroupId: PeerGroupId
let index: ChatListIndex
let content: ChatListItemContent
public let content: ChatListItemContent
let editing: Bool
let hasActiveRevealControls: Bool
let selected: Bool
@ -122,7 +122,7 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour {
public func selected(listView: ListView) {
switch self.content {
case let .peer(message, peer, _, _, _, _, _, _, isAd, _, _):
case let .peer(message, peer, _, _, _, _, _, _, isAd, _, _, _):
if let message = message, let peer = peer.peer {
self.interaction.messageSelected(peer, message, isAd)
} else if let peer = peer.peer {
@ -522,7 +522,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var displayAsMessage = false
var enablePreview = true
switch item.content {
case let .peer(message, peerValue, _, _, _, _, _, _, _, _, displayAsMessageValue):
case let .peer(message, peerValue, _, _, _, _, _, _, _, _, displayAsMessageValue, _):
displayAsMessage = displayAsMessageValue
if displayAsMessage, let author = message?.author as? TelegramUser {
peer = author
@ -673,11 +673,12 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let isPeerGroup: Bool
let isAd: Bool
let displayAsMessage: Bool
let hasFailedMessages: Bool
var groupHiddenByDefault = false
switch item.content {
case let .peer(messageValue, peerValue, combinedReadStateValue, notificationSettingsValue, peerPresenceValue, summaryInfoValue, embeddedStateValue, inputActivitiesValue, isAdValue, ignoreUnreadBadge, displayAsMessageValue):
case let .peer(messageValue, peerValue, combinedReadStateValue, notificationSettingsValue, peerPresenceValue, summaryInfoValue, embeddedStateValue, inputActivitiesValue, isAdValue, ignoreUnreadBadge, displayAsMessageValue, hasFailedMessagesValue):
message = messageValue
contentPeer = .chat(peerValue)
combinedReadState = combinedReadStateValue
@ -698,6 +699,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
isPeerGroup = false
isAd = isAdValue
displayAsMessage = displayAsMessageValue
hasFailedMessages = hasFailedMessagesValue
case let .groupReference(_, peers, messageValue, unreadState, hiddenByDefault):
if let _ = messageValue, !peers.isEmpty {
contentPeer = .chat(peers[0].peer)
@ -717,6 +719,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
peerPresence = nil
isAd = false
displayAsMessage = false
hasFailedMessages = false
}
if let messageValue = message {
@ -975,7 +978,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
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 {
if message.flags.contains(.Failed) {
if hasFailedMessages {
statusState = .failed(item.presentationData.theme.chatList.failedFillColor, item.presentationData.theme.chatList.failedForegroundColor)
} else {
if let combinedReadState = combinedReadState, combinedReadState.isOutgoingMessageIndexRead(message.index) {
@ -1061,7 +1064,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var credibilityIconOffset: CGFloat = 0.0
if displayAsMessage {
switch item.content {
case let .peer(message, _, _, _, _, _, _, _, _, _, _):
case let .peer(message, _, _, _, _, _, _, _, _, _, _, _):
if let peer = message?.author {
if peer.isScam {
currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(item.presentationData.theme, type: .regular)
@ -1153,7 +1156,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let peerRevealOptions: [ItemListRevealOption]
let peerLeftRevealOptions: [ItemListRevealOption]
switch item.content {
case let .peer(_, renderedPeer, _, _, presence, _ ,_ ,_, _, _, displayAsMessage):
case let .peer(_, renderedPeer, _, _, presence, _ ,_ ,_, _, _, displayAsMessage, _):
if !displayAsMessage, let peer = renderedPeer.peer as? TelegramUser, let presence = presence as? TelegramUserPresence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != item.context.account.peerId {
let relativeStatus = relativeUserPresenceStatus(presence, relativeTo: timestamp)
if case .online = relativeStatus {

View File

@ -141,10 +141,10 @@ public struct ChatListNodeState: Equatable {
private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatListNodeInteraction, peerGroupId: PeerGroupId, mode: ChatListNodeMode, entries: [ChatListNodeViewTransitionInsertEntry]) -> [ListViewInsertItem] {
return entries.map { entry -> ListViewInsertItem in
switch entry.entry {
case let .PeerEntry(index, presentationData, message, combinedReadState, notificationSettings, embeddedState, peer, presence, summaryInfo, editing, hasActiveRevealControls, selected, inputActivities, isAd):
case let .PeerEntry(index, presentationData, message, combinedReadState, notificationSettings, embeddedState, peer, presence, summaryInfo, editing, hasActiveRevealControls, selected, inputActivities, isAd, hasFailedMessages):
switch mode {
case .chatList:
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, context: context, peerGroupId: peerGroupId, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, notificationSettings: notificationSettings, presence: presence, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, isAd: isAd, ignoreUnreadBadge: false, displayAsMessage: false), 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, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, notificationSettings: notificationSettings, presence: presence, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, isAd: isAd, 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):
let itemPeer = peer.chatMainPeer
var chatPeer: Peer?
@ -222,10 +222,10 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatListNodeInteraction, peerGroupId: PeerGroupId, mode: ChatListNodeMode, entries: [ChatListNodeViewTransitionUpdateEntry]) -> [ListViewUpdateItem] {
return entries.map { entry -> ListViewUpdateItem in
switch entry.entry {
case let .PeerEntry(index, presentationData, message, combinedReadState, notificationSettings, embeddedState, peer, presence, summaryInfo, editing, hasActiveRevealControls, selected, inputActivities, isAd):
case let .PeerEntry(index, presentationData, message, combinedReadState, notificationSettings, embeddedState, peer, presence, summaryInfo, editing, hasActiveRevealControls, selected, inputActivities, isAd, hasFailedMessages):
switch mode {
case .chatList:
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, context: context, peerGroupId: peerGroupId, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, notificationSettings: notificationSettings, presence: presence, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, isAd: isAd, ignoreUnreadBadge: false, displayAsMessage: false), 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, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, notificationSettings: notificationSettings, presence: presence, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, isAd: isAd, 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):
let itemPeer = peer.chatMainPeer
var chatPeer: Peer?
@ -549,7 +549,7 @@ public final class ChatListNode: ListView {
let (rawEntries, isLoading) = chatListNodeEntriesForView(update.view, state: state, savedMessagesPeer: savedMessagesPeer, hideArchivedFolderByDefault: hideArchivedFolderByDefault, displayArchiveIntro: displayArchiveIntro, mode: mode)
let entries = rawEntries.filter { entry in
switch entry {
case let .PeerEntry(_, _, _, _, _, _, peer, _, _, _, _, _, _, _):
case let .PeerEntry(_, _, _, _, _, _, peer, _, _, _, _, _, _, _, _):
switch mode {
case .chatList:
return true
@ -654,7 +654,7 @@ public final class ChatListNode: ListView {
var didIncludeHiddenByDefaultArchive = false
if let previous = previousView {
for entry in previous.filteredEntries {
if case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _) = entry {
if case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = entry {
if index.pinningIndex != nil {
previousPinnedChats.append(index.messageIndex.id.peerId)
}
@ -670,11 +670,11 @@ public final class ChatListNode: ListView {
var doesIncludeArchive = false
var doesIncludeHiddenByDefaultArchive = false
for entry in processedView.filteredEntries {
if case let .PeerEntry(index, _, _, _, _, _, _, _, _ , _, _, _, _, _) = entry {
if index.pinningIndex != nil {
updatedPinnedChats.append(index.messageIndex.id.peerId)
if case let .PeerEntry(peerEntry) = entry {
if peerEntry.index.pinningIndex != nil {
updatedPinnedChats.append(peerEntry.index.messageIndex.id.peerId)
}
if index.messageIndex.id.peerId == removingPeerId {
if peerEntry.index.messageIndex.id.peerId == removingPeerId {
doesIncludeRemovingPeerId = true
}
} else if case let .GroupReferenceEntry(entry) = entry {
@ -749,7 +749,7 @@ public final class ChatListNode: ListView {
continue
}
switch chatListView.filteredEntries[entryCount - i - 1] {
case let .PeerEntry(_, _, _, readState, notificationSettings, _, _, _, _, _, _, _, _, _):
case let .PeerEntry(_, _, _, readState, notificationSettings, _, _, _, _, _, _, _, _, _, _):
if let readState = readState {
let count = readState.count
rawUnreadCount += count
@ -895,7 +895,7 @@ public final class ChatListNode: ListView {
var referenceId: PinnedItemId?
var beforeAll = false
switch toEntry {
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, isAd):
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, isAd, _):
if isAd {
beforeAll = true
} else {
@ -913,7 +913,7 @@ public final class ChatListNode: ListView {
var itemId: PinnedItemId?
switch fromEntry {
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _):
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
itemId = .peer(index.messageIndex.id.peerId)
/*case let .GroupReferenceEntry(_, _, groupId, _, _, _, _):
itemId = .group(groupId)*/
@ -1338,7 +1338,7 @@ public final class ChatListNode: ListView {
continue
}
switch chatListView.filteredEntries[entryCount - i - 1] {
case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _):
case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _):
if interaction.highlightedChatLocation?.location == ChatLocation.peer(peer.peerId) {
current = (index, peer.peerId, entryCount - i - 1)
break outer
@ -1371,10 +1371,10 @@ public final class ChatListNode: ListView {
case .previous(unread: false), .next(unread: false):
var target: (ChatListIndex, PeerId)? = nil
if let current = current, entryCount > 1 {
if current.2 > 0, case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _) = chatListView.filteredEntries[current.2 - 1] {
if current.2 > 0, case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _) = chatListView.filteredEntries[current.2 - 1] {
next = (index, peer.peerId)
}
if current.2 <= entryCount - 2, case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _) = chatListView.filteredEntries[current.2 + 1] {
if current.2 <= entryCount - 2, case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _) = chatListView.filteredEntries[current.2 + 1] {
previous = (index, peer.peerId)
}
if case .previous = option {
@ -1383,7 +1383,7 @@ public final class ChatListNode: ListView {
target = next
}
} else if entryCount > 0 {
if case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _) = chatListView.filteredEntries[entryCount - 1] {
if case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _) = chatListView.filteredEntries[entryCount - 1] {
target = (index, peer.peerId)
}
}
@ -1402,7 +1402,7 @@ public final class ChatListNode: ListView {
|> take(1)
|> deliverOnMainQueue).start(next: { update in
let entries = update.view.entries
if entries.count > index, case let .MessageEntry(index, _, _, _, _, renderedPeer, _, _) = entries[10 - index - 1] {
if entries.count > index, case let .MessageEntry(index, _, _, _, _, renderedPeer, _, _, _) = entries[10 - index - 1] {
let location: ChatListNodeLocation = .scroll(index: index, sourceIndex: .absoluteLowerBound, scrollPosition: .center(.top), animated: true)
self.setChatListLocation(location)
self.peerSelected?(renderedPeer.peerId, false, false)
@ -1445,7 +1445,7 @@ public final class ChatListNode: ListView {
continue
}
switch chatListView.filteredEntries[entryCount - i - 1] {
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _):
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
return index
default:
break

View File

@ -13,14 +13,14 @@ enum ChatListNodeEntryId: Hashable {
}
enum ChatListNodeEntry: Comparable, Identifiable {
case PeerEntry(index: ChatListIndex, presentationData: ChatListPresentationData, message: Message?, readState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, embeddedInterfaceState: PeerChatListEmbeddedInterfaceState?, peer: RenderedPeer, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, inputActivities: [(Peer, PeerInputActivity)]?, isAd: Bool)
case PeerEntry(index: ChatListIndex, presentationData: ChatListPresentationData, message: Message?, readState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, embeddedInterfaceState: PeerChatListEmbeddedInterfaceState?, peer: RenderedPeer, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, inputActivities: [(Peer, PeerInputActivity)]?, isAd: Bool, hasFailedMessages: 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)
var sortIndex: ChatListIndex {
switch self {
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _):
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
return index
case let .HoleEntry(hole, _):
return ChatListIndex(pinningIndex: nil, messageIndex: hole.index)
@ -33,7 +33,7 @@ enum ChatListNodeEntry: Comparable, Identifiable {
var stableId: ChatListNodeEntryId {
switch self {
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _):
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
return .PeerId(index.messageIndex.id.peerId.toInt64())
case let .HoleEntry(hole, _):
return .Hole(Int64(hole.index.id.id))
@ -50,9 +50,9 @@ enum ChatListNodeEntry: Comparable, Identifiable {
static func ==(lhs: ChatListNodeEntry, rhs: ChatListNodeEntry) -> Bool {
switch lhs {
case let .PeerEntry(lhsIndex, lhsPresentationData, lhsMessage, lhsUnreadCount, lhsNotificationSettings, lhsEmbeddedState, lhsPeer, lhsPresence, lhsSummaryInfo, lhsEditing, lhsHasRevealControls, lhsSelected, lhsInputActivities, lhsAd):
case let .PeerEntry(lhsIndex, lhsPresentationData, lhsMessage, lhsUnreadCount, lhsNotificationSettings, lhsEmbeddedState, lhsPeer, lhsPresence, lhsSummaryInfo, lhsEditing, lhsHasRevealControls, lhsSelected, lhsInputActivities, lhsAd, lhsHasFailedMessages):
switch rhs {
case let .PeerEntry(rhsIndex, rhsPresentationData, rhsMessage, rhsUnreadCount, rhsNotificationSettings, rhsEmbeddedState, rhsPeer, rhsPresence, rhsSummaryInfo, rhsEditing, rhsHasRevealControls, rhsSelected, rhsInputActivities, rhsAd):
case let .PeerEntry(rhsIndex, rhsPresentationData, rhsMessage, rhsUnreadCount, rhsNotificationSettings, rhsEmbeddedState, rhsPeer, rhsPresence, rhsSummaryInfo, rhsEditing, rhsHasRevealControls, rhsSelected, rhsInputActivities, rhsAd, rhsHasFailedMessages):
if lhsIndex != rhsIndex {
return false
}
@ -119,7 +119,9 @@ enum ChatListNodeEntry: Comparable, Identifiable {
if lhsAd != rhsAd {
return false
}
if lhsHasFailedMessages != rhsHasFailedMessages {
return false
}
return true
default:
return false
@ -203,7 +205,7 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState,
}
loop: for entry in view.entries {
switch entry {
case let .MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, peerPresence, summaryInfo):
case let .MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, peerPresence, summaryInfo, hasFailed):
if let savedMessagesPeer = savedMessagesPeer, savedMessagesPeer.id == index.messageIndex.id.peerId {
continue loop
}
@ -216,7 +218,7 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState,
updatedMessage = nil
updatedCombinedReadState = nil
}
result.append(.PeerEntry(index: offsetPinnedIndex(index, offset: pinnedIndexOffset), presentationData: state.presentationData, message: updatedMessage, readState: updatedCombinedReadState, notificationSettings: notificationSettings, 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], isAd: false))
result.append(.PeerEntry(index: offsetPinnedIndex(index, offset: pinnedIndexOffset), presentationData: state.presentationData, message: updatedMessage, readState: updatedCombinedReadState, notificationSettings: notificationSettings, 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], isAd: false, hasFailedMessages: hasFailed))
case let .HoleEntry(hole):
if hole.index.timestamp == Int32.max - 1 {
return ([], true)
@ -228,13 +230,13 @@ 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, notificationSettings: nil, 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, isAd: false))
result.append(.PeerEntry(index: ChatListIndex.absoluteUpperBound.predecessor, presentationData: state.presentationData, message: nil, readState: nil, notificationSettings: nil, 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, isAd: false, hasFailedMessages: false))
} else {
if !view.additionalItemEntries.isEmpty {
for entry in view.additionalItemEntries.reversed() {
switch entry {
case let .MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, peerPresence, summaryInfo):
result.append(.PeerEntry(index: ChatListIndex(pinningIndex: pinningIndex, messageIndex: index.messageIndex), presentationData: state.presentationData, message: message, readState: combinedReadState, notificationSettings: notificationSettings, 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], isAd: true))
case let .MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, peerPresence, summaryInfo, hasFailed):
result.append(.PeerEntry(index: ChatListIndex(pinningIndex: pinningIndex, messageIndex: index.messageIndex), presentationData: state.presentationData, message: message, readState: combinedReadState, notificationSettings: notificationSettings, 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], isAd: true, hasFailedMessages: hasFailed))
if pinningIndex != 0 {
pinningIndex -= 1
}

View File

@ -12,6 +12,7 @@ static_library(
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/TextSelectionNode:TextSelectionNode",
"//submodules/ReactionSelectionNode:ReactionSelectionNode",
"//submodules/AppBundle:AppBundle",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -2,6 +2,8 @@ import Foundation
import AsyncDisplayKit
import Display
import TelegramPresentationData
import TextSelectionNode
import AppBundle
private final class ContextActionsSelectionGestureRecognizer: UIPanGestureRecognizer {
var updateLocation: ((CGPoint, Bool) -> Void)?
@ -38,7 +40,7 @@ private enum ContextItemNode {
case separator(ASDisplayNode)
}
final class ContextActionsContainerNode: ASDisplayNode {
private final class InnerActionsContainerNode: ASDisplayNode {
private let presentationData: PresentationData
private var effectView: UIVisualEffectView?
private var itemNodes: [ContextItemNode]
@ -242,3 +244,179 @@ final class ContextActionsContainerNode: ASDisplayNode {
return nil
}
}
private final class InnerTextSelectionTipContainerNode: ASDisplayNode {
private let presentationData: PresentationData
private var effectView: UIVisualEffectView?
private let textNode: TextNode
private var textSelectionNode: TextSelectionNode?
private let iconNode: ASImageNode
private let text: String
private let targetSelectionIndex: Int
init(presentationData: PresentationData) {
self.presentationData = presentationData
self.textNode = TextNode()
var rawText = self.presentationData.strings.ChatContextMenu_TextSelectionTip
if let range = rawText.range(of: "|") {
rawText.removeSubrange(range)
self.text = rawText
self.targetSelectionIndex = NSRange(range, in: rawText).lowerBound
} else {
self.text = rawText
self.targetSelectionIndex = 1
}
self.iconNode = ASImageNode()
self.iconNode.displaysAsynchronously = false
self.iconNode.displayWithoutProcessing = true
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Tip"), color: presentationData.theme.contextMenu.primaryColor)
super.init()
self.clipsToBounds = true
self.cornerRadius = 14.0
self.backgroundColor = presentationData.theme.contextMenu.backgroundColor
let textSelectionNode = TextSelectionNode(theme: TextSelectionTheme(selection: presentationData.theme.contextMenu.primaryColor.withAlphaComponent(0.15), knob: presentationData.theme.contextMenu.primaryColor, knobDiameter: 8.0), strings: presentationData.strings, textNode: self.textNode, updateIsActive: { _ in
}, present: { _, _ in
}, rootNode: self, performAction: { _, _ in
})
self.textSelectionNode = textSelectionNode
self.addSubnode(self.textNode)
self.addSubnode(self.iconNode)
self.textSelectionNode.flatMap(self.addSubnode)
self.addSubnode(textSelectionNode.highlightAreaNode)
}
func updateLayout(widthClass: ContainerViewLayoutSizeClass, width: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
switch widthClass {
case .compact:
if let effectView = self.effectView {
self.effectView = nil
effectView.removeFromSuperview()
}
case .regular:
if self.effectView == nil {
let effectView: UIVisualEffectView
if #available(iOS 13.0, *) {
if self.presentationData.theme.overallDarkAppearance {
effectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterialDark))
} else {
effectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterialLight))
}
} else if #available(iOS 10.0, *) {
effectView = UIVisualEffectView(effect: UIBlurEffect(style: .regular))
} else {
effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
}
self.effectView = effectView
self.view.insertSubview(effectView, at: 0)
}
}
let verticalInset: CGFloat = 10.0
let horizontalInset: CGFloat = 16.0
let standardIconWidth: CGFloat = 32.0
let iconSideInset: CGFloat = 12.0
let textFont = Font.regular(floor(presentationData.fontSize.baseDisplaySize * 14.0 / 17.0))
let iconSize = self.iconNode.image?.size ?? CGSize(width: 16.0, height: 16.0)
let makeTextLayout = TextNode.asyncLayout(self.textNode)
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: self.text, font: textFont, textColor: self.presentationData.theme.contextMenu.primaryColor), backgroundColor: nil, minimumNumberOfLines: 0, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: width - horizontalInset * 2.0 - iconSize.width - 8.0, height: .greatestFiniteMagnitude), alignment: .left, lineSpacing: 0.0, cutout: nil, insets: UIEdgeInsets(), lineColor: nil, textShadowColor: nil, textStroke: nil))
let _ = textApply()
let textFrame = CGRect(origin: CGPoint(x: horizontalInset, y: verticalInset), size: textLayout.size)
transition.updateFrame(node: self.textNode, frame: textFrame)
let size = CGSize(width: width, height: textLayout.size.height + verticalInset * 2.0)
let iconFrame = CGRect(origin: CGPoint(x: size.width - standardIconWidth - iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)
transition.updateFrame(node: self.iconNode, frame: iconFrame)
if let textSelectionNode = self.textSelectionNode {
transition.updateFrame(node: textSelectionNode, frame: textFrame)
textSelectionNode.highlightAreaNode.frame = textFrame
}
if let effectView = self.effectView {
transition.updateFrame(view: effectView, frame: CGRect(origin: CGPoint(), size: size))
}
return size
}
func updateTheme(presentationData: PresentationData) {
self.backgroundColor = presentationData.theme.contextMenu.backgroundColor
}
func animateIn() {
if let textSelectionNode = self.textSelectionNode {
textSelectionNode.pretendInitiateSelection()
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.textSelectionNode?.pretendExtendSelection(to: strongSelf.targetSelectionIndex)
})
}
}
}
final class ContextActionsContainerNode: ASDisplayNode {
private let actionsNode: InnerActionsContainerNode
private let textSelectionTipNode: InnerTextSelectionTipContainerNode?
init(presentationData: PresentationData, items: [ContextMenuItem], getController: @escaping () -> ContextController?, actionSelected: @escaping (ContextMenuActionResult) -> Void, feedbackTap: @escaping () -> Void, displayTextSelectionTip: Bool) {
self.actionsNode = InnerActionsContainerNode(presentationData: presentationData, items: items, getController: getController, actionSelected: actionSelected, feedbackTap: feedbackTap)
if displayTextSelectionTip {
let textSelectionTipNode = InnerTextSelectionTipContainerNode(presentationData: presentationData)
textSelectionTipNode.isUserInteractionEnabled = false
self.textSelectionTipNode = textSelectionTipNode
} else {
self.textSelectionTipNode = nil
}
super.init()
self.addSubnode(self.actionsNode)
self.textSelectionTipNode.flatMap(self.addSubnode)
}
func updateLayout(widthClass: ContainerViewLayoutSizeClass, constrainedWidth: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
let actionsSize = self.actionsNode.updateLayout(widthClass: widthClass, constrainedWidth: constrainedWidth, transition: transition)
var contentSize = actionsSize
transition.updateFrame(node: self.actionsNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: actionsSize))
if let textSelectionTipNode = self.textSelectionTipNode {
contentSize.height += 8.0
let textSelectionTipSize = textSelectionTipNode.updateLayout(widthClass: widthClass, width: actionsSize.width, transition: transition)
transition.updateFrame(node: textSelectionTipNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentSize.height), size: textSelectionTipSize))
contentSize.height += textSelectionTipSize.height
}
return contentSize
}
func actionNode(at point: CGPoint) -> ContextActionNode? {
return self.actionsNode.actionNode(at: self.view.convert(point, to: self.actionsNode.view))
}
func updateTheme(presentationData: PresentationData) {
self.actionsNode.updateTheme(presentationData: presentationData)
self.textSelectionTipNode?.updateTheme(presentationData: presentationData)
}
func animateIn() {
self.textSelectionTipNode?.animateIn()
}
}

View File

@ -69,6 +69,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
private let attemptTransitionControllerIntoNavigation: () -> Void
private let getController: () -> ContextController?
private weak var gesture: ContextGesture?
private var displayTextSelectionTip: Bool
private var didSetItemsReady = false
let itemsReady = Promise<Bool>()
@ -107,7 +108,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
private let itemsDisposable = MetaDisposable()
init(account: Account, controller: ContextController, presentationData: PresentationData, source: ContextContentSource, items: Signal<[ContextMenuItem], NoError>, reactionItems: [ReactionContextItem], beginDismiss: @escaping (ContextMenuActionResult) -> Void, recognizer: TapLongTapOrDoubleTapGestureRecognizer?, gesture: ContextGesture?, reactionSelected: @escaping (String) -> Void, beganAnimatingOut: @escaping () -> Void, attemptTransitionControllerIntoNavigation: @escaping () -> Void) {
init(account: Account, controller: ContextController, presentationData: PresentationData, source: ContextContentSource, items: Signal<[ContextMenuItem], NoError>, reactionItems: [ReactionContextItem], beginDismiss: @escaping (ContextMenuActionResult) -> Void, recognizer: TapLongTapOrDoubleTapGestureRecognizer?, gesture: ContextGesture?, reactionSelected: @escaping (String) -> Void, beganAnimatingOut: @escaping () -> Void, attemptTransitionControllerIntoNavigation: @escaping () -> Void, displayTextSelectionTip: Bool) {
self.presentationData = presentationData
self.source = source
self.items = items
@ -116,6 +117,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
self.beganAnimatingOut = beganAnimatingOut
self.attemptTransitionControllerIntoNavigation = attemptTransitionControllerIntoNavigation
self.gesture = gesture
self.displayTextSelectionTip = displayTextSelectionTip
self.getController = { [weak controller] in
return controller
@ -163,7 +165,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
beginDismiss(result)
}, feedbackTap: {
feedbackTap?()
})
}, displayTextSelectionTip: self.displayTextSelectionTip)
if !reactionItems.isEmpty {
let reactionContextNode = ReactionContextNode(account: account, theme: presentationData.theme, items: reactionItems)
@ -528,6 +530,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}, completion: { [weak self] in
self?.didCompleteAnimationIn = true
self?.hapticFeedback.prepareTap()
self?.actionsContainerNode.animateIn()
})
}
} else {
@ -535,6 +538,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
self.effectView.effect = makeCustomZoomBlurEffect()
}, completion: { [weak self] _ in
self?.didCompleteAnimationIn = true
self?.actionsContainerNode.animateIn()
})
}
@ -970,7 +974,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
self?.beginDismiss(result)
}, feedbackTap: { [weak self] in
self?.hapticFeedback.tap()
})
}, displayTextSelectionTip: self.displayTextSelectionTip)
self.scrollNode.insertSubnode(self.actionsContainerNode, aboveSubnode: previousActionsContainerNode)
if let layout = self.validLayout {
@ -982,6 +986,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
if !self.didSetItemsReady {
self.didSetItemsReady = true
self.displayTextSelectionTip = false
self.itemsReady.set(.single(true))
}
}
@ -1374,6 +1379,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
private weak var recognizer: TapLongTapOrDoubleTapGestureRecognizer?
private weak var gesture: ContextGesture?
private let displayTextSelectionTip: Bool
private var animatedDidAppear = false
private var wasDismissed = false
@ -1384,7 +1390,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
public var reactionSelected: ((String) -> Void)?
public init(account: Account, presentationData: PresentationData, source: ContextContentSource, items: Signal<[ContextMenuItem], NoError>, reactionItems: [ReactionContextItem], recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil, gesture: ContextGesture? = nil) {
public init(account: Account, presentationData: PresentationData, source: ContextContentSource, items: Signal<[ContextMenuItem], NoError>, reactionItems: [ReactionContextItem], recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil, gesture: ContextGesture? = nil, displayTextSelectionTip: Bool = false) {
self.account = account
self.presentationData = presentationData
self.source = source
@ -1392,6 +1398,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
self.reactionItems = reactionItems
self.recognizer = recognizer
self.gesture = gesture
self.displayTextSelectionTip = displayTextSelectionTip
super.init(navigationBarPresentationData: nil)
@ -1425,7 +1432,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
default:
break
}
})
}, displayTextSelectionTip: self.displayTextSelectionTip)
self.displayNodeDidLoad()

View File

@ -88,12 +88,12 @@ public struct ChatListGroupReferenceEntry: Equatable {
}
public enum ChatListEntry: Comparable {
case MessageEntry(ChatListIndex, Message?, CombinedPeerReadState?, PeerNotificationSettings?, PeerChatListEmbeddedInterfaceState?, RenderedPeer, PeerPresence?, ChatListMessageTagSummaryInfo)
case MessageEntry(ChatListIndex, Message?, CombinedPeerReadState?, PeerNotificationSettings?, PeerChatListEmbeddedInterfaceState?, RenderedPeer, PeerPresence?, ChatListMessageTagSummaryInfo, Bool)
case HoleEntry(ChatListHole)
public var index: ChatListIndex {
switch self {
case let .MessageEntry(index, _, _, _, _, _, _, _):
case let .MessageEntry(index, _, _, _, _, _, _, _, _):
return index
case let .HoleEntry(hole):
return ChatListIndex(pinningIndex: nil, messageIndex: hole.index)
@ -102,9 +102,9 @@ public enum ChatListEntry: Comparable {
public static func ==(lhs: ChatListEntry, rhs: ChatListEntry) -> Bool {
switch lhs {
case let .MessageEntry(lhsIndex, lhsMessage, lhsReadState, lhsSettings, lhsEmbeddedState, lhsPeer, lhsPresence, lhsInfo):
case let .MessageEntry(lhsIndex, lhsMessage, lhsReadState, lhsSettings, lhsEmbeddedState, lhsPeer, lhsPresence, lhsInfo, lhsHasFailed):
switch rhs {
case let .MessageEntry(rhsIndex, rhsMessage, rhsReadState, rhsSettings, rhsEmbeddedState, rhsPeer, rhsPresence, rhsInfo):
case let .MessageEntry(rhsIndex, rhsMessage, rhsReadState, rhsSettings, rhsEmbeddedState, rhsPeer, rhsPresence, rhsInfo, rhsHasFailed):
if lhsIndex != rhsIndex {
return false
}
@ -141,6 +141,9 @@ public enum ChatListEntry: Comparable {
if lhsInfo != rhsInfo {
return false
}
if lhsHasFailed != rhsHasFailed {
return false
}
return true
default:
return false
@ -178,7 +181,7 @@ private func processedChatListEntry(_ entry: MutableChatListEntry, cachedDataTab
enum MutableChatListEntry: Equatable {
case IntermediateMessageEntry(ChatListIndex, IntermediateMessage?, CombinedPeerReadState?, PeerChatListEmbeddedInterfaceState?)
case MessageEntry(ChatListIndex, Message?, CombinedPeerReadState?, PeerNotificationSettings?, PeerChatListEmbeddedInterfaceState?, RenderedPeer, PeerPresence?, ChatListMessageTagSummaryInfo)
case MessageEntry(ChatListIndex, Message?, CombinedPeerReadState?, PeerNotificationSettings?, PeerChatListEmbeddedInterfaceState?, RenderedPeer, PeerPresence?, ChatListMessageTagSummaryInfo, Bool)
case HoleEntry(ChatListHole)
init(_ intermediateEntry: ChatListIntermediateEntry, cachedDataTable: CachedPeerDataTable, readStateTable: MessageHistoryReadStateTable, messageHistoryTable: MessageHistoryTable) {
@ -194,7 +197,7 @@ enum MutableChatListEntry: Equatable {
switch self {
case let .IntermediateMessageEntry(index, _, _, _):
return index
case let .MessageEntry(index, _, _, _, _, _, _, _):
case let .MessageEntry(index, _, _, _, _, _, _, _, _):
return index
case let .HoleEntry(hole):
return ChatListIndex(pinningIndex: nil, messageIndex: hole.index)
@ -488,13 +491,13 @@ final class MutableChatListView {
if !updatedPeerNotificationSettings.isEmpty {
for i in 0 ..< self.entries.count {
switch self.entries[i] {
case let .MessageEntry(index, message, readState, _, embeddedState, peer, peerPresence, summaryInfo):
case let .MessageEntry(index, message, readState, _, embeddedState, peer, peerPresence, summaryInfo, hasFailed):
var notificationSettingsPeerId = peer.peerId
if let peer = peer.peers[peer.peerId], let associatedPeerId = peer.associatedPeerId {
notificationSettingsPeerId = associatedPeerId
}
if let settings = updatedPeerNotificationSettings[notificationSettingsPeerId] {
self.entries[i] = .MessageEntry(index, message, readState, settings, embeddedState, peer, peerPresence, summaryInfo)
self.entries[i] = .MessageEntry(index, message, readState, settings, embeddedState, peer, peerPresence, summaryInfo, hasFailed)
hasChanges = true
}
default:
@ -502,17 +505,35 @@ final class MutableChatListView {
}
}
}
if !transaction.updatedFailedMessagePeerIds.isEmpty {
for i in 0 ..< self.entries.count {
switch self.entries[i] {
case let .MessageEntry(index, message, readState, settings, embeddedState, peer, peerPresence, summaryInfo, previousHasFailed):
if transaction.updatedFailedMessagePeerIds.contains(index.messageIndex.id.peerId) {
let hasFailed = postbox.messageHistoryFailedTable.contains(peerId: index.messageIndex.id.peerId)
if previousHasFailed != hasFailed {
self.entries[i] = .MessageEntry(index, message, readState, settings, embeddedState, peer, peerPresence, summaryInfo, hasFailed)
hasChanges = true
}
}
default:
continue
}
}
}
if !updatedPeers.isEmpty {
for i in 0 ..< self.entries.count {
switch self.entries[i] {
case let .MessageEntry(index, message, readState, settings, embeddedState, peer, peerPresence, summaryInfo):
case let .MessageEntry(index, message, readState, settings, embeddedState, peer, peerPresence, summaryInfo, hasFailed):
var updatedMessage: Message?
if let message = message {
updatedMessage = updateMessagePeers(message, updatedPeers: updatedPeers)
}
let updatedPeer = updatedRenderedPeer(peer, updatedPeers: updatedPeers)
if updatedMessage != nil || updatedPeer != nil {
self.entries[i] = .MessageEntry(index, updatedMessage ?? message, readState, settings, embeddedState, updatedPeer ?? peer, peerPresence, summaryInfo)
self.entries[i] = .MessageEntry(index, updatedMessage ?? message, readState, settings, embeddedState, updatedPeer ?? peer, peerPresence, summaryInfo, hasFailed)
hasChanges = true
}
default:
@ -523,13 +544,13 @@ final class MutableChatListView {
if !updatedPeerPresences.isEmpty {
for i in 0 ..< self.entries.count {
switch self.entries[i] {
case let .MessageEntry(index, message, readState, settings, embeddedState, peer, _, summaryInfo):
case let .MessageEntry(index, message, readState, settings, embeddedState, peer, _, summaryInfo, hasFailed):
var presencePeerId = peer.peerId
if let peer = peer.peers[peer.peerId], let associatedPeerId = peer.associatedPeerId {
presencePeerId = associatedPeerId
}
if let presence = updatedPeerPresences[presencePeerId] {
self.entries[i] = .MessageEntry(index, message, readState, settings, embeddedState, peer, presence, summaryInfo)
self.entries[i] = .MessageEntry(index, message, readState, settings, embeddedState, peer, presence, summaryInfo, hasFailed)
hasChanges = true
}
default:
@ -540,7 +561,7 @@ final class MutableChatListView {
if !transaction.currentUpdatedMessageTagSummaries.isEmpty || !transaction.currentUpdatedMessageActionsSummaries.isEmpty {
for i in 0 ..< self.entries.count {
switch self.entries[i] {
case let .MessageEntry(index, message, readState, settings, embeddedState, peer, peerPresence, currentSummary):
case let .MessageEntry(index, message, readState, settings, embeddedState, peer, peerPresence, currentSummary, hasFailed):
var updatedTagSummaryCount: Int32?
var updatedActionsSummaryCount: Int32?
@ -561,7 +582,7 @@ final class MutableChatListView {
if updatedTagSummaryCount != nil || updatedActionsSummaryCount != nil {
let summaryInfo = ChatListMessageTagSummaryInfo(tagSummaryCount: updatedTagSummaryCount ?? currentSummary.tagSummaryCount, actionsSummaryCount: updatedActionsSummaryCount ?? currentSummary.actionsSummaryCount)
self.entries[i] = .MessageEntry(index, message, readState, settings, embeddedState, peer, peerPresence, summaryInfo)
self.entries[i] = .MessageEntry(index, message, readState, settings, embeddedState, peer, peerPresence, summaryInfo, hasFailed)
hasChanges = true
}
default:
@ -816,51 +837,48 @@ 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, message, combinedReadState, embeddedState):
let renderedMessage: Message?
if let message = message {
renderedMessage = renderMessage(message)
case let .IntermediateMessageEntry(index, message, combinedReadState, embeddedState):
let renderedMessage: Message?
if let message = message {
renderedMessage = renderMessage(message)
} else {
renderedMessage = nil
}
var peers = SimpleDictionary<PeerId, Peer>()
var notificationSettings: PeerNotificationSettings?
var presence: PeerPresence?
if let peer = getPeer(index.messageIndex.id.peerId) {
peers[peer.id] = peer
if let associatedPeerId = peer.associatedPeerId {
if let associatedPeer = getPeer(associatedPeerId) {
peers[associatedPeer.id] = associatedPeer
}
notificationSettings = getPeerNotificationSettings(associatedPeerId)
presence = getPeerPresence(associatedPeerId)
} else {
renderedMessage = nil
notificationSettings = getPeerNotificationSettings(index.messageIndex.id.peerId)
presence = getPeerPresence(index.messageIndex.id.peerId)
}
var peers = SimpleDictionary<PeerId, Peer>()
var notificationSettings: PeerNotificationSettings?
var presence: PeerPresence?
if let peer = getPeer(index.messageIndex.id.peerId) {
peers[peer.id] = peer
if let associatedPeerId = peer.associatedPeerId {
if let associatedPeer = getPeer(associatedPeerId) {
peers[associatedPeer.id] = associatedPeer
}
notificationSettings = getPeerNotificationSettings(associatedPeerId)
presence = getPeerPresence(associatedPeerId)
} else {
notificationSettings = getPeerNotificationSettings(index.messageIndex.id.peerId)
presence = getPeerPresence(index.messageIndex.id.peerId)
}
}
var tagSummaryCount: Int32?
var actionsSummaryCount: Int32?
if let tagSummary = self.summaryComponents.tagSummary {
let key = MessageHistoryTagsSummaryKey(tag: tagSummary.tag, peerId: index.messageIndex.id.peerId, namespace: tagSummary.namespace)
if let summary = postbox.messageHistoryTagsSummaryTable.get(key) {
tagSummaryCount = summary.count
}
}
var tagSummaryCount: Int32?
var actionsSummaryCount: Int32?
if let actionsSummary = self.summaryComponents.actionsSummary {
let key = PendingMessageActionsSummaryKey(type: actionsSummary.type, peerId: index.messageIndex.id.peerId, namespace: actionsSummary.namespace)
actionsSummaryCount = postbox.pendingMessageActionsMetadataTable.getCount(.peerNamespaceAction(key.peerId, key.namespace, key.type))
}
if let tagSummary = self.summaryComponents.tagSummary {
let key = MessageHistoryTagsSummaryKey(tag: tagSummary.tag, peerId: index.messageIndex.id.peerId, namespace: tagSummary.namespace)
if let summary = postbox.messageHistoryTagsSummaryTable.get(key) {
tagSummaryCount = summary.count
}
}
if let actionsSummary = self.summaryComponents.actionsSummary {
let key = PendingMessageActionsSummaryKey(type: actionsSummary.type, peerId: index.messageIndex.id.peerId, namespace: actionsSummary.namespace)
actionsSummaryCount = postbox.pendingMessageActionsMetadataTable.getCount(.peerNamespaceAction(key.peerId, key.namespace, key.type))
}
return .MessageEntry(index, renderedMessage, combinedReadState, notificationSettings, embeddedState, RenderedPeer(peerId: index.messageIndex.id.peerId, peers: peers), presence, ChatListMessageTagSummaryInfo(tagSummaryCount: tagSummaryCount, actionsSummaryCount: actionsSummaryCount))
/*case let .IntermediateGroupReferenceEntry(groupId, index, counters):
let message = postbox.messageHistoryTable.getMessage(index.messageIndex).flatMap(postbox.renderIntermediateMessage)
return .GroupReferenceEntry(groupId, index, message, ChatListGroupReferenceTopPeers(postbox: postbox, groupId: groupId), counters ?? ChatListGroupReferenceUnreadCounters(postbox: postbox, groupId: groupId))*/
default:
return nil
return .MessageEntry(index, renderedMessage, combinedReadState, notificationSettings, embeddedState, RenderedPeer(peerId: index.messageIndex.id.peerId, peers: peers), presence, ChatListMessageTagSummaryInfo(tagSummaryCount: tagSummaryCount, actionsSummaryCount: actionsSummaryCount), postbox.messageHistoryFailedTable.contains(peerId: index.messageIndex.id.peerId))
default:
return nil
}
}
@ -892,8 +910,8 @@ public final class ChatListView {
var entries: [ChatListEntry] = []
for entry in mutableView.entries {
switch entry {
case let .MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, peerPresence, summaryInfo):
entries.append(.MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, peerPresence, summaryInfo))
case let .MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, peerPresence, summaryInfo, hasFailed):
entries.append(.MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, peerPresence, summaryInfo, hasFailed))
case let .HoleEntry(hole):
entries.append(.HoleEntry(hole))
/*case let .GroupReferenceEntry(groupId, index, message, topPeers, counters):
@ -912,8 +930,8 @@ public final class ChatListView {
var additionalItemEntries: [ChatListEntry] = []
for entry in mutableView.additionalItemEntries {
switch entry {
case let .MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, peerPresence, summaryInfo):
additionalItemEntries.append(.MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, peerPresence, summaryInfo))
case let .MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, peerPresence, summaryInfo, hasFailed):
additionalItemEntries.append(.MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, peerPresence, summaryInfo, hasFailed))
case .HoleEntry:
assertionFailure()
/*case .GroupReferenceEntry:

View File

@ -0,0 +1,55 @@
import Foundation
final class MessageHistoryFailedTable: Table {
static func tableSpec(_ id: Int32) -> ValueBoxTable {
return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: true)
}
private let sharedKey = ValueBoxKey(length: 8 + 4 + 4)
private(set) var updatedPeerIds = Set<PeerId>()
private func key(_ id: MessageId) -> ValueBoxKey {
self.sharedKey.setInt64(0, value: id.peerId.toInt64())
self.sharedKey.setInt32(8, value: id.namespace)
self.sharedKey.setInt32(8 + 4, value: id.id)
return self.sharedKey
}
private func lowerBound(peerId: PeerId) -> ValueBoxKey {
let key = ValueBoxKey(length: 8)
key.setInt64(0, value: peerId.toInt64())
return key
}
private func upperBound(peerId: PeerId) -> ValueBoxKey {
let key = ValueBoxKey(length: 8)
key.setInt64(0, value: peerId.toInt64())
return key.successor
}
func add(_ id: MessageId) {
self.valueBox.set(self.table, key: self.key(id), value: MemoryBuffer())
self.updatedPeerIds.insert(id.peerId)
}
func remove(_ id: MessageId) {
self.valueBox.remove(self.table, key: self.key(id), secure: false)
self.updatedPeerIds.insert(id.peerId)
}
func contains(peerId: PeerId) -> Bool {
var result = false
self.valueBox.range(self.table, start: self.lowerBound(peerId: peerId), end: self.upperBound(peerId: peerId), keys: { _ in
result = true
return false
}, limit: 1)
return result
}
override func beforeCommit() {
self.updatedPeerIds.removeAll()
}
}

View File

@ -69,6 +69,7 @@ final class MessageHistoryTable: Table {
let historyMetadataTable: MessageHistoryMetadataTable
let globallyUniqueMessageIdsTable: MessageGloballyUniqueIdTable
let unsentTable: MessageHistoryUnsentTable
let failedTable: MessageHistoryFailedTable
let tagsTable: MessageHistoryTagsTable
let globalTagsTable: GlobalMessageHistoryTagsTable
let localTagsTable: LocalMessageHistoryTagsTable
@ -78,7 +79,7 @@ final class MessageHistoryTable: Table {
let summaryTable: MessageHistoryTagsSummaryTable
let pendingActionsTable: PendingMessageActionsTable
init(valueBox: ValueBox, table: ValueBoxTable, seedConfiguration: SeedConfiguration, messageHistoryIndexTable: MessageHistoryIndexTable, messageHistoryHoleIndexTable: MessageHistoryHoleIndexTable, messageMediaTable: MessageMediaTable, historyMetadataTable: MessageHistoryMetadataTable, globallyUniqueMessageIdsTable: MessageGloballyUniqueIdTable, unsentTable: MessageHistoryUnsentTable, tagsTable: MessageHistoryTagsTable, globalTagsTable: GlobalMessageHistoryTagsTable, localTagsTable: LocalMessageHistoryTagsTable, readStateTable: MessageHistoryReadStateTable, synchronizeReadStateTable: MessageHistorySynchronizeReadStateTable, textIndexTable: MessageHistoryTextIndexTable, summaryTable: MessageHistoryTagsSummaryTable, pendingActionsTable: PendingMessageActionsTable) {
init(valueBox: ValueBox, table: ValueBoxTable, seedConfiguration: SeedConfiguration, messageHistoryIndexTable: MessageHistoryIndexTable, messageHistoryHoleIndexTable: MessageHistoryHoleIndexTable, messageMediaTable: MessageMediaTable, historyMetadataTable: MessageHistoryMetadataTable, globallyUniqueMessageIdsTable: MessageGloballyUniqueIdTable, unsentTable: MessageHistoryUnsentTable, failedTable: MessageHistoryFailedTable, tagsTable: MessageHistoryTagsTable, globalTagsTable: GlobalMessageHistoryTagsTable, localTagsTable: LocalMessageHistoryTagsTable, readStateTable: MessageHistoryReadStateTable, synchronizeReadStateTable: MessageHistorySynchronizeReadStateTable, textIndexTable: MessageHistoryTextIndexTable, summaryTable: MessageHistoryTagsSummaryTable, pendingActionsTable: PendingMessageActionsTable) {
self.seedConfiguration = seedConfiguration
self.messageHistoryIndexTable = messageHistoryIndexTable
self.messageHistoryHoleIndexTable = messageHistoryHoleIndexTable
@ -86,6 +87,7 @@ final class MessageHistoryTable: Table {
self.historyMetadataTable = historyMetadataTable
self.globallyUniqueMessageIdsTable = globallyUniqueMessageIdsTable
self.unsentTable = unsentTable
self.failedTable = failedTable
self.tagsTable = tagsTable
self.globalTagsTable = globalTagsTable
self.localTagsTable = localTagsTable
@ -249,6 +251,9 @@ final class MessageHistoryTable: Table {
if message.flags.contains(.Unsent) && !message.flags.contains(.Failed) {
self.unsentTable.add(message.id, operations: &unsentMessageOperations)
}
if message.flags.contains(.Failed) {
self.failedTable.add(message.id)
}
let tags = message.tags.rawValue
if tags != 0 {
for i in 0 ..< 32 {
@ -1194,6 +1199,9 @@ final class MessageHistoryTable: Table {
if message.flags.contains(.Unsent) && !message.flags.contains(.Failed) {
self.unsentTable.remove(index.id, operations: &unsentMessageOperations)
}
if message.flags.contains(.Failed) {
self.failedTable.remove(message.id)
}
if let globallyUniqueId = message.globallyUniqueId {
self.globallyUniqueMessageIdsTable.remove(peerId: message.id.peerId, globallyUniqueId: globallyUniqueId)
@ -1447,6 +1455,23 @@ final class MessageHistoryTable: Table {
break
}
if previousMessage.id != message.id {
if previousMessage.flags.contains(.Failed) {
self.failedTable.remove(previousMessage.id)
}
if message.flags.contains(.Failed) {
self.failedTable.add(message.id)
}
} else {
if previousMessage.flags.contains(.Failed) != message.flags.contains(.Failed) {
if previousMessage.flags.contains(.Failed) {
self.failedTable.remove(previousMessage.id)
} else {
self.failedTable.add(message.id)
}
}
}
if self.seedConfiguration.peerNamespacesRequiringMessageTextIndex.contains(message.id.peerId.namespace) {
if previousMessage.id != message.id || previousMessage.text != message.text || previousMessage.tags != message.tags {
self.textIndexTable.remove(messageId: previousMessage.id)

View File

@ -1134,6 +1134,7 @@ public final class Postbox {
let additionalChatListItemsTable: AdditionalChatListItemsTable
let messageHistoryMetadataTable: MessageHistoryMetadataTable
let messageHistoryUnsentTable: MessageHistoryUnsentTable
let messageHistoryFailedTable: MessageHistoryFailedTable
let messageHistoryTagsTable: MessageHistoryTagsTable
let globalMessageHistoryTagsTable: GlobalMessageHistoryTagsTable
let localMessageHistoryTagsTable: LocalMessageHistoryTagsTable
@ -1211,6 +1212,7 @@ public final class Postbox {
self.messageHistoryMetadataTable = MessageHistoryMetadataTable(valueBox: self.valueBox, table: MessageHistoryMetadataTable.tableSpec(10))
self.messageHistoryHoleIndexTable = MessageHistoryHoleIndexTable(valueBox: self.valueBox, table: MessageHistoryHoleIndexTable.tableSpec(56), metadataTable: self.messageHistoryMetadataTable, seedConfiguration: self.seedConfiguration)
self.messageHistoryUnsentTable = MessageHistoryUnsentTable(valueBox: self.valueBox, table: MessageHistoryUnsentTable.tableSpec(11))
self.messageHistoryFailedTable = MessageHistoryFailedTable(valueBox: self.valueBox, table: MessageHistoryFailedTable.tableSpec(49))
self.invalidatedMessageHistoryTagsSummaryTable = InvalidatedMessageHistoryTagsSummaryTable(valueBox: self.valueBox, table: InvalidatedMessageHistoryTagsSummaryTable.tableSpec(47))
self.messageHistoryTagsSummaryTable = MessageHistoryTagsSummaryTable(valueBox: self.valueBox, table: MessageHistoryTagsSummaryTable.tableSpec(44), invalidateTable: self.invalidatedMessageHistoryTagsSummaryTable)
self.pendingMessageActionsMetadataTable = PendingMessageActionsMetadataTable(valueBox: self.valueBox, table: PendingMessageActionsMetadataTable.tableSpec(45))
@ -1227,7 +1229,7 @@ public final class Postbox {
self.timestampBasedMessageAttributesTable = TimestampBasedMessageAttributesTable(valueBox: self.valueBox, table: TimestampBasedMessageAttributesTable.tableSpec(34), indexTable: self.timestampBasedMessageAttributesIndexTable)
self.textIndexTable = MessageHistoryTextIndexTable(valueBox: self.valueBox, table: MessageHistoryTextIndexTable.tableSpec(41))
self.additionalChatListItemsTable = AdditionalChatListItemsTable(valueBox: self.valueBox, table: AdditionalChatListItemsTable.tableSpec(55))
self.messageHistoryTable = MessageHistoryTable(valueBox: self.valueBox, table: MessageHistoryTable.tableSpec(7), seedConfiguration: seedConfiguration, messageHistoryIndexTable: self.messageHistoryIndexTable, messageHistoryHoleIndexTable: self.messageHistoryHoleIndexTable, messageMediaTable: self.mediaTable, historyMetadataTable: self.messageHistoryMetadataTable, globallyUniqueMessageIdsTable: self.globallyUniqueMessageIdsTable, unsentTable: self.messageHistoryUnsentTable, tagsTable: self.messageHistoryTagsTable, globalTagsTable: self.globalMessageHistoryTagsTable, localTagsTable: self.localMessageHistoryTagsTable, readStateTable: self.readStateTable, synchronizeReadStateTable: self.synchronizeReadStateTable, textIndexTable: self.textIndexTable, summaryTable: self.messageHistoryTagsSummaryTable, pendingActionsTable: self.pendingMessageActionsTable)
self.messageHistoryTable = MessageHistoryTable(valueBox: self.valueBox, table: MessageHistoryTable.tableSpec(7), seedConfiguration: seedConfiguration, messageHistoryIndexTable: self.messageHistoryIndexTable, messageHistoryHoleIndexTable: self.messageHistoryHoleIndexTable, messageMediaTable: self.mediaTable, historyMetadataTable: self.messageHistoryMetadataTable, globallyUniqueMessageIdsTable: self.globallyUniqueMessageIdsTable, unsentTable: self.messageHistoryUnsentTable, failedTable: self.messageHistoryFailedTable, tagsTable: self.messageHistoryTagsTable, globalTagsTable: self.globalMessageHistoryTagsTable, localTagsTable: self.localMessageHistoryTagsTable, readStateTable: self.readStateTable, synchronizeReadStateTable: self.synchronizeReadStateTable, textIndexTable: self.textIndexTable, summaryTable: self.messageHistoryTagsSummaryTable, pendingActionsTable: self.pendingMessageActionsTable)
self.peerChatStateTable = PeerChatStateTable(valueBox: self.valueBox, table: PeerChatStateTable.tableSpec(13))
self.peerNameTokenIndexTable = ReverseIndexReferenceTable<PeerIdReverseIndexReference>(valueBox: self.valueBox, table: ReverseIndexReferenceTable<PeerIdReverseIndexReference>.tableSpec(26))
self.peerNameIndexTable = PeerNameIndexTable(valueBox: self.valueBox, table: PeerNameIndexTable.tableSpec(27), peerTable: self.peerTable, peerNameTokenIndexTable: self.peerNameTokenIndexTable)
@ -1267,6 +1269,7 @@ public final class Postbox {
tables.append(self.globallyUniqueMessageIdsTable)
tables.append(self.messageHistoryMetadataTable)
tables.append(self.messageHistoryUnsentTable)
tables.append(self.messageHistoryFailedTable)
tables.append(self.messageHistoryTagsTable)
tables.append(self.globalMessageHistoryTagsTable)
tables.append(self.localMessageHistoryTagsTable)
@ -1700,7 +1703,7 @@ public final class Postbox {
let transactionParticipationInTotalUnreadCountUpdates = self.peerNotificationSettingsTable.transactionParticipationInTotalUnreadCountUpdates(postbox: self)
self.chatListIndexTable.commitWithTransaction(postbox: self, alteredInitialPeerCombinedReadStates: alteredInitialPeerCombinedReadStates, updatedPeers: updatedPeers, transactionParticipationInTotalUnreadCountUpdates: transactionParticipationInTotalUnreadCountUpdates, updatedRootUnreadState: &self.currentUpdatedTotalUnreadState, updatedGroupTotalUnreadSummaries: &self.currentUpdatedGroupTotalUnreadSummaries, currentUpdatedGroupSummarySynchronizeOperations: &self.currentUpdatedGroupSummarySynchronizeOperations)
let transaction = PostboxTransaction(currentUpdatedState: self.currentUpdatedState, currentPeerHoleOperations: self.currentPeerHoleOperations, currentOperationsByPeerId: self.currentOperationsByPeerId, chatListOperations: self.currentChatListOperations, currentUpdatedChatListInclusions: self.currentUpdatedChatListInclusions, currentUpdatedPeers: self.currentUpdatedPeers, currentUpdatedPeerNotificationSettings: self.currentUpdatedPeerNotificationSettings, currentUpdatedPeerNotificationBehaviorTimestamps: self.currentUpdatedPeerNotificationBehaviorTimestamps, currentUpdatedCachedPeerData: self.currentUpdatedCachedPeerData, currentUpdatedPeerPresences: currentUpdatedPeerPresences, currentUpdatedPeerChatListEmbeddedStates: self.currentUpdatedPeerChatListEmbeddedStates, currentUpdatedTotalUnreadState: self.currentUpdatedTotalUnreadState, currentUpdatedTotalUnreadSummaries: self.currentUpdatedGroupTotalUnreadSummaries, alteredInitialPeerCombinedReadStates: alteredInitialPeerCombinedReadStates, currentPeerMergedOperationLogOperations: self.currentPeerMergedOperationLogOperations, currentTimestampBasedMessageAttributesOperations: self.currentTimestampBasedMessageAttributesOperations, unsentMessageOperations: self.currentUnsentOperations, updatedSynchronizePeerReadStateOperations: self.currentUpdatedSynchronizeReadStateOperations, currentUpdatedGroupSummarySynchronizeOperations: self.currentUpdatedGroupSummarySynchronizeOperations, currentPreferencesOperations: self.currentPreferencesOperations, currentOrderedItemListOperations: self.currentOrderedItemListOperations, currentItemCollectionItemsOperations: self.currentItemCollectionItemsOperations, currentItemCollectionInfosOperations: self.currentItemCollectionInfosOperations, currentUpdatedPeerChatStates: self.currentUpdatedPeerChatStates, currentGlobalTagsOperations: self.currentGlobalTagsOperations, currentLocalTagsOperations: self.currentLocalTagsOperations, updatedMedia: self.currentUpdatedMedia, replaceRemoteContactCount: self.currentReplaceRemoteContactCount, replaceContactPeerIds: self.currentReplacedContactPeerIds, currentPendingMessageActionsOperations: self.currentPendingMessageActionsOperations, currentUpdatedMessageActionsSummaries: self.currentUpdatedMessageActionsSummaries, currentUpdatedMessageTagSummaries: self.currentUpdatedMessageTagSummaries, currentInvalidateMessageTagSummaries: self.currentInvalidateMessageTagSummaries, currentUpdatedPendingPeerNotificationSettings: self.currentUpdatedPendingPeerNotificationSettings, replacedAdditionalChatListItems: self.currentReplacedAdditionalChatListItems, updatedNoticeEntryKeys: self.currentUpdatedNoticeEntryKeys, updatedCacheEntryKeys: self.currentUpdatedCacheEntryKeys, currentUpdatedMasterClientId: currentUpdatedMasterClientId)
let transaction = PostboxTransaction(currentUpdatedState: self.currentUpdatedState, currentPeerHoleOperations: self.currentPeerHoleOperations, currentOperationsByPeerId: self.currentOperationsByPeerId, chatListOperations: self.currentChatListOperations, currentUpdatedChatListInclusions: self.currentUpdatedChatListInclusions, currentUpdatedPeers: self.currentUpdatedPeers, currentUpdatedPeerNotificationSettings: self.currentUpdatedPeerNotificationSettings, currentUpdatedPeerNotificationBehaviorTimestamps: self.currentUpdatedPeerNotificationBehaviorTimestamps, currentUpdatedCachedPeerData: self.currentUpdatedCachedPeerData, currentUpdatedPeerPresences: currentUpdatedPeerPresences, currentUpdatedPeerChatListEmbeddedStates: self.currentUpdatedPeerChatListEmbeddedStates, currentUpdatedTotalUnreadState: self.currentUpdatedTotalUnreadState, currentUpdatedTotalUnreadSummaries: self.currentUpdatedGroupTotalUnreadSummaries, alteredInitialPeerCombinedReadStates: alteredInitialPeerCombinedReadStates, currentPeerMergedOperationLogOperations: self.currentPeerMergedOperationLogOperations, currentTimestampBasedMessageAttributesOperations: self.currentTimestampBasedMessageAttributesOperations, unsentMessageOperations: self.currentUnsentOperations, updatedSynchronizePeerReadStateOperations: self.currentUpdatedSynchronizeReadStateOperations, currentUpdatedGroupSummarySynchronizeOperations: self.currentUpdatedGroupSummarySynchronizeOperations, currentPreferencesOperations: self.currentPreferencesOperations, currentOrderedItemListOperations: self.currentOrderedItemListOperations, currentItemCollectionItemsOperations: self.currentItemCollectionItemsOperations, currentItemCollectionInfosOperations: self.currentItemCollectionInfosOperations, currentUpdatedPeerChatStates: self.currentUpdatedPeerChatStates, currentGlobalTagsOperations: self.currentGlobalTagsOperations, currentLocalTagsOperations: self.currentLocalTagsOperations, updatedMedia: self.currentUpdatedMedia, replaceRemoteContactCount: self.currentReplaceRemoteContactCount, replaceContactPeerIds: self.currentReplacedContactPeerIds, currentPendingMessageActionsOperations: self.currentPendingMessageActionsOperations, currentUpdatedMessageActionsSummaries: self.currentUpdatedMessageActionsSummaries, currentUpdatedMessageTagSummaries: self.currentUpdatedMessageTagSummaries, currentInvalidateMessageTagSummaries: self.currentInvalidateMessageTagSummaries, currentUpdatedPendingPeerNotificationSettings: self.currentUpdatedPendingPeerNotificationSettings, replacedAdditionalChatListItems: self.currentReplacedAdditionalChatListItems, updatedNoticeEntryKeys: self.currentUpdatedNoticeEntryKeys, updatedCacheEntryKeys: self.currentUpdatedCacheEntryKeys, currentUpdatedMasterClientId: currentUpdatedMasterClientId, updatedFailedMessagePeerIds: self.messageHistoryFailedTable.updatedPeerIds)
var updatedTransactionState: Int64?
var updatedMasterClientId: Int64?
if !transaction.isEmpty {

View File

@ -40,6 +40,7 @@ final class PostboxTransaction {
let replacedAdditionalChatListItems: [PeerId]?
let updatedNoticeEntryKeys: Set<NoticeEntryKey>
let updatedCacheEntryKeys: Set<ItemCacheEntryId>
let updatedFailedMessagePeerIds: Set<PeerId>
var isEmpty: Bool {
if currentUpdatedState != nil {
@ -159,10 +160,13 @@ final class PostboxTransaction {
if !updatedCacheEntryKeys.isEmpty {
return false
}
if !updatedFailedMessagePeerIds.isEmpty {
return false
}
return true
}
init(currentUpdatedState: PostboxCoding?, currentPeerHoleOperations: [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]] = [:], currentOperationsByPeerId: [PeerId: [MessageHistoryOperation]], chatListOperations: [PeerGroupId: [ChatListOperation]], currentUpdatedChatListInclusions: [PeerId: PeerChatListInclusion], currentUpdatedPeers: [PeerId: Peer], currentUpdatedPeerNotificationSettings: [PeerId: PeerNotificationSettings], currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp], currentUpdatedCachedPeerData: [PeerId: CachedPeerData], currentUpdatedPeerPresences: [PeerId: PeerPresence], currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?], currentUpdatedTotalUnreadState: ChatListTotalUnreadState?, currentUpdatedTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary], alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState], currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation], currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation], unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], currentUpdatedGroupSummarySynchronizeOperations: [PeerGroupAndNamespace: Bool], currentPreferencesOperations: [PreferencesOperation], currentOrderedItemListOperations: [Int32: [OrderedItemListOperation]], currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]], currentItemCollectionInfosOperations: [ItemCollectionInfosOperation], currentUpdatedPeerChatStates: Set<PeerId>, currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation], currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: [MediaId: Media?], replaceRemoteContactCount: Int32?, replaceContactPeerIds: Set<PeerId>?, currentPendingMessageActionsOperations: [PendingMessageActionsOperation], currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32], currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], currentInvalidateMessageTagSummaries: [InvalidatedMessageHistoryTagsSummaryEntryOperation], currentUpdatedPendingPeerNotificationSettings: Set<PeerId>, replacedAdditionalChatListItems: [PeerId]?, updatedNoticeEntryKeys: Set<NoticeEntryKey>, updatedCacheEntryKeys: Set<ItemCacheEntryId>, currentUpdatedMasterClientId: Int64?) {
init(currentUpdatedState: PostboxCoding?, currentPeerHoleOperations: [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]] = [:], currentOperationsByPeerId: [PeerId: [MessageHistoryOperation]], chatListOperations: [PeerGroupId: [ChatListOperation]], currentUpdatedChatListInclusions: [PeerId: PeerChatListInclusion], currentUpdatedPeers: [PeerId: Peer], currentUpdatedPeerNotificationSettings: [PeerId: PeerNotificationSettings], currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp], currentUpdatedCachedPeerData: [PeerId: CachedPeerData], currentUpdatedPeerPresences: [PeerId: PeerPresence], currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?], currentUpdatedTotalUnreadState: ChatListTotalUnreadState?, currentUpdatedTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary], alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState], currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation], currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation], unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], currentUpdatedGroupSummarySynchronizeOperations: [PeerGroupAndNamespace: Bool], currentPreferencesOperations: [PreferencesOperation], currentOrderedItemListOperations: [Int32: [OrderedItemListOperation]], currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]], currentItemCollectionInfosOperations: [ItemCollectionInfosOperation], currentUpdatedPeerChatStates: Set<PeerId>, currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation], currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: [MediaId: Media?], replaceRemoteContactCount: Int32?, replaceContactPeerIds: Set<PeerId>?, currentPendingMessageActionsOperations: [PendingMessageActionsOperation], currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32], currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], currentInvalidateMessageTagSummaries: [InvalidatedMessageHistoryTagsSummaryEntryOperation], currentUpdatedPendingPeerNotificationSettings: Set<PeerId>, replacedAdditionalChatListItems: [PeerId]?, updatedNoticeEntryKeys: Set<NoticeEntryKey>, updatedCacheEntryKeys: Set<ItemCacheEntryId>, currentUpdatedMasterClientId: Int64?, updatedFailedMessagePeerIds: Set<PeerId>) {
self.currentUpdatedState = currentUpdatedState
self.currentPeerHoleOperations = currentPeerHoleOperations
self.currentOperationsByPeerId = currentOperationsByPeerId
@ -201,5 +205,6 @@ final class PostboxTransaction {
self.replacedAdditionalChatListItems = replacedAdditionalChatListItems
self.updatedNoticeEntryKeys = updatedNoticeEntryKeys
self.updatedCacheEntryKeys = updatedCacheEntryKeys
self.updatedFailedMessagePeerIds = updatedFailedMessagePeerIds
}
}

View File

@ -349,7 +349,8 @@ final class ViewTracker {
}
}
if !transaction.chatListOperations.isEmpty || !transaction.currentUpdatedPeerNotificationSettings.isEmpty || !transaction.currentUpdatedPeers.isEmpty || !transaction.currentInvalidateMessageTagSummaries.isEmpty || !transaction.currentUpdatedMessageTagSummaries.isEmpty || !transaction.currentOperationsByPeerId.isEmpty || transaction.replacedAdditionalChatListItems != nil || !transaction.currentUpdatedPeerPresences.isEmpty {
if !transaction.chatListOperations.isEmpty || !transaction.currentUpdatedPeerNotificationSettings.isEmpty || !transaction.currentUpdatedPeers.isEmpty || !transaction.currentInvalidateMessageTagSummaries.isEmpty || !transaction.currentUpdatedMessageTagSummaries.isEmpty || !transaction.currentOperationsByPeerId.isEmpty || transaction.replacedAdditionalChatListItems != nil || !transaction.currentUpdatedPeerPresences.isEmpty ||
!transaction.updatedFailedMessagePeerIds.isEmpty {
for (mutableView, pipe) in self.chatListViews.copyItems() {
let context = MutableChatListViewReplayContext()
if mutableView.replay(postbox: postbox, operations: transaction.chatListOperations, updatedPeerNotificationSettings: transaction.currentUpdatedPeerNotificationSettings, updatedPeers: transaction.currentUpdatedPeers, updatedPeerPresences: transaction.currentUpdatedPeerPresences, transaction: transaction, context: context) {

View File

@ -215,22 +215,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, 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))]), notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: 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, 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))]), notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, 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, 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, notificationSettings: nil, presence: TelegramUserPresence(status: .present(until: presenceTimestamp), lastActivity: presenceTimestamp), summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: [(peer2, .typingText)], isAd: false, ignoreUnreadBadge: false, displayAsMessage: 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, 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, notificationSettings: nil, presence: TelegramUserPresence(status: .present(until: presenceTimestamp), lastActivity: presenceTimestamp), summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: [(peer2, .typingText)], isAd: false, 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, 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, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: 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, 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, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, 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, 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, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: 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, 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, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, 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, 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, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: 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, 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, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, 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, 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))]), notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: 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, 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))]), notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, 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

@ -439,17 +439,17 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
let timestamp = self.referenceTimestamp
let timestamp1 = timestamp + 120
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, 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))]), notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: 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, 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))]), notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, 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, 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))]), notificationSettings: nil, presence: TelegramUserPresence(status: .present(until: presenceTimestamp), lastActivity: presenceTimestamp), summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: 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, 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))]), notificationSettings: nil, presence: TelegramUserPresence(status: .present(until: presenceTimestamp), lastActivity: presenceTimestamp), summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, 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, 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, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: 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, 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, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, 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, 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, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: 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, 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, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, 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

@ -351,24 +351,24 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
let timestamp = self.referenceTimestamp
let timestamp1 = timestamp + 120
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, 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))]), notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: 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, 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))]), notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, 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, 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, notificationSettings: nil, presence: TelegramUserPresence(status: .present(until: presenceTimestamp), lastActivity: presenceTimestamp), summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: [(peer2, .typingText)], isAd: false, ignoreUnreadBadge: false, displayAsMessage: 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, 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, notificationSettings: nil, presence: TelegramUserPresence(status: .present(until: presenceTimestamp), lastActivity: presenceTimestamp), summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: [(peer2, .typingText)], isAd: false, 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, 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, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: 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, 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, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, 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, 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, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: 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, 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, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, 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, 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, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: 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, 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, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, 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, 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))]), notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: 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, 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))]), notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, 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, 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, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: 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, 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, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, 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

@ -804,7 +804,7 @@ public final class ShareController: ViewController {
var peers: [RenderedPeer] = []
for entry in view.0.entries.reversed() {
switch entry {
case let .MessageEntry(_, _, _, _, _, renderedPeer, _, _):
case let .MessageEntry(_, _, _, _, _, renderedPeer, _, _, _):
if let peer = renderedPeer.peers[renderedPeer.peerId], peer.id != accountPeer.id, canSendMessagesToPeer(peer) {
peers.append(renderedPeer)
}

View File

@ -336,7 +336,7 @@ final class ChatHistoryPreloadManager {
var indices: [(ChatHistoryPreloadIndex, Bool, Bool)] = []
for entry in view.0.entries {
if case let .MessageEntry(index, _, readState, notificationSettings, _, _, _, _) = entry {
if case let .MessageEntry(index, _, readState, notificationSettings, _, _, _, _, _) = entry {
var hasUnread = false
if let readState = readState {
hasUnread = readState.count != 0

View File

@ -133,6 +133,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
case cellularDataPermissionWarning = 13
case chatMessageSearchResultsTip = 14
case chatMessageOptionsTip = 15
case chatTextSelectionTip = 16
var key: ValueBoxKey {
let v = ValueBoxKey(length: 4)
@ -238,6 +239,10 @@ private struct ApplicationSpecificNoticeKeys {
static func chatMessageOptionsTip() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.chatMessageOptionsTip.key)
}
static func chatTextSelectionTip() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.chatTextSelectionTip.key)
}
}
public struct ApplicationSpecificNotice {
@ -573,6 +578,28 @@ public struct ApplicationSpecificNotice {
}
}
public static func getChatTextSelectionTips(accountManager: AccountManager) -> Signal<Int32, NoError> {
return accountManager.transaction { transaction -> Int32 in
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatTextSelectionTip()) as? ApplicationSpecificCounterNotice {
return value.value
} else {
return 0
}
}
}
public static func incrementChatTextSelectionTips(accountManager: AccountManager, count: Int32 = 1) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
var currentValue: Int32 = 0
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatTextSelectionTip()) as? ApplicationSpecificCounterNotice {
currentValue = value.value
}
currentValue += count
transaction.setNotice(ApplicationSpecificNoticeKeys.chatTextSelectionTip(), ApplicationSpecificCounterNotice(value: currentValue))
}
}
public static func reset(accountManager: AccountManager) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lamp.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -561,7 +561,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
break
}
}
let _ = combineLatest(queue: .mainQueue(), contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: strongSelf.presentationInterfaceState, context: strongSelf.context, messages: updatedMessages, controllerInteraction: strongSelf.controllerInteraction, selectAll: selectAll, interfaceInteraction: strongSelf.interfaceInteraction), loadedStickerPack(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, reference: .animatedEmoji, forceActualized: false)).start(next: { actions, animatedEmojiStickers in
let _ = combineLatest(queue: .mainQueue(), contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: strongSelf.presentationInterfaceState, context: strongSelf.context, messages: updatedMessages, controllerInteraction: strongSelf.controllerInteraction, selectAll: selectAll, interfaceInteraction: strongSelf.interfaceInteraction), loadedStickerPack(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, reference: .animatedEmoji, forceActualized: false), ApplicationSpecificNotice.getChatTextSelectionTips(accountManager: strongSelf.context.sharedContext.accountManager)
).start(next: { actions, animatedEmojiStickers, chatTextSelectionTips in
guard let strongSelf = self, !actions.isEmpty else {
return
}
@ -590,7 +591,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if Namespaces.Message.allScheduled.contains(message.id.namespace) {
reactionItems = []
}
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: message)), items: .single(actions), reactionItems: reactionItems, recognizer: recognizer)
let displayTextSelectionTip = !message.text.isEmpty && chatTextSelectionTips < 3
if displayTextSelectionTip {
let _ = ApplicationSpecificNotice.incrementChatTextSelectionTips(accountManager: strongSelf.context.sharedContext.accountManager).start()
}
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: message)), items: .single(actions), reactionItems: reactionItems, recognizer: recognizer, displayTextSelectionTip: displayTextSelectionTip)
strongSelf.currentContextController = controller
controller.reactionSelected = { [weak controller] value in
guard let strongSelf = self, let message = updatedMessages.first else {

View File

@ -11,6 +11,7 @@ import TelegramStringFormatting
import MergeLists
import ChatListUI
import AccountContext
import ContextUI
private enum ChatListSearchEntryStableId: Hashable {
case messageId(MessageId)
@ -76,7 +77,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, index: ChatListIndex(pinningIndex: nil, messageIndex: message.index), content: .peer(message: message, peer: peer, combinedReadState: readState, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: true, displayAsMessage: true), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
return ChatListItem(presentationData: presentationData, context: context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: message.index), content: .peer(message: message, peer: peer, combinedReadState: readState, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: true, displayAsMessage: true, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
}
}
}
@ -129,7 +130,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
private let previousEntries = Atomic<[ChatListSearchEntry]?>(value: nil)
init(context: AccountContext, location: SearchMessagesLocation, searchQuery: String, searchResult: SearchMessagesResult, searchState: SearchMessagesState) {
init(context: AccountContext, location: SearchMessagesLocation, searchQuery: String, searchResult: SearchMessagesResult, searchState: SearchMessagesState, presentInGlobalOverlay: @escaping (ViewController) -> Void) {
self.context = context
self.location = location
self.searchQuery = searchQuery
@ -183,7 +184,24 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
}, updatePeerGrouping: { _, _ in
}, togglePeerMarkedUnread: { _, _ in
}, toggleArchivedFolderHiddenByDefault: {
}, activateChatPreview: { _, _, _ in
}, activateChatPreview: { [weak self] item, node, gesture in
guard let strongSelf = self else {
gesture?.cancel()
return
}
switch item.content {
case let .peer(peer):
if let message = peer.message {
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)
presentInGlobalOverlay(contextController)
} else {
gesture?.cancel()
}
default:
gesture?.cancel()
}
})
interaction.searchTextHighightState = searchQuery
self.interaction = interaction
@ -317,3 +335,32 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
}
}
}
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
let controller: ViewController
weak var sourceNode: ASDisplayNode?
let navigationController: NavigationController? = nil
let passthroughTouches: Bool = true
init(controller: ViewController, sourceNode: ASDisplayNode?) {
self.controller = controller
self.sourceNode = sourceNode
}
func transitionInfo() -> ContextControllerTakeControllerInfo? {
let sourceNode = self.sourceNode
return ContextControllerTakeControllerInfo(contentAreaInScreenSpace: CGRect(origin: CGPoint(), size: CGSize(width: 10.0, height: 10.0)), sourceNode: { [weak sourceNode] in
if let sourceNode = sourceNode {
return (sourceNode, sourceNode.bounds)
} else {
return nil
}
})
}
func animatedIn() {
}
}

View File

@ -64,7 +64,9 @@ final class ChatSearchResultsController: ViewController {
}
override public func loadDisplayNode() {
self.displayNode = ChatSearchResultsControllerNode(context: self.context, location: self.location, searchQuery: self.searchQuery, searchResult: self.searchResult, searchState: self.searchState)
self.displayNode = ChatSearchResultsControllerNode(context: self.context, location: self.location, searchQuery: self.searchQuery, searchResult: self.searchResult, searchState: self.searchState, presentInGlobalOverlay: { [weak self] c in
self?.presentInGlobalOverlay(c)
})
self.controllerNode.resultSelected = { [weak self] messageIndex in
self?.navigateToMessageIndex(messageIndex)
self?.dismiss()

View File

@ -34,12 +34,12 @@ private func cancelScrollViewGestures(view: UIView?) {
}
}
private func generateKnobImage(color: UIColor, inverted: Bool = false) -> UIImage? {
private func generateKnobImage(color: UIColor, diameter: CGFloat, inverted: Bool = false) -> UIImage? {
let f: (CGSize, CGContext) -> Void = { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(color.cgColor)
context.fill(CGRect(origin: CGPoint(x: (size.width - 2.0) / 2.0, y: size.width / 2.0), size: CGSize(width: 2.0, height: size.height - size.width / 2.0 - 1.0)))
context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.width)))
context.fillEllipse(in: CGRect(origin: CGPoint(x: floor((size.width - diameter) / 2.0), y: floor((size.width - diameter) / 2.0)), size: CGSize(width: diameter, height: diameter)))
context.fillEllipse(in: CGRect(origin: CGPoint(x: (size.width - 2.0) / 2.0, y: size.width + 2.0), size: CGSize(width: 2.0, height: 2.0)))
}
let size = CGSize(width: 12.0, height: 12.0 + 2.0 + 2.0)
@ -53,10 +53,12 @@ private func generateKnobImage(color: UIColor, inverted: Bool = false) -> UIImag
public final class TextSelectionTheme {
public let selection: UIColor
public let knob: UIColor
public let knobDiameter: CGFloat
public init(selection: UIColor, knob: UIColor) {
public init(selection: UIColor, knob: UIColor, knobDiameter: CGFloat = 12.0) {
self.selection = selection
self.knob = knob
self.knobDiameter = knobDiameter
}
}
@ -204,6 +206,9 @@ public final class TextSelectionNode: ASDisplayNode {
public let highlightAreaNode: ASDisplayNode
private var recognizer: TextSelectionGetureRecognizer?
private var displayLinkAnimator: DisplayLinkAnimator?
public init(theme: TextSelectionTheme, strings: PresentationStrings, textNode: TextNode, updateIsActive: @escaping (Bool) -> Void, present: @escaping (ViewController, Any?) -> Void, rootNode: ASDisplayNode, performAction: @escaping (String, TextSelectionAction) -> Void) {
self.theme = theme
self.strings = strings
@ -214,13 +219,13 @@ public final class TextSelectionNode: ASDisplayNode {
self.performAction = performAction
self.leftKnob = ASImageNode()
self.leftKnob.isUserInteractionEnabled = false
self.leftKnob.image = generateKnobImage(color: theme.knob)
self.leftKnob.image = generateKnobImage(color: theme.knob, diameter: theme.knobDiameter)
self.leftKnob.displaysAsynchronously = false
self.leftKnob.displayWithoutProcessing = true
self.leftKnob.alpha = 0.0
self.rightKnob = ASImageNode()
self.rightKnob.isUserInteractionEnabled = false
self.rightKnob.image = generateKnobImage(color: theme.knob, inverted: true)
self.rightKnob.image = generateKnobImage(color: theme.knob, diameter: theme.knobDiameter, inverted: true)
self.rightKnob.displaysAsynchronously = false
self.rightKnob.displayWithoutProcessing = true
self.rightKnob.alpha = 0.0
@ -255,7 +260,6 @@ public final class TextSelectionNode: ASDisplayNode {
let mappedPoint = strongSelf.view.convert(point, to: strongSelf.textNode.view)
if let stringIndex = strongSelf.textNode.attributesAtPoint(mappedPoint, orNearest: true)?.0 {
//let string = attributedString.string as NSString
var updatedMin = currentRange.0
var updatedMax = currentRange.1
switch knob {
@ -324,6 +328,7 @@ public final class TextSelectionNode: ASDisplayNode {
self?.dismissSelection()
self?.updateIsActive(false)
}
self.recognizer = recognizer
self.view.addGestureRecognizer(recognizer)
}
@ -337,6 +342,60 @@ public final class TextSelectionNode: ASDisplayNode {
}
}
public func pretendInitiateSelection() {
guard let cachedLayout = self.textNode.cachedLayout, let attributedString = cachedLayout.attributedString else {
return
}
var resultRange: NSRange?
let stringIndex = 0
let string = attributedString.string as NSString
let inputRange = CFRangeMake(0, string.length)
let flag = UInt(kCFStringTokenizerUnitWord)
let locale = CFLocaleCopyCurrent()
let tokenizer = CFStringTokenizerCreate(kCFAllocatorDefault, string as CFString, inputRange, flag, locale)
var tokenType = CFStringTokenizerAdvanceToNextToken(tokenizer)
while !tokenType.isEmpty {
let currentTokenRange = CFStringTokenizerGetCurrentTokenRange(tokenizer)
if currentTokenRange.location <= stringIndex && currentTokenRange.location + currentTokenRange.length > stringIndex {
resultRange = NSRange(location: currentTokenRange.location, length: currentTokenRange.length)
break
}
tokenType = CFStringTokenizerAdvanceToNextToken(tokenizer)
}
if resultRange == nil {
resultRange = NSRange(location: stringIndex, length: 1)
}
self.currentRange = resultRange.flatMap {
($0.lowerBound, $0.upperBound)
}
self.updateSelection(range: resultRange, animateIn: true)
self.updateIsActive(true)
}
public func pretendExtendSelection(to index: Int) {
guard let cachedLayout = self.textNode.cachedLayout, let attributedString = cachedLayout.attributedString, let endRangeRect = cachedLayout.rangeRects(in: NSRange(location: index, length: 1))?.first else {
return
}
let startPoint = self.rightKnob.frame.center
let endPoint = endRangeRect.center
let displayLinkAnimator = DisplayLinkAnimator(duration: 0.3, from: 0.0, to: 1.0, update: { [weak self] progress in
guard let strongSelf = self else {
return
}
let point = CGPoint(x: (1.0 - progress) * startPoint.x + progress * endPoint.x, y: (1.0 - progress) * startPoint.y + progress * endPoint.y)
strongSelf.recognizer?.moveKnob?(.right, point)
}, completion: { [weak self] in
guard let strongSelf = self else {
return
}
})
self.displayLinkAnimator = displayLinkAnimator
}
private func updateSelection(range: NSRange?, animateIn: Bool) {
var rects: [CGRect]?
@ -362,8 +421,8 @@ public final class TextSelectionNode: ASDisplayNode {
highlightOverlay.frame = self.bounds
highlightOverlay.updateRects(rects)
if let image = self.leftKnob.image {
self.leftKnob.frame = CGRect(origin: CGPoint(x: floor(rects[0].minX - 1.0 - image.size.width / 2.0), y: rects[0].minY - 1.0 - image.size.width), size: CGSize(width: image.size.width, height: image.size.width + rects[0].height + 2.0))
self.rightKnob.frame = CGRect(origin: CGPoint(x: floor(rects[rects.count - 1].maxX + 1.0 - image.size.width / 2.0), y: rects[rects.count - 1].maxY + 1.0 - (rects[0].height + 2.0)), size: CGSize(width: image.size.width, height: image.size.width + rects[0].height + 2.0))
self.leftKnob.frame = CGRect(origin: CGPoint(x: floor(rects[0].minX - 1.0 - image.size.width / 2.0), y: rects[0].minY - 1.0 - self.theme.knobDiameter), size: CGSize(width: image.size.width, height: self.theme.knobDiameter + rects[0].height + 2.0))
self.rightKnob.frame = CGRect(origin: CGPoint(x: floor(rects[rects.count - 1].maxX + 1.0 - image.size.width / 2.0), y: rects[rects.count - 1].maxY + 1.0 - (rects[0].height + 2.0)), size: CGSize(width: image.size.width, height: self.theme.knobDiameter + rects[0].height + 2.0))
}
if self.leftKnob.alpha.isZero {
highlightOverlay.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)

View File

@ -319,7 +319,7 @@ 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, _, _) = entry {
if case let .MessageEntry(index, message, readState, _, _, renderedPeer, _, _, hasFailed) = entry {
guard index.messageIndex.id.peerId.namespace != Namespaces.Peer.SecretChat else {
return nil
}
@ -332,7 +332,7 @@ func makeBridgeChat(_ entry: ChatListEntry, strings: PresentationStrings) -> (TG
bridgeChat.text = message.text
bridgeChat.outgoing = !message.flags.contains(.Incoming)
bridgeChat.deliveryState = makeBridgeDeliveryState(message)
bridgeChat.deliveryError = message.flags.contains(.Failed)
bridgeChat.deliveryError = hasFailed
bridgeChat.media = makeBridgeMedia(message: message, strings: strings, filterUnsupportedActions: false)
}
bridgeChat.unread = readState?.isUnread ?? false