[WIP] Business

This commit is contained in:
Isaac 2024-02-23 23:12:28 +04:00
parent a603c138e9
commit 50339b3c4c
22 changed files with 411 additions and 67 deletions

View File

@ -94,11 +94,13 @@ public enum ChatListItemContent {
public var commandPrefix: String? public var commandPrefix: String?
public var searchQuery: String? public var searchQuery: String?
public var messageCount: Int? public var messageCount: Int?
public var hideSeparator: Bool
public init(commandPrefix: String?, searchQuery: String?, messageCount: Int?) { public init(commandPrefix: String?, searchQuery: String?, messageCount: Int?, hideSeparator: Bool) {
self.commandPrefix = commandPrefix self.commandPrefix = commandPrefix
self.searchQuery = searchQuery self.searchQuery = searchQuery
self.messageCount = messageCount self.messageCount = messageCount
self.hideSeparator = hideSeparator
} }
} }
@ -1166,6 +1168,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
private var currentItemHeight: CGFloat? private var currentItemHeight: CGFloat?
let forwardedIconNode: ASImageNode let forwardedIconNode: ASImageNode
let textNode: TextNodeWithEntities let textNode: TextNodeWithEntities
var trailingTextBadgeNode: TextNode?
var trailingTextBadgeBackground: UIImageView?
var dustNode: InvisibleInkDustNode? var dustNode: InvisibleInkDustNode?
let inputActivitiesNode: ChatListInputActivitiesNode let inputActivitiesNode: ChatListInputActivitiesNode
let dateNode: TextNode let dateNode: TextNode
@ -1437,6 +1441,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
self.textNode = TextNodeWithEntities() self.textNode = TextNodeWithEntities()
self.textNode.textNode.isUserInteractionEnabled = false self.textNode.textNode.isUserInteractionEnabled = false
self.textNode.textNode.displaysAsynchronously = true self.textNode.textNode.displaysAsynchronously = true
self.textNode.textNode.layer.anchorPoint = CGPoint()
self.inputActivitiesNode = ChatListInputActivitiesNode() self.inputActivitiesNode = ChatListInputActivitiesNode()
self.inputActivitiesNode.isUserInteractionEnabled = false self.inputActivitiesNode.isUserInteractionEnabled = false
@ -1823,6 +1828,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
func asyncLayout() -> (_ item: ChatListItem, _ params: ListViewItemLayoutParams, _ first: Bool, _ last: Bool, _ firstWithHeader: Bool, _ nextIsPinned: Bool) -> (ListViewItemNodeLayout, (Bool, Bool) -> Void) { func asyncLayout() -> (_ item: ChatListItem, _ params: ListViewItemLayoutParams, _ first: Bool, _ last: Bool, _ firstWithHeader: Bool, _ nextIsPinned: Bool) -> (ListViewItemNodeLayout, (Bool, Bool) -> Void) {
let dateLayout = TextNode.asyncLayout(self.dateNode) let dateLayout = TextNode.asyncLayout(self.dateNode)
let textLayout = TextNodeWithEntities.asyncLayout(self.textNode) let textLayout = TextNodeWithEntities.asyncLayout(self.textNode)
let makeTrailingTextBadgeLayout = TextNode.asyncLayout(self.trailingTextBadgeNode)
let titleLayout = TextNode.asyncLayout(self.titleNode) let titleLayout = TextNode.asyncLayout(self.titleNode)
let authorLayout = self.authorNode.asyncLayout() let authorLayout = self.authorNode.asyncLayout()
let makeMeasureLayout = TextNode.asyncLayout(self.measureNode) let makeMeasureLayout = TextNode.asyncLayout(self.measureNode)
@ -2073,7 +2079,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
if case let .peer(peerData) = item.content, let customMessageListData = peerData.customMessageListData, customMessageListData.commandPrefix != nil { if case let .peer(peerData) = item.content, let customMessageListData = peerData.customMessageListData, customMessageListData.commandPrefix != nil {
avatarDiameter = 40.0 avatarDiameter = 40.0
avatarLeftInset = 18.0 + avatarDiameter avatarLeftInset = 17.0 + avatarDiameter
} else { } else {
if item.interaction.isInlineMode { if item.interaction.isInlineMode {
avatarLeftInset = 12.0 avatarLeftInset = 12.0
@ -2666,7 +2672,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
topIndex = peerData.messages.first?.index topIndex = peerData.messages.first?.index
} }
if case let .peer(peerData) = item.content, let customMessageListData = peerData.customMessageListData { if case let .peer(peerData) = item.content, let customMessageListData = peerData.customMessageListData {
if let messageCount = customMessageListData.messageCount { if let messageCount = customMessageListData.messageCount, customMessageListData.commandPrefix == nil {
dateText = "\(messageCount)" dateText = "\(messageCount)"
} else { } else {
dateText = " " dateText = " "
@ -2990,9 +2996,23 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let (authorLayout, authorApply) = authorLayout(item.context, rawContentWidth - badgeSize, item.presentationData.theme, effectiveAuthorTitle, forumThreads) let (authorLayout, authorApply) = authorLayout(item.context, rawContentWidth - badgeSize, item.presentationData.theme, effectiveAuthorTitle, forumThreads)
var textBottomRightCutout: CGFloat = 0.0
let trailingTextBadgeInsets = UIEdgeInsets(top: 2.0 - UIScreenPixel, left: 5.0, bottom: 2.0 - UIScreenPixel, right: 5.0)
var trailingTextBadgeLayoutAndApply: (TextNodeLayout, () -> TextNode)?
if case let .peer(peerData) = item.content, let customMessageListData = peerData.customMessageListData, customMessageListData.commandPrefix != nil, let messageCount = customMessageListData.messageCount, messageCount > 1 {
let trailingText: String
//TODO:localize
trailingText = "+\(messageCount - 1) MORE"
let trailingAttributedText = NSAttributedString(string: trailingText, font: Font.regular(12.0), textColor: theme.messageTextColor)
let (layout, apply) = makeTrailingTextBadgeLayout(TextNodeLayoutArguments(attributedString: trailingAttributedText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: rawContentWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
trailingTextBadgeLayoutAndApply = (layout, apply)
textBottomRightCutout += layout.size.width + 4.0 + trailingTextBadgeInsets.left + trailingTextBadgeInsets.right
}
var textCutout: TextNodeCutout? var textCutout: TextNodeCutout?
if !textLeftCutout.isZero { if !textLeftCutout.isZero || !textBottomRightCutout.isZero {
textCutout = TextNodeCutout(topLeft: CGSize(width: textLeftCutout, height: 10.0), topRight: nil, bottomRight: nil) textCutout = TextNodeCutout(topLeft: textLeftCutout.isZero ? nil : CGSize(width: textLeftCutout, height: 10.0), topRight: nil, bottomRight: textBottomRightCutout.isZero ? nil : CGSize(width: textBottomRightCutout, height: 10.0))
} }
var textMaxWidth = rawContentWidth - badgeSize var textMaxWidth = rawContentWidth - badgeSize
@ -3552,6 +3572,19 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let _ = measureApply() let _ = measureApply()
let _ = dateApply() let _ = dateApply()
var currentTextSnapshotView: UIView?
if transition.isAnimated, let currentItem, currentItem.editing != item.editing, strongSelf.textNode.textNode.cachedLayout?.linesRects() != textLayout.linesRects() {
if let textSnapshotView = strongSelf.textNode.textNode.view.snapshotContentTree() {
textSnapshotView.layer.anchorPoint = CGPoint()
currentTextSnapshotView = textSnapshotView
strongSelf.textNode.textNode.view.superview?.insertSubview(textSnapshotView, aboveSubview: strongSelf.textNode.textNode.view)
textSnapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak textSnapshotView] _ in
textSnapshotView?.removeFromSuperview()
})
strongSelf.textNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18)
}
}
let _ = textApply(TextNodeWithEntities.Arguments( let _ = textApply(TextNodeWithEntities.Arguments(
context: item.context, context: item.context,
cache: item.interaction.animationCache, cache: item.interaction.animationCache,
@ -3572,7 +3605,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var dateFrame = CGRect(origin: CGPoint(x: contentRect.origin.x + contentRect.size.width - dateLayout.size.width, y: contentRect.origin.y + 2.0), size: dateLayout.size) var dateFrame = CGRect(origin: CGPoint(x: contentRect.origin.x + contentRect.size.width - dateLayout.size.width, y: contentRect.origin.y + 2.0), size: dateLayout.size)
if case let .peer(peerData) = item.content, let customMessageListData = peerData.customMessageListData, customMessageListData.messageCount != nil { if case let .peer(peerData) = item.content, let customMessageListData = peerData.customMessageListData, customMessageListData.messageCount != nil, customMessageListData.commandPrefix == nil {
dateFrame.origin.x -= 10.0 dateFrame.origin.x -= 10.0
let dateDisclosureIconView: UIImageView let dateDisclosureIconView: UIImageView
@ -3818,6 +3851,60 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
strongSelf.authorNode.assignParentNode(parentNode: nil) strongSelf.authorNode.assignParentNode(parentNode: nil)
} }
if let currentTextSnapshotView {
transition.updatePosition(layer: currentTextSnapshotView.layer, position: textNodeFrame.origin)
}
if let trailingTextBadgeLayoutAndApply {
let badgeSize = CGSize(width: trailingTextBadgeLayoutAndApply.0.size.width + trailingTextBadgeInsets.left + trailingTextBadgeInsets.right, height: trailingTextBadgeLayoutAndApply.0.size.height + trailingTextBadgeInsets.top + trailingTextBadgeInsets.bottom - UIScreenPixel)
var badgeFrame: CGRect
if textLayout.numberOfLines > 1 {
badgeFrame = CGRect(origin: CGPoint(x: textLayout.trailingLineWidth, y: textNodeFrame.height - 3.0 - badgeSize.height), size: badgeSize)
} else {
let firstLineFrame = textLayout.linesRects().first ?? CGRect(origin: CGPoint(), size: textNodeFrame.size)
badgeFrame = CGRect(origin: CGPoint(x: 0.0, y: firstLineFrame.height + 5.0), size: badgeSize)
}
if badgeFrame.origin.x + badgeFrame.width >= textNodeFrame.width - 2.0 - 10.0 {
badgeFrame.origin.x = textNodeFrame.width - 2.0 - badgeFrame.width
}
let trailingTextBadgeBackground: UIImageView
if let current = strongSelf.trailingTextBadgeBackground {
trailingTextBadgeBackground = current
} else {
trailingTextBadgeBackground = UIImageView(image: tagBackgroundImage)
strongSelf.trailingTextBadgeBackground = trailingTextBadgeBackground
strongSelf.textNode.textNode.view.addSubview(trailingTextBadgeBackground)
}
trailingTextBadgeBackground.tintColor = theme.pinnedItemBackgroundColor.mixedWith(theme.unreadBadgeInactiveBackgroundColor, alpha: 0.1)
trailingTextBadgeBackground.frame = badgeFrame
let trailingTextBadgeFrame = CGRect(origin: CGPoint(x: badgeFrame.minX + trailingTextBadgeInsets.left, y: badgeFrame.minY + trailingTextBadgeInsets.top), size: trailingTextBadgeLayoutAndApply.0.size)
let trailingTextBadgeNode = trailingTextBadgeLayoutAndApply.1()
if strongSelf.trailingTextBadgeNode !== trailingTextBadgeNode {
strongSelf.trailingTextBadgeNode?.removeFromSupernode()
strongSelf.trailingTextBadgeNode = trailingTextBadgeNode
strongSelf.textNode.textNode.addSubnode(trailingTextBadgeNode)
trailingTextBadgeNode.layer.anchorPoint = CGPoint()
}
trailingTextBadgeNode.frame = trailingTextBadgeFrame
} else {
if let trailingTextBadgeNode = strongSelf.trailingTextBadgeNode {
strongSelf.trailingTextBadgeNode = nil
trailingTextBadgeNode.removeFromSupernode()
}
if let trailingTextBadgeBackground = strongSelf.trailingTextBadgeBackground {
strongSelf.trailingTextBadgeBackground = nil
trailingTextBadgeBackground.removeFromSuperview()
}
}
if !itemTags.isEmpty { if !itemTags.isEmpty {
let itemTagListFrame = CGRect(origin: CGPoint(x: contentRect.minX, y: contentRect.maxY - 12.0), size: CGSize(width: contentRect.width, height: 20.0)) let itemTagListFrame = CGRect(origin: CGPoint(x: contentRect.minX, y: contentRect.maxY - 12.0), size: CGSize(width: contentRect.width, height: 20.0))
@ -4127,7 +4214,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} }
if case let .peer(peerData) = item.content, let customMessageListData = peerData.customMessageListData { if case let .peer(peerData) = item.content, let customMessageListData = peerData.customMessageListData {
if customMessageListData.messageCount != nil { if customMessageListData.hideSeparator {
strongSelf.separatorNode.isHidden = true strongSelf.separatorNode.isHidden = true
} }
} }

View File

@ -433,7 +433,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
}, },
requiresPremiumForMessaging: peerEntry.requiresPremiumForMessaging, requiresPremiumForMessaging: peerEntry.requiresPremiumForMessaging,
displayAsTopicList: peerEntry.displayAsTopicList, displayAsTopicList: peerEntry.displayAsTopicList,
tags: chatListItemTags(accountPeerId: context.account.peerId, peer: peer.chatMainPeer, isUnread: combinedReadState?.isUnread ?? false, isMuted: isRemovedFromTotalUnreadCount, isContact: isContact, hasUnseenMentions: hasUnseenMentions, chatListFilters: chatListFilters) tags: chatListItemTags(location: location, accountPeerId: context.account.peerId, peer: peer.chatMainPeer, isUnread: combinedReadState?.isUnread ?? false, isMuted: isRemovedFromTotalUnreadCount, isContact: isContact, hasUnseenMentions: hasUnseenMentions, chatListFilters: chatListFilters)
)), )),
editing: editing, editing: editing,
hasActiveRevealControls: hasActiveRevealControls, hasActiveRevealControls: hasActiveRevealControls,
@ -811,7 +811,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
}, },
requiresPremiumForMessaging: peerEntry.requiresPremiumForMessaging, requiresPremiumForMessaging: peerEntry.requiresPremiumForMessaging,
displayAsTopicList: peerEntry.displayAsTopicList, displayAsTopicList: peerEntry.displayAsTopicList,
tags: chatListItemTags(accountPeerId: context.account.peerId, peer: peer.chatMainPeer, isUnread: combinedReadState?.isUnread ?? false, isMuted: isRemovedFromTotalUnreadCount, isContact: isContact, hasUnseenMentions: hasUnseenMentions, chatListFilters: chatListFilters) tags: chatListItemTags(location: location, accountPeerId: context.account.peerId, peer: peer.chatMainPeer, isUnread: combinedReadState?.isUnread ?? false, isMuted: isRemovedFromTotalUnreadCount, isContact: isContact, hasUnseenMentions: hasUnseenMentions, chatListFilters: chatListFilters)
)), )),
editing: editing, editing: editing,
hasActiveRevealControls: hasActiveRevealControls, hasActiveRevealControls: hasActiveRevealControls,
@ -4206,7 +4206,11 @@ func hideChatListContacts(context: AccountContext) {
let _ = ApplicationSpecificNotice.setDisplayChatListContacts(accountManager: context.sharedContext.accountManager).startStandalone() let _ = ApplicationSpecificNotice.setDisplayChatListContacts(accountManager: context.sharedContext.accountManager).startStandalone()
} }
func chatListItemTags(accountPeerId: EnginePeer.Id, peer: EnginePeer?, isUnread: Bool, isMuted: Bool, isContact: Bool, hasUnseenMentions: Bool, chatListFilters: [ChatListFilter]?) -> [ChatListItemContent.Tag] { func chatListItemTags(location: ChatListControllerLocation, accountPeerId: EnginePeer.Id, peer: EnginePeer?, isUnread: Bool, isMuted: Bool, isContact: Bool, hasUnseenMentions: Bool, chatListFilters: [ChatListFilter]?) -> [ChatListItemContent.Tag] {
if case .chatList = location {
} else {
return []
}
guard let chatListFilters, !chatListFilters.isEmpty else { guard let chatListFilters, !chatListFilters.isEmpty else {
return [] return []
} }

View File

@ -1202,6 +1202,8 @@ public func canSendMessagesToChat(_ state: ChatPresentationInterfaceState) -> Bo
} else { } else {
return false return false
} }
} else if case .customChatContents = state.chatLocation {
return true
} else { } else {
return false return false
} }

View File

@ -816,6 +816,11 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
func setMessage(_ message: Message, displayInfo: Bool = true, translateToLanguage: String? = nil, peerIsCopyProtected: Bool = false) { func setMessage(_ message: Message, displayInfo: Bool = true, translateToLanguage: String? = nil, peerIsCopyProtected: Bool = false) {
self.currentMessage = message self.currentMessage = message
var displayInfo = displayInfo
if Namespaces.Message.allNonRegular.contains(message.id.namespace) {
displayInfo = false
}
var canDelete: Bool var canDelete: Bool
var canShare = !message.containsSecretMedia var canShare = !message.containsSecretMedia

View File

@ -285,6 +285,7 @@ private enum PreferencesKeyValues: Int32 {
case didCacheSavedMessageTagsPrefix = 34 case didCacheSavedMessageTagsPrefix = 34
case displaySavedChatsAsTopics = 35 case displaySavedChatsAsTopics = 35
case shortcutMessages = 37 case shortcutMessages = 37
case timezoneList = 38
} }
public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey { public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey {
@ -474,6 +475,12 @@ public struct PreferencesKeys {
key.setInt32(0, value: PreferencesKeyValues.shortcutMessages.rawValue) key.setInt32(0, value: PreferencesKeyValues.shortcutMessages.rawValue)
return key return key
} }
public static func timezoneList() -> ValueBoxKey {
let key = ValueBoxKey(length: 4)
key.setInt32(0, value: PreferencesKeyValues.timezoneList.rawValue)
return key
}
} }
private enum SharedDataKeyValues: Int32 { private enum SharedDataKeyValues: Int32 {

View File

@ -185,5 +185,13 @@ public extension TelegramEngine {
public func sendMessageShortcut(peerId: EnginePeer.Id, id: Int32) { public func sendMessageShortcut(peerId: EnginePeer.Id, id: Int32) {
let _ = _internal_sendMessageShortcut(account: self.account, peerId: peerId, id: id).startStandalone() let _ = _internal_sendMessageShortcut(account: self.account, peerId: peerId, id: id).startStandalone()
} }
public func cachedTimeZoneList() -> Signal<TimeZoneList?, NoError> {
return _internal_cachedTimeZoneList(account: self.account)
}
public func keepCachedTimeZoneListUpdated() -> Signal<Never, NoError> {
return _internal_keepCachedTimeZoneListUpdated(account: self.account)
}
} }
} }

View File

@ -229,7 +229,7 @@ func _internal_shortcutMessageList(account: Account) -> Signal<ShortcutMessageLi
historyViewKeys[shortcut.id] = historyViewKey historyViewKeys[shortcut.id] = historyViewKey
keys.append(historyViewKey) keys.append(historyViewKey)
let summaryKey: PostboxViewKey = .historyTagSummaryView(tag: [], peerId: account.peerId, threadId: Int64(shortcut.id), namespace: Namespaces.Message.ScheduledCloud, customTag: nil) let summaryKey: PostboxViewKey = .historyTagSummaryView(tag: [], peerId: account.peerId, threadId: Int64(shortcut.id), namespace: Namespaces.Message.QuickReplyCloud, customTag: nil)
summaryKeys[shortcut.id] = summaryKey summaryKeys[shortcut.id] = summaryKey
keys.append(summaryKey) keys.append(summaryKey)
} }

View File

@ -0,0 +1,106 @@
import Foundation
import Postbox
import SwiftSignalKit
import TelegramApi
import MtProtoKit
public final class TimeZoneList: Codable, Equatable {
public final class Item: Codable, Equatable {
public let id: String
public let title: String
public let utcOffset: Int32
public init(id: String, title: String, utcOffset: Int32) {
self.id = id
self.title = title
self.utcOffset = utcOffset
}
public static func ==(lhs: Item, rhs: Item) -> Bool {
if lhs === rhs {
return true
}
if lhs.id != rhs.id {
return false
}
if lhs.title != rhs.title {
return false
}
if lhs.utcOffset != rhs.utcOffset {
return false
}
return true
}
}
public let items: [Item]
public let hashValue: Int32
public init(items: [Item], hashValue: Int32) {
self.items = items
self.hashValue = hashValue
}
public static func ==(lhs: TimeZoneList, rhs: TimeZoneList) -> Bool {
if lhs === rhs {
return true
}
if lhs.items != rhs.items {
return false
}
if lhs.hashValue != rhs.hashValue {
return false
}
return true
}
}
func _internal_cachedTimeZoneList(account: Account) -> Signal<TimeZoneList?, NoError> {
let viewKey: PostboxViewKey = .preferences(keys: Set([PreferencesKeys.timezoneList()]))
return account.postbox.combinedView(keys: [viewKey])
|> map { views -> TimeZoneList? in
guard let view = views.views[viewKey] as? PreferencesView else {
return nil
}
guard let value = view.values[PreferencesKeys.timezoneList()]?.get(TimeZoneList.self) else {
return nil
}
return value
}
}
func _internal_keepCachedTimeZoneListUpdated(account: Account) -> Signal<Never, NoError> {
let updateSignal = _internal_cachedTimeZoneList(account: account)
|> take(1)
|> mapToSignal { list -> Signal<Never, NoError> in
return account.network.request(Api.functions.help.getTimezonesList(hash: list?.hashValue ?? 0))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.help.TimezonesList?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<Never, NoError> in
guard let result else {
return .complete()
}
return account.postbox.transaction { transaction in
switch result {
case let .timezonesList(timezones, hash):
var items: [TimeZoneList.Item] = []
for item in timezones {
switch item {
case let .timezone(id, name, utcOffset):
items.append(TimeZoneList.Item(id: id, title: name, utcOffset: utcOffset))
}
}
transaction.setPreferencesEntry(key: PreferencesKeys.timezoneList(), value: PreferencesEntry(TimeZoneList(items: items, hashValue: hash)))
case .timezonesListNotModified:
break
}
}
|> ignoreValues
}
}
return updateSignal
}

View File

@ -267,7 +267,9 @@ public final class ChatListHeaderComponent: Component {
} }
func update(title: String, theme: PresentationTheme, availableSize: CGSize, transition: Transition) -> CGSize { func update(title: String, theme: PresentationTheme, availableSize: CGSize, transition: Transition) -> CGSize {
self.titleView.attributedText = NSAttributedString(string: title, font: Font.regular(17.0), textColor: theme.rootController.navigationBar.accentTextColor) let titleText = NSAttributedString(string: title, font: Font.regular(17.0), textColor: theme.rootController.navigationBar.accentTextColor)
let titleTextUpdated = self.titleView.attributedText != titleText
self.titleView.attributedText = titleText
let titleSize = self.titleView.updateLayout(CGSize(width: 100.0, height: 44.0)) let titleSize = self.titleView.updateLayout(CGSize(width: 100.0, height: 44.0))
self.accessibilityLabel = title self.accessibilityLabel = title
@ -287,7 +289,12 @@ public final class ChatListHeaderComponent: Component {
transition.setPosition(view: self.arrowView, position: arrowFrame.center) transition.setPosition(view: self.arrowView, position: arrowFrame.center)
transition.setBounds(view: self.arrowView, bounds: CGRect(origin: CGPoint(), size: arrowFrame.size)) transition.setBounds(view: self.arrowView, bounds: CGRect(origin: CGPoint(), size: arrowFrame.size))
transition.setFrame(view: self.titleView, frame: CGRect(origin: CGPoint(x: iconOffset - 3.0 + arrowSize.width + iconSpacing, y: floor((availableSize.height - titleSize.height) / 2.0)), size: titleSize)) let titleFrame = CGRect(origin: CGPoint(x: iconOffset - 3.0 + arrowSize.width + iconSpacing, y: floor((availableSize.height - titleSize.height) / 2.0)), size: titleSize)
if titleTextUpdated {
self.titleView.frame = titleFrame
} else {
transition.setFrame(view: self.titleView, frame: titleFrame)
}
return CGSize(width: iconOffset + arrowSize.width + iconSpacing + titleSize.width, height: availableSize.height) return CGSize(width: iconOffset + arrowSize.width + iconSpacing + titleSize.width, height: availableSize.height)
} }
@ -479,7 +486,9 @@ public final class ChatListHeaderComponent: Component {
transition.setPosition(view: self.titleScaleContainer, position: CGPoint(x: size.width * 0.5, y: size.height * 0.5)) transition.setPosition(view: self.titleScaleContainer, position: CGPoint(x: size.width * 0.5, y: size.height * 0.5))
transition.setBounds(view: self.titleScaleContainer, bounds: CGRect(origin: self.titleScaleContainer.bounds.origin, size: size)) transition.setBounds(view: self.titleScaleContainer, bounds: CGRect(origin: self.titleScaleContainer.bounds.origin, size: size))
self.titleTextView.attributedText = NSAttributedString(string: content.title, font: Font.semibold(17.0), textColor: theme.rootController.navigationBar.primaryTextColor) let titleText = NSAttributedString(string: content.title, font: Font.semibold(17.0), textColor: theme.rootController.navigationBar.primaryTextColor)
let titleTextUpdated = self.titleTextView.attributedText != titleText
self.titleTextView.attributedText = titleText
let buttonSpacing: CGFloat = 8.0 let buttonSpacing: CGFloat = 8.0
@ -616,7 +625,11 @@ public final class ChatListHeaderComponent: Component {
let titleTextSize = self.titleTextView.updateLayout(CGSize(width: remainingWidth, height: size.height)) let titleTextSize = self.titleTextView.updateLayout(CGSize(width: remainingWidth, height: size.height))
let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleTextSize.width) / 2.0) + sideContentWidth, y: floor((size.height - titleTextSize.height) / 2.0)), size: titleTextSize) let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleTextSize.width) / 2.0) + sideContentWidth, y: floor((size.height - titleTextSize.height) / 2.0)), size: titleTextSize)
if titleTextUpdated {
self.titleTextView.frame = titleFrame
} else {
transition.setFrame(view: self.titleTextView, frame: titleFrame) transition.setFrame(view: self.titleTextView, frame: titleFrame)
}
if let titleComponent = content.titleComponent { if let titleComponent = content.titleComponent {
var titleContentTransition = transition var titleContentTransition = transition

View File

@ -227,7 +227,12 @@ public final class PlainButtonComponent: Component {
} }
let contentFrame = CGRect(origin: CGPoint(x: component.contentInsets.left + floor((size.width - component.contentInsets.left - component.contentInsets.right - contentSize.width) * 0.5), y: component.contentInsets.top + floor((size.height - component.contentInsets.top - component.contentInsets.bottom - contentSize.height) * 0.5)), size: contentSize) let contentFrame = CGRect(origin: CGPoint(x: component.contentInsets.left + floor((size.width - component.contentInsets.left - component.contentInsets.right - contentSize.width) * 0.5), y: component.contentInsets.top + floor((size.height - component.contentInsets.top - component.contentInsets.bottom - contentSize.height) * 0.5)), size: contentSize)
contentTransition.setPosition(view: contentView, position: CGPoint(x: contentFrame.minX + contentFrame.width * contentView.layer.anchorPoint.x, y: contentFrame.minY + contentFrame.height * contentView.layer.anchorPoint.y)) let contentPosition = CGPoint(x: contentFrame.minX + contentFrame.width * contentView.layer.anchorPoint.x, y: contentFrame.minY + contentFrame.height * contentView.layer.anchorPoint.y)
if !component.animateContents && (abs(contentView.center.x - contentPosition.x) <= 2.0 && abs(contentView.center.y - contentPosition.y) <= 2.0){
contentView.center = contentPosition
} else {
contentTransition.setPosition(view: contentView, position: contentPosition)
}
if component.animateContents { if component.animateContents {
contentTransition.setBounds(view: contentView, bounds: CGRect(origin: CGPoint(), size: contentFrame.size)) contentTransition.setBounds(view: contentView, bounds: CGRect(origin: CGPoint(), size: contentFrame.size))

View File

@ -238,7 +238,8 @@ final class GreetingMessageListItemComponent: Component {
customMessageListData: ChatListItemContent.CustomMessageListData( customMessageListData: ChatListItemContent.CustomMessageListData(
commandPrefix: nil, commandPrefix: nil,
searchQuery: nil, searchQuery: nil,
messageCount: component.count messageCount: component.count,
hideSeparator: true
) )
)), )),
editing: false, editing: false,

View File

@ -109,7 +109,7 @@ final class QuickReplyEmptyStateComponent: Component {
transition: .immediate, transition: .immediate,
component: AnyComponent(LottieComponent( component: AnyComponent(LottieComponent(
content: LottieComponent.AppBundleContent(name: "WriteEmoji"), content: LottieComponent.AppBundleContent(name: "WriteEmoji"),
loop: false loop: true
)), )),
environment: {}, environment: {},
containerSize: CGSize(width: 120.0, height: 120.0) containerSize: CGSize(width: 120.0, height: 120.0)

View File

@ -250,7 +250,8 @@ final class QuickReplySetupScreenComponent: Component {
customMessageListData: ChatListItemContent.CustomMessageListData( customMessageListData: ChatListItemContent.CustomMessageListData(
commandPrefix: "/\(item.shortcut)", commandPrefix: "/\(item.shortcut)",
searchQuery: nil, searchQuery: nil,
messageCount: nil messageCount: item.totalCount,
hideSeparator: false
) )
)), )),
editing: isEditing, editing: isEditing,
@ -744,13 +745,14 @@ final class QuickReplySetupScreenComponent: Component {
tabsNodeIsSearch: false, tabsNodeIsSearch: false,
accessoryPanelContainer: nil, accessoryPanelContainer: nil,
accessoryPanelContainerHeight: 0.0, accessoryPanelContainerHeight: 0.0,
activateSearch: { [weak self] searchContentNode in activateSearch: { [weak self] _ in
guard let self else { guard let self else {
return return
} }
self.isSearchDisplayControllerActive = true let _ = self
self.state?.updated(transition: .spring(duration: 0.4)) //self.isSearchDisplayControllerActive = true
//self.state?.updated(transition: .spring(duration: 0.4))
}, },
openStatusSetup: { _ in openStatusSetup: { _ in
}, },

View File

@ -82,11 +82,14 @@ final class BusinessHoursSetupScreenComponent: Component {
} }
var timezoneId: String var timezoneId: String
var days: [Day] private(set) var days: [Day]
private(set) var intersectingDays = Set<Int>()
init(timezoneId: String, days: [Day]) { init(timezoneId: String, days: [Day]) {
self.timezoneId = timezoneId self.timezoneId = timezoneId
self.days = days self.days = days
self.validate()
} }
init(businessHours: TelegramBusinessHours) { init(businessHours: TelegramBusinessHours) {
@ -107,6 +110,19 @@ final class BusinessHoursSetupScreenComponent: Component {
}) })
} }
} }
self.validate()
}
mutating func validate() {
self.intersectingDays.removeAll()
}
mutating func update(days: [Day]) {
self.days = days
self.validate()
} }
func asBusinessHours() throws -> TelegramBusinessHours { func asBusinessHours() throws -> TelegramBusinessHours {
@ -165,6 +181,10 @@ final class BusinessHoursSetupScreenComponent: Component {
private var showHours: Bool = false private var showHours: Bool = false
private var daysState = DaysState(timezoneId: "", days: []) private var daysState = DaysState(timezoneId: "", days: [])
private var timeZoneList: TimeZoneList?
private var timezonesDisposable: Disposable?
private var keepTimezonesUpdatedDisposable: Disposable?
override init(frame: CGRect) { override init(frame: CGRect) {
self.scrollView = ScrollView() self.scrollView = ScrollView()
self.scrollView.showsVerticalScrollIndicator = true self.scrollView.showsVerticalScrollIndicator = true
@ -191,6 +211,8 @@ final class BusinessHoursSetupScreenComponent: Component {
} }
deinit { deinit {
self.timezonesDisposable?.dispose()
self.keepTimezonesUpdatedDisposable?.dispose()
} }
func scrollToTop() { func scrollToTop() {
@ -210,6 +232,21 @@ final class BusinessHoursSetupScreenComponent: Component {
} catch let error { } catch let error {
let _ = error let _ = error
//TODO:localize //TODO:localize
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
//TODO:localize
self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: "Business hours are intersecting. Reset?", actions: [
TextAlertAction(type: .genericAction, title: "Cancel", action: {
}),
TextAlertAction(type: .defaultAction, title: "Reset", action: { [weak self] in
guard let self else {
return
}
let _ = self
complete()
})
]), in: .window(.root))
return false return false
} }
} else { } else {
@ -271,10 +308,20 @@ final class BusinessHoursSetupScreenComponent: Component {
} else { } else {
self.showHours = false self.showHours = false
self.daysState.timezoneId = TimeZone.current.identifier self.daysState.timezoneId = TimeZone.current.identifier
self.daysState.days = (0 ..< 7).map { _ in self.daysState.update(days: (0 ..< 7).map { _ in
return Day(ranges: []) return Day(ranges: [])
})
} }
self.timezonesDisposable = (component.context.engine.accountData.cachedTimeZoneList()
|> deliverOnMainQueue).start(next: { [weak self] timeZoneList in
guard let self else {
return
} }
self.timeZoneList = timeZoneList
self.state?.updated(transition: .immediate)
})
self.keepTimezonesUpdatedDisposable = component.context.engine.accountData.keepCachedTimeZoneListUpdated().startStrict()
} }
let environment = environment[EnvironmentType.self].value let environment = environment[EnvironmentType.self].value
@ -508,11 +555,13 @@ final class BusinessHoursSetupScreenComponent: Component {
return return
} }
if dayIndex < self.daysState.days.count { if dayIndex < self.daysState.days.count {
if self.daysState.days[dayIndex].ranges == nil { var days = self.daysState.days
self.daysState.days[dayIndex].ranges = [] if days[dayIndex].ranges == nil {
days[dayIndex].ranges = []
} else { } else {
self.daysState.days[dayIndex].ranges = nil days[dayIndex].ranges = nil
} }
self.daysState.update(days: days)
} }
self.state?.updated(transition: .immediate) self.state?.updated(transition: .immediate)
})), })),
@ -529,7 +578,9 @@ final class BusinessHoursSetupScreenComponent: Component {
return return
} }
if self.daysState.days[dayIndex] != day { if self.daysState.days[dayIndex] != day {
self.daysState.days[dayIndex] = day var days = self.daysState.days
days[dayIndex] = day
self.daysState.update(days: days)
self.state?.updated(transition: .immediate) self.state?.updated(transition: .immediate)
} }
} }
@ -569,6 +620,18 @@ final class BusinessHoursSetupScreenComponent: Component {
daysContentHeight += daysSectionSize.height daysContentHeight += daysSectionSize.height
daysContentHeight += sectionSpacing daysContentHeight += sectionSpacing
let timezoneValueText: String
if let timeZoneList = self.timeZoneList {
if let item = timeZoneList.items.first(where: { $0.id == self.daysState.timezoneId }) {
timezoneValueText = item.title
} else {
timezoneValueText = TimeZone(identifier: self.daysState.timezoneId)?.localizedName(for: .shortStandard, locale: Locale.current) ?? " "
}
} else {
//TODO:localize
timezoneValueText = "Loading..."
}
let timezoneSectionSize = self.timezoneSection.update( let timezoneSectionSize = self.timezoneSection.update(
transition: transition, transition: transition,
component: AnyComponent(ListSectionComponent( component: AnyComponent(ListSectionComponent(
@ -588,7 +651,7 @@ final class BusinessHoursSetupScreenComponent: Component {
)), )),
icon: ListActionItemComponent.Icon(component: AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent( icon: ListActionItemComponent.Icon(component: AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString( text: .plain(NSAttributedString(
string: TimeZone(identifier: self.daysState.timezoneId)?.localizedName(for: .shortStandard, locale: Locale.current) ?? self.daysState.timezoneId, string: timezoneValueText,
font: Font.regular(presentationData.listsFontSize.baseDisplaySize), font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
textColor: environment.theme.list.itemSecondaryTextColor textColor: environment.theme.list.itemSecondaryTextColor
)), )),

View File

@ -58,8 +58,7 @@ private func preparedLanguageListSearchContainerTransition(presentationData: Pre
} }
private final class TimezoneListSearchContainerNode: SearchDisplayControllerContentNode { private final class TimezoneListSearchContainerNode: SearchDisplayControllerContentNode {
private let timezoneData: TimezoneData private let timeZoneList: TimeZoneList
private let dimNode: ASDisplayNode private let dimNode: ASDisplayNode
private let listNode: ListView private let listNode: ListView
@ -78,8 +77,8 @@ private final class TimezoneListSearchContainerNode: SearchDisplayControllerCont
return true return true
} }
init(context: AccountContext, timezoneData: TimezoneData, action: @escaping (String) -> Void) { init(context: AccountContext, timeZoneList: TimeZoneList, action: @escaping (String) -> Void) {
self.timezoneData = timezoneData self.timeZoneList = timeZoneList
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.presentationData = presentationData self.presentationData = presentationData
@ -102,16 +101,18 @@ private final class TimezoneListSearchContainerNode: SearchDisplayControllerCont
self.addSubnode(self.dimNode) self.addSubnode(self.dimNode)
self.addSubnode(self.listNode) self.addSubnode(self.listNode)
let querySplitCharacterSet: CharacterSet = CharacterSet(charactersIn: " /.+")
let foundItems = self.searchQuery.get() let foundItems = self.searchQuery.get()
|> mapToSignal { query -> Signal<[TimezoneData.Item]?, NoError> in |> mapToSignal { query -> Signal<[TimeZoneList.Item]?, NoError> in
if let query, !query.isEmpty { if let query, !query.isEmpty {
let query = query.lowercased() let query = query.lowercased()
return .single(timezoneData.items.filter { item in return .single(timeZoneList.items.filter { item in
if item.id.lowercased().hasPrefix(query) { if item.id.lowercased().hasPrefix(query) {
return true return true
} }
if item.title.lowercased().split(separator: " ").contains(where: { $0.hasPrefix(query) }) { if item.title.lowercased().components(separatedBy: querySplitCharacterSet).contains(where: { $0.hasPrefix(query) }) {
return true return true
} }
@ -132,7 +133,7 @@ private final class TimezoneListSearchContainerNode: SearchDisplayControllerCont
for item in items { for item in items {
entries.append(TimezoneListEntry( entries.append(TimezoneListEntry(
id: item.id, id: item.id,
offset: item.offset, offset: Int(item.utcOffset),
title: item.title title: item.title
)) ))
} }
@ -301,7 +302,7 @@ final class TimezoneSelectionScreenNode: ViewControllerTracingNode {
private let requestDeactivateSearch: () -> Void private let requestDeactivateSearch: () -> Void
private let present: (ViewController, Any?) -> Void private let present: (ViewController, Any?) -> Void
private let push: (ViewController) -> Void private let push: (ViewController) -> Void
private let timezoneData: TimezoneData private var timeZoneList: TimeZoneList?
private var didSetReady = false private var didSetReady = false
let _ready = ValuePromise<Bool>() let _ready = ValuePromise<Bool>()
@ -331,29 +332,33 @@ final class TimezoneSelectionScreenNode: ViewControllerTracingNode {
return presentationData.strings.VoiceOver_ScrollStatus(row, count).string return presentationData.strings.VoiceOver_ScrollStatus(row, count).string
} }
let timezoneData = TimezoneData()
self.timezoneData = timezoneData
super.init() super.init()
self.backgroundColor = presentationData.theme.list.plainBackgroundColor self.backgroundColor = presentationData.theme.list.plainBackgroundColor
self.addSubnode(self.listNode) self.addSubnode(self.listNode)
let previousEntriesHolder = Atomic<([TimezoneListEntry], PresentationTheme, PresentationStrings)?>(value: nil) let previousEntriesHolder = Atomic<([TimezoneListEntry], PresentationTheme, PresentationStrings)?>(value: nil)
self.listDisposable = (self.presentationDataValue.get() self.listDisposable = (combineLatest(queue: .mainQueue(),
|> deliverOnMainQueue).start(next: { [weak self] presentationData in self.presentationDataValue.get(),
context.engine.accountData.cachedTimeZoneList()
)
|> deliverOnMainQueue).start(next: { [weak self] presentationData, timeZoneList in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
strongSelf.timeZoneList = timeZoneList
var entries: [TimezoneListEntry] = [] var entries: [TimezoneListEntry] = []
for item in timezoneData.items { if let timeZoneList {
for item in timeZoneList.items {
entries.append(TimezoneListEntry( entries.append(TimezoneListEntry(
id: item.id, id: item.id,
offset: item.offset, offset: Int(item.utcOffset),
title: item.title title: item.title
)) ))
} }
}
entries.sort() entries.sort()
let previousEntriesAndPresentationData = previousEntriesHolder.swap((entries, presentationData.theme, presentationData.strings)) let previousEntriesAndPresentationData = previousEntriesHolder.swap((entries, presentationData.theme, presentationData.strings))
@ -447,8 +452,11 @@ final class TimezoneSelectionScreenNode: ViewControllerTracingNode {
guard let (containerLayout, navigationBarHeight) = self.containerLayout, self.searchDisplayController == nil else { guard let (containerLayout, navigationBarHeight) = self.containerLayout, self.searchDisplayController == nil else {
return return
} }
guard let timeZoneList = self.timeZoneList else {
return
}
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: TimezoneListSearchContainerNode(context: self.context, timezoneData: self.timezoneData, action: self.action), inline: true, cancel: { [weak self] in self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: TimezoneListSearchContainerNode(context: self.context, timeZoneList: timeZoneList, action: self.action), inline: true, cancel: { [weak self] in
self?.requestDeactivateSearch() self?.requestDeactivateSearch()
}) })

View File

@ -104,6 +104,9 @@ extension ChatControllerImpl {
if case let .peer(peerId) = self.chatLocation, messageLocation.peerId == peerId, !isPinnedMessages, !isScheduledMessages { if case let .peer(peerId) = self.chatLocation, messageLocation.peerId == peerId, !isPinnedMessages, !isScheduledMessages {
forceInCurrentChat = true forceInCurrentChat = true
} }
if case .customChatContents = self.chatLocation {
forceInCurrentChat = true
}
if isPinnedMessages, let messageId = messageLocation.messageId { if isPinnedMessages, let messageId = messageLocation.messageId {
let _ = (combineLatest( let _ = (combineLatest(
@ -205,6 +208,7 @@ extension ChatControllerImpl {
if case let .id(_, params) = messageLocation { if case let .id(_, params) = messageLocation {
quote = params.quote.flatMap { quote in (string: quote.string, offset: quote.offset) } quote = params.quote.flatMap { quote in (string: quote.string, offset: quote.offset) }
} }
self.chatDisplayNode.historyNode.scrollToMessage(from: scrollFromIndex, to: message.index, animated: animated, quote: quote, scrollPosition: scrollPosition) self.chatDisplayNode.historyNode.scrollToMessage(from: scrollFromIndex, to: message.index, animated: animated, quote: quote, scrollPosition: scrollPosition)
if delayCompletion { if delayCompletion {

View File

@ -1883,7 +1883,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
overlayNavigationBar.updateLayout(size: barFrame.size, transition: transition) overlayNavigationBar.updateLayout(size: barFrame.size, transition: transition)
} }
var listInsets = UIEdgeInsets(top: containerInsets.bottom + contentBottomInset, left: containerInsets.right, bottom: containerInsets.top, right: containerInsets.left) var listInsets = UIEdgeInsets(top: containerInsets.bottom + contentBottomInset, left: containerInsets.right, bottom: containerInsets.top + 6.0, right: containerInsets.left)
let listScrollIndicatorInsets = UIEdgeInsets(top: containerInsets.bottom + inputPanelsHeight, left: containerInsets.right, bottom: containerInsets.top, right: containerInsets.left) let listScrollIndicatorInsets = UIEdgeInsets(top: containerInsets.bottom + inputPanelsHeight, left: containerInsets.right, bottom: containerInsets.top, right: containerInsets.left)
var childContentInsets: UIEdgeInsets = containerInsets var childContentInsets: UIEdgeInsets = containerInsets

View File

@ -546,12 +546,12 @@ func chatHistoryEntriesForView(
let message = Message( let message = Message(
stableId: UInt32.max - 1001 - UInt32(i), stableId: UInt32.max - 1001 - UInt32(i),
stableVersion: 0, stableVersion: 0,
id: MessageId(peerId: context.account.peerId, namespace: Namespaces.Message.Local, id: 123 - Int32(i)), id: MessageId(peerId: context.account.peerId, namespace: Namespaces.Message.Local, id: Int32.max - 100 - Int32(i)),
globallyUniqueId: nil, globallyUniqueId: nil,
groupingKey: nil, groupingKey: nil,
groupInfo: nil, groupInfo: nil,
threadId: nil, threadId: nil,
timestamp: Int32(i), timestamp: -Int32(i),
flags: [.Incoming], flags: [.Incoming],
tags: [], tags: [],
globalTags: [], globalTags: [],

View File

@ -1283,10 +1283,15 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
} }
return true return true
}) })
|> mapToSignal { _ in |> mapToSignal { location, _ -> Signal<((MessageHistoryView, ViewUpdateType), ChatHistoryLocationInput?), NoError> in
return historyView return historyView
|> map { historyView in
return (historyView, location)
} }
|> map { view, update in }
|> map { viewAndUpdate, location in
let (view, update) = viewAndUpdate
let version = currentViewVersion.modify({ value in let version = currentViewVersion.modify({ value in
if let value = value { if let value = value {
return value + 1 return value + 1
@ -1295,11 +1300,21 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
} }
})! })!
var scrollPositionValue: ChatHistoryViewScrollPosition?
if let location {
switch location.content {
case let .Scroll(subject, _, _, scrollPosition, animated, highlight):
scrollPositionValue = .index(subject: subject, position: scrollPosition, directionHint: .Up, animated: animated, highlight: highlight, displayLink: false)
default:
break
}
}
return ( return (
ChatHistoryViewUpdate.HistoryView( ChatHistoryViewUpdate.HistoryView(
view: view, view: view,
type: .Generic(type: update), type: .Generic(type: update),
scrollPosition: nil, scrollPosition: scrollPositionValue,
flashIndicators: false, flashIndicators: false,
originalScrollPosition: nil, originalScrollPosition: nil,
initialData: ChatHistoryCombinedInitialData( initialData: ChatHistoryCombinedInitialData(
@ -1309,10 +1324,10 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
cachedDataMessages: nil, cachedDataMessages: nil,
readStateData: nil readStateData: nil
), ),
id: 0 id: location?.id ?? 0
), ),
version, version,
nil, location,
nil nil
) )
} }

View File

@ -76,13 +76,22 @@ func accessoryPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceS
replyPanelNode.interfaceInteraction = interfaceInteraction replyPanelNode.interfaceInteraction = interfaceInteraction
replyPanelNode.updateThemeAndStrings(theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings) replyPanelNode.updateThemeAndStrings(theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings)
return replyPanelNode return replyPanelNode
} else if let peerId = chatPresentationInterfaceState.chatLocation.peerId { } else {
let panelNode = ReplyAccessoryPanelNode(context: context, chatPeerId: peerId, messageId: replyMessageSubject.messageId, quote: replyMessageSubject.quote, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings, nameDisplayOrder: chatPresentationInterfaceState.nameDisplayOrder, dateTimeFormat: chatPresentationInterfaceState.dateTimeFormat, animationCache: chatControllerInteraction?.presentationContext.animationCache, animationRenderer: chatControllerInteraction?.presentationContext.animationRenderer) var chatPeerId: EnginePeer.Id?
if let peerId = chatPresentationInterfaceState.chatLocation.peerId {
chatPeerId = peerId
} else if case .customChatContents = chatPresentationInterfaceState.chatLocation {
chatPeerId = context.account.peerId
}
if let chatPeerId {
let panelNode = ReplyAccessoryPanelNode(context: context, chatPeerId: chatPeerId, messageId: replyMessageSubject.messageId, quote: replyMessageSubject.quote, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings, nameDisplayOrder: chatPresentationInterfaceState.nameDisplayOrder, dateTimeFormat: chatPresentationInterfaceState.dateTimeFormat, animationCache: chatControllerInteraction?.presentationContext.animationCache, animationRenderer: chatControllerInteraction?.presentationContext.animationRenderer)
panelNode.interfaceInteraction = interfaceInteraction panelNode.interfaceInteraction = interfaceInteraction
return panelNode return panelNode
} else { } else {
return nil return nil
} }
}
} else { } else {
return nil return nil
} }

View File

@ -271,6 +271,10 @@ private func canViewReadStats(message: Message, participantCount: Int?, isMessag
} }
func canReplyInChat(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, accountPeerId: PeerId) -> Bool { func canReplyInChat(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, accountPeerId: PeerId) -> Bool {
if case .customChatContents = chatPresentationInterfaceState.chatLocation {
return true
}
guard let peer = chatPresentationInterfaceState.renderedPeer?.peer else { guard let peer = chatPresentationInterfaceState.renderedPeer?.peer else {
return false return false
} }
@ -338,7 +342,7 @@ func canReplyInChat(_ chatPresentationInterfaceState: ChatPresentationInterfaceS
case .replyThread: case .replyThread:
canReply = true canReply = true
case .customChatContents: case .customChatContents:
canReply = false canReply = true
} }
return canReply return canReply
} }

View File

@ -220,7 +220,8 @@ private struct CommandChatInputContextPanelEntry: Comparable, Identifiable {
customMessageListData: ChatListItemContent.CustomMessageListData( customMessageListData: ChatListItemContent.CustomMessageListData(
commandPrefix: "/\(shortcut.shortcut)", commandPrefix: "/\(shortcut.shortcut)",
searchQuery: command.searchQuery.flatMap { "/\($0)"}, searchQuery: command.searchQuery.flatMap { "/\($0)"},
messageCount: nil messageCount: shortcut.totalCount,
hideSeparator: false
) )
)), )),
editing: false, editing: false,