diff --git a/TelegramUI.xcodeproj/project.pbxproj b/TelegramUI.xcodeproj/project.pbxproj index de37bfbcfb..46a8349703 100644 --- a/TelegramUI.xcodeproj/project.pbxproj +++ b/TelegramUI.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ D06879571DB8F22200424BBD /* FetchCachedRepresentations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06879561DB8F22200424BBD /* FetchCachedRepresentations.swift */; }; D073CE631DCBBE5D007511FD /* MessageSent.caf in Resources */ = {isa = PBXBuildFile; fileRef = D073CE621DCBBE5D007511FD /* MessageSent.caf */; }; D073CE651DCBC26B007511FD /* ServiceSoundManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D073CE641DCBC26B007511FD /* ServiceSoundManager.swift */; }; + D073CE711DCBF23F007511FD /* DeclareEncodables.swift in Sources */ = {isa = PBXBuildFile; fileRef = D073CE701DCBF23F007511FD /* DeclareEncodables.swift */; }; D07A7DA31D957671005BCD27 /* ListMessageSnippetItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07A7DA21D957671005BCD27 /* ListMessageSnippetItemNode.swift */; }; D07A7DA51D95783C005BCD27 /* ListMessageNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07A7DA41D95783C005BCD27 /* ListMessageNode.swift */; }; D07CFF741DCA207200761F81 /* PeerSelectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07CFF731DCA207200761F81 /* PeerSelectionController.swift */; }; @@ -256,6 +257,7 @@ D06879561DB8F22200424BBD /* FetchCachedRepresentations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchCachedRepresentations.swift; sourceTree = ""; }; D073CE621DCBBE5D007511FD /* MessageSent.caf */ = {isa = PBXFileReference; lastKnownFileType = file; name = MessageSent.caf; path = TelegramUI/Sounds/MessageSent.caf; sourceTree = ""; }; D073CE641DCBC26B007511FD /* ServiceSoundManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceSoundManager.swift; sourceTree = ""; }; + D073CE701DCBF23F007511FD /* DeclareEncodables.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeclareEncodables.swift; sourceTree = ""; }; D07A7DA21D957671005BCD27 /* ListMessageSnippetItemNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListMessageSnippetItemNode.swift; sourceTree = ""; }; D07A7DA41D95783C005BCD27 /* ListMessageNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListMessageNode.swift; sourceTree = ""; }; D07CFF731DCA207200761F81 /* PeerSelectionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerSelectionController.swift; sourceTree = ""; }; @@ -1106,6 +1108,7 @@ D0B844571DAC44E8005F29E1 /* PeerPresenceStatusManager.swift */, D0ED5D4A1DC806D7007CBB15 /* ApplicationSpecificData.swift */, D073CE641DCBC26B007511FD /* ServiceSoundManager.swift */, + D073CE701DCBF23F007511FD /* DeclareEncodables.swift */, ); name = Utils; sourceTree = ""; @@ -1432,6 +1435,7 @@ D0F69DFF1D6B8A880046BCD6 /* ChatListController.swift in Sources */, D0E7A1C11D8C258D00C37A6F /* ChatHistoryEntriesForView.swift in Sources */, D0F69DF11D6B8A6C0046BCD6 /* AuthorizationController.swift in Sources */, + D073CE711DCBF23F007511FD /* DeclareEncodables.swift in Sources */, D0E7A1BF1D8C24B900C37A6F /* ChatHistoryViewForLocation.swift in Sources */, D0F69E891D6B8C850046BCD6 /* FastBlur.m in Sources */, D07CFF761DCA224100761F81 /* PeerSelectionControllerNode.swift in Sources */, diff --git a/TelegramUI/AccessoryPanelNode.swift b/TelegramUI/AccessoryPanelNode.swift index 4c177c0260..a472cc91d6 100644 --- a/TelegramUI/AccessoryPanelNode.swift +++ b/TelegramUI/AccessoryPanelNode.swift @@ -4,10 +4,4 @@ import AsyncDisplayKit class AccessoryPanelNode: ASDisplayNode { var dismiss: (() -> Void)? var interfaceInteraction: ChatPanelInterfaceInteraction? - - var insets = UIEdgeInsets() { - didSet { - self.setNeedsLayout() - } - } } diff --git a/TelegramUI/ChatController.swift b/TelegramUI/ChatController.swift index f098408c0c..1df42b0c6c 100644 --- a/TelegramUI/ChatController.swift +++ b/TelegramUI/ChatController.swift @@ -453,7 +453,8 @@ public class ChatController: ViewController { super.viewWillDisappear(animated) let peerId = self.peerId - let interfaceState = self.presentationInterfaceState.interfaceState + let timestamp = Int32(Date().timeIntervalSince1970) + let interfaceState = self.presentationInterfaceState.interfaceState.withUpdatedTimestamp(timestamp) self.account.postbox.modify({ modifier -> Void in modifier.updatePeerChatInterfaceState(peerId, update: { _ in return interfaceState diff --git a/TelegramUI/ChatControllerNode.swift b/TelegramUI/ChatControllerNode.swift index 438d5e6616..90b66cebbf 100644 --- a/TelegramUI/ChatControllerNode.swift +++ b/TelegramUI/ChatControllerNode.swift @@ -144,6 +144,10 @@ class ChatControllerNode: ASDisplayNode { if let inputNode = self.inputNode { previousInputHeight = inputNode.bounds.size.height } + var previousInputPanelOrigin = CGPoint(x: 0.0, y: layout.size.height - previousInputHeight) + if let inputPanelNode = self.inputPanelNode { + previousInputPanelOrigin.y -= inputPanelNode.bounds.size.height + } self.containerLayoutAndNavigationBarHeight = (layout, navigationBarHeight) var dismissedInputNode: ChatInputNode? @@ -261,7 +265,6 @@ class ChatControllerNode: ASDisplayNode { } immediatelyLayoutAccessoryPanelAndAnimateAppearance = true - accessoryPanelNode.insets = UIEdgeInsets(top: 0.0, left: 45.0, bottom: 0.0, right: 54.0) } } else if let accessoryPanelNode = self.accessoryPanelNode { dismissedAccessoryPanelNode = self.accessoryPanelNode @@ -322,7 +325,9 @@ class ChatControllerNode: ASDisplayNode { if let accessoryPanelNode = self.accessoryPanelNode, let accessoryPanelFrame = accessoryPanelFrame, !accessoryPanelNode.frame.equalTo(accessoryPanelFrame) { if immediatelyLayoutAccessoryPanelAndAnimateAppearance { - accessoryPanelNode.frame = accessoryPanelFrame.offsetBy(dx: 0.0, dy: accessoryPanelFrame.size.height) + var startAccessoryPanelFrame = accessoryPanelFrame + startAccessoryPanelFrame.origin.y = previousInputPanelOrigin.y + accessoryPanelNode.frame = startAccessoryPanelFrame accessoryPanelNode.alpha = 0.0 } diff --git a/TelegramUI/ChatInterfaceState.swift b/TelegramUI/ChatInterfaceState.swift index a5b4386eb3..4c5f7bd107 100644 --- a/TelegramUI/ChatInterfaceState.swift +++ b/TelegramUI/ChatInterfaceState.swift @@ -57,24 +57,59 @@ struct ChatTextInputState: Coding, Equatable { } } +final class ChatEmbeddedInterfaceState: PeerChatListEmbeddedInterfaceState { + let timestamp: Int32 + let text: String + + init(timestamp: Int32, text: String) { + self.timestamp = timestamp + self.text = text + } + + init(decoder: Decoder) { + self.timestamp = decoder.decodeInt32ForKey("d") + self.text = decoder.decodeStringForKey("t") + } + + func encode(_ encoder: Encoder) { + encoder.encodeInt32(self.timestamp, forKey: "d") + encoder.encodeString(self.text, forKey: "t") + } + + public func isEqual(to: PeerChatListEmbeddedInterfaceState) -> Bool { + if let to = to as? ChatEmbeddedInterfaceState { + return self.timestamp == to.timestamp && self.text == to.text + } else { + return false + } + } +} + final class ChatInterfaceState: PeerChatInterfaceState, Equatable { + let timestamp: Int32 let inputState: ChatTextInputState let replyMessageId: MessageId? let forwardMessageIds: [MessageId]? let selectionState: ChatInterfaceSelectionState? var chatListEmbeddedState: PeerChatListEmbeddedInterfaceState? { - return nil + if !self.inputState.inputText.isEmpty && self.timestamp != 0 { + return ChatEmbeddedInterfaceState(timestamp: self.timestamp, text: self.inputState.inputText) + } else { + return nil + } } init() { + self.timestamp = 0 self.inputState = ChatTextInputState() self.replyMessageId = nil self.forwardMessageIds = nil self.selectionState = nil } - init(inputState: ChatTextInputState, replyMessageId: MessageId?, forwardMessageIds: [MessageId]?, selectionState: ChatInterfaceSelectionState?) { + init(timestamp: Int32, inputState: ChatTextInputState, replyMessageId: MessageId?, forwardMessageIds: [MessageId]?, selectionState: ChatInterfaceSelectionState?) { + self.timestamp = timestamp self.inputState = inputState self.replyMessageId = replyMessageId self.forwardMessageIds = forwardMessageIds @@ -82,6 +117,7 @@ final class ChatInterfaceState: PeerChatInterfaceState, Equatable { } init(decoder: Decoder) { + self.timestamp = decoder.decodeInt32ForKey("ts") if let inputState = decoder.decodeObjectForKey("is", decoder: { return ChatTextInputState(decoder: $0) }) as? ChatTextInputState { self.inputState = inputState } else { @@ -108,6 +144,7 @@ final class ChatInterfaceState: PeerChatInterfaceState, Equatable { } func encode(_ encoder: Encoder) { + encoder.encodeInt32(self.timestamp, forKey: "ts") encoder.encodeObject(self.inputState, forKey: "is") if let replyMessageId = self.replyMessageId { encoder.encodeInt64(replyMessageId.peerId.toInt64(), forKey: "r.p") @@ -152,15 +189,15 @@ final class ChatInterfaceState: PeerChatInterfaceState, Equatable { } func withUpdatedInputState(_ inputState: ChatTextInputState) -> ChatInterfaceState { - return ChatInterfaceState(inputState: inputState, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, selectionState: self.selectionState) + return ChatInterfaceState(timestamp: self.timestamp, inputState: inputState, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, selectionState: self.selectionState) } func withUpdatedReplyMessageId(_ replyMessageId: MessageId?) -> ChatInterfaceState { - return ChatInterfaceState(inputState: self.inputState, replyMessageId: replyMessageId, forwardMessageIds: self.forwardMessageIds, selectionState: self.selectionState) + return ChatInterfaceState(timestamp: self.timestamp, inputState: self.inputState, replyMessageId: replyMessageId, forwardMessageIds: self.forwardMessageIds, selectionState: self.selectionState) } func withUpdatedForwardMessageIds(_ forwardMessageIds: [MessageId]?) -> ChatInterfaceState { - return ChatInterfaceState(inputState: self.inputState, replyMessageId: self.replyMessageId, forwardMessageIds: forwardMessageIds, selectionState: self.selectionState) + return ChatInterfaceState(timestamp: self.timestamp, inputState: self.inputState, replyMessageId: self.replyMessageId, forwardMessageIds: forwardMessageIds, selectionState: self.selectionState) } func withUpdatedSelectedMessage(_ messageId: MessageId) -> ChatInterfaceState { @@ -169,7 +206,7 @@ final class ChatInterfaceState: PeerChatInterfaceState, Equatable { selectedIds.formUnion(selectionState.selectedIds) } selectedIds.insert(messageId) - return ChatInterfaceState(inputState: self.inputState, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, selectionState: ChatInterfaceSelectionState(selectedIds: selectedIds)) + return ChatInterfaceState(timestamp: self.timestamp, inputState: self.inputState, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, selectionState: ChatInterfaceSelectionState(selectedIds: selectedIds)) } func withToggledSelectedMessage(_ messageId: MessageId) -> ChatInterfaceState { @@ -182,10 +219,14 @@ final class ChatInterfaceState: PeerChatInterfaceState, Equatable { } else { selectedIds.insert(messageId) } - return ChatInterfaceState(inputState: self.inputState, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, selectionState: ChatInterfaceSelectionState(selectedIds: selectedIds)) + return ChatInterfaceState(timestamp: self.timestamp, inputState: self.inputState, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, selectionState: ChatInterfaceSelectionState(selectedIds: selectedIds)) } func withoutSelectionState() -> ChatInterfaceState { - return ChatInterfaceState(inputState: self.inputState, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, selectionState: nil) + return ChatInterfaceState(timestamp: self.timestamp, inputState: self.inputState, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, selectionState: nil) + } + + func withUpdatedTimestamp(_ timestamp: Int32) -> ChatInterfaceState { + return ChatInterfaceState(timestamp: timestamp, inputState: self.inputState, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, selectionState: self.selectionState) } } diff --git a/TelegramUI/ChatListItem.swift b/TelegramUI/ChatListItem.swift index a777491cea..33c0833ba0 100644 --- a/TelegramUI/ChatListItem.swift +++ b/TelegramUI/ChatListItem.swift @@ -11,22 +11,24 @@ class ChatListItem: ListViewItem { let message: Message let combinedReadState: CombinedPeerReadState? let notificationSettings: PeerNotificationSettings? + let embeddedState: PeerChatListEmbeddedInterfaceState? let action: (Message) -> Void let selectable: Bool = true - init(account: Account, message: Message, combinedReadState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, action: @escaping (Message) -> Void) { + init(account: Account, message: Message, combinedReadState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, embeddedState: PeerChatListEmbeddedInterfaceState?, action: @escaping (Message) -> Void) { self.account = account self.message = message self.combinedReadState = combinedReadState self.notificationSettings = notificationSettings + self.embeddedState = embeddedState self.action = action } func nodeConfiguredForWidth(async: @escaping (@escaping () -> Void) -> Void, width: CGFloat, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> Void) -> Void) { async { let node = ChatListItemNode() - node.setupItem(account: self.account, message: self.message, combinedReadState: self.combinedReadState, notificationSettings: self.notificationSettings) + node.setupItem(account: self.account, message: self.message, combinedReadState: self.combinedReadState, notificationSettings: self.notificationSettings, embeddedState: self.embeddedState) node.relativePosition = (first: previousItem == nil, last: nextItem == nil) node.insets = ChatListItemNode.insets(first: node.relativePosition.first, last: node.relativePosition.last) node.layoutForWidth(width, item: self, previousItem: previousItem, nextItem: nextItem) @@ -38,7 +40,7 @@ class ChatListItem: ListViewItem { assert(node is ChatListItemNode) if let node = node as? ChatListItemNode { Queue.mainQueue().async { - node.setupItem(account: self.account, message: self.message, combinedReadState: self.combinedReadState, notificationSettings: self.notificationSettings) + node.setupItem(account: self.account, message: self.message, combinedReadState: self.combinedReadState, notificationSettings: self.notificationSettings, embeddedState: self.embeddedState) let layout = node.asyncLayout() async { let first = previousItem == nil @@ -115,6 +117,7 @@ class ChatListItemNode: ListViewItemNode { var message: Message? var combinedReadState: CombinedPeerReadState? var notificationSettings: PeerNotificationSettings? + var embeddedState: PeerChatListEmbeddedInterfaceState? private let backgroundNode: ASDisplayNode private let highlightedBackgroundNode: ASDisplayNode @@ -205,11 +208,12 @@ class ChatListItemNode: ListViewItemNode { self.contentNode.addSubnode(self.mutedIconNode) } - func setupItem(account: Account, message: Message, combinedReadState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?) { + func setupItem(account: Account, message: Message, combinedReadState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, embeddedState: PeerChatListEmbeddedInterfaceState?) { self.account = account self.message = message self.combinedReadState = combinedReadState self.notificationSettings = notificationSettings + self.embeddedState = embeddedState let peer = message.peers[message.id.peerId] if let peer = peer { @@ -280,6 +284,7 @@ class ChatListItemNode: ListViewItemNode { let message = self.message let combinedReadState = self.combinedReadState let notificationSettings = self.notificationSettings + let embeddedState = self.embeddedState return { account, width, first, last in var textAttributedString: NSAttributedString? @@ -317,7 +322,12 @@ class ChatListItemNode: ListViewItemNode { } let attributedText: NSAttributedString - if let author = message.author as? TelegramUser, let peer = peer, peer as? TelegramUser == nil { + if let embeddedState = embeddedState as? ChatEmbeddedInterfaceState { + let mutableAttributedText = NSMutableAttributedString() + mutableAttributedText.append(NSAttributedString(string: "Draft: ", font: textFont, textColor: UIColor(0xdd4b39))) + mutableAttributedText.append(NSAttributedString(string: embeddedState.text, font: textFont, textColor: UIColor.black)) + attributedText = mutableAttributedText; + } else if let author = message.author as? TelegramUser, let peer = peer, peer as? TelegramUser == nil { let peerText: NSString = (author.id == account?.peerId ? "You: " : author.compactDisplayTitle + ": ") as NSString let mutableAttributedText = NSMutableAttributedString(string: peerText.appending(messageText as String), attributes: [kCTFontAttributeName as String: textFont]) diff --git a/TelegramUI/ChatListNode.swift b/TelegramUI/ChatListNode.swift index b1160dc267..c012684c70 100644 --- a/TelegramUI/ChatListNode.swift +++ b/TelegramUI/ChatListNode.swift @@ -37,10 +37,10 @@ private func mappedInsertEntries(account: Account, nodeInteraction: ChatListNode return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(placeholder: "Search for messages or users", activate: { nodeInteraction.activateSearch() }), directionHint: entry.directionHint) - case let .MessageEntry(message, combinedReadState, notificationSettings): + case let .MessageEntry(_, message, combinedReadState, notificationSettings, embeddedState): switch mode { case .chatList: - return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(account: account, message: message, combinedReadState: combinedReadState, notificationSettings: notificationSettings, action: { _ in + return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(account: account, message: message, combinedReadState: combinedReadState, notificationSettings: notificationSettings, embeddedState: embeddedState, action: { _ in nodeInteraction.peerSelected(message.id.peerId) }), directionHint: entry.directionHint) case .peers: @@ -63,10 +63,10 @@ private func mappedUpdateEntries(account: Account, nodeInteraction: ChatListNode return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(placeholder: "Search for messages or users", activate: { nodeInteraction.activateSearch() }), directionHint: entry.directionHint) - case let .MessageEntry(message, combinedReadState, notificationSettings): + case let .MessageEntry(_, message, combinedReadState, notificationSettings, embeddedState): switch mode { case .chatList: - return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(account: account, message: message, combinedReadState: combinedReadState, notificationSettings: notificationSettings, action: { _ in + return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(account: account, message: message, combinedReadState: combinedReadState, notificationSettings: notificationSettings, embeddedState: embeddedState, action: { _ in nodeInteraction.peerSelected(message.id.peerId) }), directionHint: entry.directionHint) case .peers: diff --git a/TelegramUI/ChatListNodeEntries.swift b/TelegramUI/ChatListNodeEntries.swift index a77b84de42..86e7735b6c 100644 --- a/TelegramUI/ChatListNodeEntries.swift +++ b/TelegramUI/ChatListNodeEntries.swift @@ -62,7 +62,7 @@ enum ChatListNodeEntryId: Hashable, CustomStringConvertible { enum ChatListNodeEntry: Comparable, Identifiable { case SearchEntry - case MessageEntry(Message, CombinedPeerReadState?, PeerNotificationSettings?) + case MessageEntry(MessageIndex, Message, CombinedPeerReadState?, PeerNotificationSettings?, PeerChatListEmbeddedInterfaceState?) case HoleEntry(ChatListHole) case Nothing(MessageIndex) @@ -70,8 +70,8 @@ enum ChatListNodeEntry: Comparable, Identifiable { switch self { case .SearchEntry: return MessageIndex.absoluteUpperBound() - case let .MessageEntry(message, _, _): - return MessageIndex(message) + case let .MessageEntry(index, _, _, _, _): + return index case let .HoleEntry(hole): return hole.index case let .Nothing(index): @@ -83,8 +83,8 @@ enum ChatListNodeEntry: Comparable, Identifiable { switch self { case .SearchEntry: return .Search - case let .MessageEntry(message, _, _): - return .PeerId(message.id.peerId.toInt64()) + case let .MessageEntry(index, _, _, _, _): + return .PeerId(index.id.peerId.toInt64()) case let .HoleEntry(hole): return .Hole(Int64(hole.index.id.id)) case let .Nothing(index): @@ -100,41 +100,51 @@ enum ChatListNodeEntry: Comparable, Identifiable { switch lhs { case .SearchEntry: switch rhs { - case .SearchEntry: - return true - default: - return false - } - case let .MessageEntry(lhsMessage, lhsUnreadCount, lhsNotificationSettings): - switch rhs { - case let .MessageEntry(rhsMessage, rhsUnreadCount, rhsNotificationSettings): - if lhsMessage.id != rhsMessage.id || lhsMessage.flags != rhsMessage.flags || lhsUnreadCount != rhsUnreadCount { + case .SearchEntry: + return true + default: return false - } - if let lhsNotificationSettings = lhsNotificationSettings, let rhsNotificationSettings = rhsNotificationSettings { - if !lhsNotificationSettings.isEqual(to: rhsNotificationSettings) { + } + case let .MessageEntry(lhsIndex, lhsMessage, lhsUnreadCount, lhsNotificationSettings, lhsEmbeddedState): + switch rhs { + case let .MessageEntry(rhsIndex, rhsMessage, rhsUnreadCount, rhsNotificationSettings, rhsEmbeddedState): + if lhsIndex != rhsIndex { return false } - } else if (lhsNotificationSettings != nil) != (rhsNotificationSettings != nil) { - return false - } - return true - default: - break + if lhsMessage.id != rhsMessage.id || lhsMessage.flags != rhsMessage.flags || lhsUnreadCount != rhsUnreadCount { + return false + } + if let lhsNotificationSettings = lhsNotificationSettings, let rhsNotificationSettings = rhsNotificationSettings { + if !lhsNotificationSettings.isEqual(to: rhsNotificationSettings) { + return false + } + } else if (lhsNotificationSettings != nil) != (rhsNotificationSettings != nil) { + return false + } + if let lhsEmbeddedState = lhsEmbeddedState, let rhsEmbeddedState = rhsEmbeddedState { + if !lhsEmbeddedState.isEqual(to: rhsEmbeddedState) { + return false + } + } else if (lhsEmbeddedState != nil) != (rhsEmbeddedState != nil) { + return false + } + return true + default: + break } case let .HoleEntry(lhsHole): switch rhs { - case let .HoleEntry(rhsHole): - return lhsHole == rhsHole - default: - return false + case let .HoleEntry(rhsHole): + return lhsHole == rhsHole + default: + return false } case let .Nothing(lhsIndex): switch rhs { - case let .Nothing(rhsIndex): - return lhsIndex == rhsIndex - default: - return false + case let .Nothing(rhsIndex): + return lhsIndex == rhsIndex + default: + return false } } return false @@ -145,8 +155,8 @@ func chatListNodeEntriesForView(_ view: ChatListView) -> [ChatListNodeEntry] { var result: [ChatListNodeEntry] = [] for entry in view.entries { switch entry { - case let .MessageEntry(message, combinedReadState, notificationSettings): - result.append(.MessageEntry(message, combinedReadState, notificationSettings)) + case let .MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState): + result.append(.MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState)) case let .HoleEntry(hole): result.append(.HoleEntry(hole)) case let .Nothing(index): diff --git a/TelegramUI/ChatListSearchContainerNode.swift b/TelegramUI/ChatListSearchContainerNode.swift index 086c11e417..e9977c5040 100644 --- a/TelegramUI/ChatListSearchContainerNode.swift +++ b/TelegramUI/ChatListSearchContainerNode.swift @@ -59,7 +59,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode { for item in items { switch item { case let .message(message): - listItems.append(ChatListItem(account: account, message: message, combinedReadState: nil, notificationSettings: nil, action: { [weak strongSelf] _ in + listItems.append(ChatListItem(account: account, message: message, combinedReadState: nil, notificationSettings: nil, embeddedState: nil, action: { [weak strongSelf] _ in if let strongSelf = strongSelf, let peer = message.peers[message.id.peerId] { strongSelf.listNode.clearHighlightAnimated(true) strongSelf.openMessage(peer, message.id) diff --git a/TelegramUI/ChatTextInputPanelNode.swift b/TelegramUI/ChatTextInputPanelNode.swift index 6c68e6919e..8c9c0337d0 100644 --- a/TelegramUI/ChatTextInputPanelNode.swift +++ b/TelegramUI/ChatTextInputPanelNode.swift @@ -391,17 +391,21 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { if self.sendButton.alpha.isZero { self.sendButton.alpha = 1.0 self.micButton.alpha = 0.0 - self.sendButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) - self.sendButton.layer.animateSpring(from: NSNumber(value: Float(0.1)), to: NSNumber(value: Float(1.0)), keyPath: "transform.scale", duration: 0.6) - self.micButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) + if animated { + self.sendButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) + self.sendButton.layer.animateSpring(from: NSNumber(value: Float(0.1)), to: NSNumber(value: Float(1.0)), keyPath: "transform.scale", duration: 0.6) + self.micButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) + } } } else { if self.micButton.alpha.isZero { self.micButton.alpha = 1.0 self.sendButton.alpha = 0.0 - self.micButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) - self.micButton.layer.animateSpring(from: NSNumber(value: Float(0.1)), to: NSNumber(value: Float(1.0)), keyPath: "transform.scale", duration: 0.6) - self.sendButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) + if animated { + self.micButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) + self.micButton.layer.animateSpring(from: NSNumber(value: Float(0.1)), to: NSNumber(value: Float(1.0)), keyPath: "transform.scale", duration: 0.6) + self.sendButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) + } } } diff --git a/TelegramUI/DeclareEncodables.swift b/TelegramUI/DeclareEncodables.swift new file mode 100644 index 0000000000..da8319cb27 --- /dev/null +++ b/TelegramUI/DeclareEncodables.swift @@ -0,0 +1,11 @@ +import Postbox + +private var telegramUIDeclaredEncodables: Void = { + declareEncodable(ChatInterfaceState.self, f: { ChatInterfaceState(decoder: $0) }) + declareEncodable(ChatEmbeddedInterfaceState.self, f: { ChatEmbeddedInterfaceState(decoder: $0) }) + return +}() + +public func telegramUIDeclareEncodables() { + let _ = telegramUIDeclaredEncodables +} diff --git a/TelegramUI/ForwardAccessoryPanelNode.swift b/TelegramUI/ForwardAccessoryPanelNode.swift index 2e7828ef61..b8086b4135 100644 --- a/TelegramUI/ForwardAccessoryPanelNode.swift +++ b/TelegramUI/ForwardAccessoryPanelNode.swift @@ -5,12 +5,26 @@ import Postbox import SwiftSignalKit import Display +private let lineImage = generateVerticallyStretchableFilledCircleImage(radius: 1.0, color: UIColor(0x007ee5)) +private let closeButtonImage = generateImage(CGSize(width: 12.0, height: 12.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setStrokeColor(UIColor(0x9099A2).cgColor) + context.setLineWidth(2.0) + context.setLineCap(.round) + context.move(to: CGPoint(x: 1.0, y: 1.0)) + context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height - 1.0)) + context.strokePath() + context.move(to: CGPoint(x: size.width - 1.0, y: 1.0)) + context.addLine(to: CGPoint(x: 1.0, y: size.height - 1.0)) + context.strokePath() +}) + final class ForwardAccessoryPanelNode: AccessoryPanelNode { private let messageDisposable = MetaDisposable() let messageIds: [MessageId] let closeButton: ASButtonNode - let lineNode: ASDisplayNode + let lineNode: ASImageNode let titleNode: ASTextNode let textNode: ASTextNode @@ -18,12 +32,14 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode { self.messageIds = messageIds self.closeButton = ASButtonNode() - self.closeButton.setImage(UIImage(bundleImageName: "Chat/Input/Acessory Panels/CloseButton")?.precomposed(), for: []) + self.closeButton.setImage(closeButtonImage, for: []) self.closeButton.hitTestSlop = UIEdgeInsetsMake(-8.0, -8.0, -8.0, -8.0) self.closeButton.displaysAsynchronously = false - self.lineNode = ASDisplayNode() - self.lineNode.backgroundColor = UIColor(0x007ee5) + self.lineNode = ASImageNode() + self.lineNode.displayWithoutProcessing = true + self.lineNode.displaysAsynchronously = false + self.lineNode.image = lineImage self.titleNode = ASTextNode() self.titleNode.truncationMode = .byTruncatingTail @@ -65,8 +81,8 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode { text = "\(messages.count) messages" } - strongSelf.titleNode.attributedText = NSAttributedString(string: authors, font: Font.regular(14.5), textColor: UIColor(0x007ee5)) - strongSelf.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.5), textColor: UIColor.black) + strongSelf.titleNode.attributedText = NSAttributedString(string: authors, font: Font.medium(15.0), textColor: UIColor(0x007ee5)) + strongSelf.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(15.0), textColor: UIColor.black) strongSelf.setNeedsLayout() } @@ -78,24 +94,28 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode { } override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { - return CGSize(width: constrainedSize.width, height: 40.0) + return CGSize(width: constrainedSize.width, height: 45.0) } override func layout() { super.layout() let bounds = self.bounds + let leftInset: CGFloat = 55.0 + let textLineInset: CGFloat = 10.0 + let rightInset: CGFloat = 55.0 + let textRightInset: CGFloat = 20.0 let closeButtonSize = self.closeButton.measure(CGSize(width: 100.0, height: 100.0)) - self.closeButton.frame = CGRect(origin: CGPoint(x: bounds.size.width - self.insets.right - closeButtonSize.width, y: 12.0), size: closeButtonSize) + self.closeButton.frame = CGRect(origin: CGPoint(x: bounds.size.width - rightInset - closeButtonSize.width, y: 19.0), size: closeButtonSize) - self.lineNode.frame = CGRect(origin: CGPoint(x: self.insets.left, y: 8.0), size: CGSize(width: 2.0, height: bounds.size.height - 5.0)) + self.lineNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 8.0), size: CGSize(width: 2.0, height: bounds.size.height - 10.0)) - let titleSize = self.titleNode.measure(CGSize(width: bounds.size.width - 11.0 - insets.left - insets.right - 14.0, height: bounds.size.height)) - self.titleNode.frame = CGRect(origin: CGPoint(x: self.insets.left + 11.0, y: 7.0), size: titleSize) + let titleSize = self.titleNode.measure(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset, height: bounds.size.height)) + self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset, y: 7.0), size: titleSize) - let textSize = self.textNode.measure(CGSize(width: bounds.size.width - 11.0 - insets.left - insets.right - 14.0, height: bounds.size.height)) - self.textNode.frame = CGRect(origin: CGPoint(x: self.insets.left + 11.0, y: 25.0), size: textSize) + let textSize = self.textNode.measure(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset, height: bounds.size.height)) + self.textNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset, y: 25.0), size: textSize) } @objc func closePressed() { diff --git a/TelegramUI/ReplyAccessoryPanelNode.swift b/TelegramUI/ReplyAccessoryPanelNode.swift index 9cf1e327fa..c84412b91d 100644 --- a/TelegramUI/ReplyAccessoryPanelNode.swift +++ b/TelegramUI/ReplyAccessoryPanelNode.swift @@ -5,12 +5,26 @@ import Postbox import SwiftSignalKit import Display +private let lineImage = generateVerticallyStretchableFilledCircleImage(radius: 1.0, color: UIColor(0x007ee5)) +private let closeButtonImage = generateImage(CGSize(width: 12.0, height: 12.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setStrokeColor(UIColor(0x9099A2).cgColor) + context.setLineWidth(2.0) + context.setLineCap(.round) + context.move(to: CGPoint(x: 1.0, y: 1.0)) + context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height - 1.0)) + context.strokePath() + context.move(to: CGPoint(x: size.width - 1.0, y: 1.0)) + context.addLine(to: CGPoint(x: 1.0, y: size.height - 1.0)) + context.strokePath() +}) + final class ReplyAccessoryPanelNode: AccessoryPanelNode { private let messageDisposable = MetaDisposable() let messageId: MessageId let closeButton: ASButtonNode - let lineNode: ASDisplayNode + let lineNode: ASImageNode let titleNode: ASTextNode let textNode: ASTextNode @@ -18,12 +32,14 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { self.messageId = messageId self.closeButton = ASButtonNode() - self.closeButton.setImage(UIImage(bundleImageName: "Chat/Input/Acessory Panels/CloseButton")?.precomposed(), for: []) + self.closeButton.setImage(closeButtonImage, for: []) self.closeButton.hitTestSlop = UIEdgeInsetsMake(-8.0, -8.0, -8.0, -8.0) self.closeButton.displaysAsynchronously = false - self.lineNode = ASDisplayNode() - self.lineNode.backgroundColor = UIColor(0x007ee5) + self.lineNode = ASImageNode() + self.lineNode.displayWithoutProcessing = true + self.lineNode.displaysAsynchronously = false + self.lineNode.image = lineImage self.titleNode = ASTextNode() self.titleNode.truncationMode = .byTruncatingTail @@ -56,8 +72,8 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { text = messageText } - strongSelf.titleNode.attributedText = NSAttributedString(string: authorName, font: Font.regular(14.5), textColor: UIColor(0x007ee5)) - strongSelf.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.5), textColor: UIColor.black) + strongSelf.titleNode.attributedText = NSAttributedString(string: authorName, font: Font.medium(15.0), textColor: UIColor(0x007ee5)) + strongSelf.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(15.0), textColor: UIColor.black) strongSelf.setNeedsLayout() } @@ -69,24 +85,28 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { } override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { - return CGSize(width: constrainedSize.width, height: 40.0) + return CGSize(width: constrainedSize.width, height: 45.0) } override func layout() { super.layout() let bounds = self.bounds + let leftInset: CGFloat = 55.0 + let textLineInset: CGFloat = 10.0 + let rightInset: CGFloat = 55.0 + let textRightInset: CGFloat = 20.0 let closeButtonSize = self.closeButton.measure(CGSize(width: 100.0, height: 100.0)) - self.closeButton.frame = CGRect(origin: CGPoint(x: bounds.size.width - self.insets.right - closeButtonSize.width, y: 12.0), size: closeButtonSize) + self.closeButton.frame = CGRect(origin: CGPoint(x: bounds.size.width - rightInset - closeButtonSize.width, y: 19.0), size: closeButtonSize) - self.lineNode.frame = CGRect(origin: CGPoint(x: self.insets.left, y: 8.0), size: CGSize(width: 2.0, height: bounds.size.height - 5.0)) + self.lineNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 8.0), size: CGSize(width: 2.0, height: bounds.size.height - 10.0)) - let titleSize = self.titleNode.measure(CGSize(width: bounds.size.width - 11.0 - insets.left - insets.right - 14.0, height: bounds.size.height)) - self.titleNode.frame = CGRect(origin: CGPoint(x: self.insets.left + 11.0, y: 7.0), size: titleSize) + let titleSize = self.titleNode.measure(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset, height: bounds.size.height)) + self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset, y: 7.0), size: titleSize) - let textSize = self.textNode.measure(CGSize(width: bounds.size.width - 11.0 - insets.left - insets.right - 14.0, height: bounds.size.height)) - self.textNode.frame = CGRect(origin: CGPoint(x: self.insets.left + 11.0, y: 25.0), size: textSize) + let textSize = self.textNode.measure(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset, height: bounds.size.height)) + self.textNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset, y: 25.0), size: textSize) } @objc func closePressed() {