mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
General UI bug fixes
This commit is contained in:
parent
9c09a61e47
commit
3b93037be9
@ -373,6 +373,17 @@ public enum ChatLocation: Equatable {
|
||||
case feed(id: Int32)
|
||||
}
|
||||
|
||||
public extension ChatLocation {
|
||||
var normalized: ChatLocation {
|
||||
switch self {
|
||||
case .peer, .feed:
|
||||
return self
|
||||
case let .replyThread(message):
|
||||
return .replyThread(message: message.normalized)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum ChatControllerActivateInput {
|
||||
case text
|
||||
case entityInput
|
||||
|
@ -1254,7 +1254,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
chatController.canReadHistory.set(false)
|
||||
source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController))
|
||||
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: source, items: chatForumTopicMenuItems(context: strongSelf.context, peerId: peer.peerId, threadId: threadId, isPinned: nil, isClosed: nil, chatListController: strongSelf, joined: joined, canSelect: true) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: source, items: chatForumTopicMenuItems(context: strongSelf.context, peerId: peer.peerId, threadId: threadId, isPinned: nil, isClosed: nil, chatListController: strongSelf, joined: joined, canSelect: false) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
|
||||
strongSelf.presentInGlobalOverlay(contextController)
|
||||
} else {
|
||||
let chatListController = ChatListControllerImpl(context: strongSelf.context, location: .forum(peerId: channel.id), controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false)
|
||||
@ -1302,6 +1302,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
return
|
||||
}
|
||||
|
||||
if case let .channel(channel) = peer, channel.flags.contains(.isForum) {
|
||||
let chatListController = ChatListControllerImpl(context: strongSelf.context, location: .forum(peerId: channel.id), controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false)
|
||||
chatListController.navigationPresentation = .master
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
|
||||
strongSelf.presentInGlobalOverlay(contextController)
|
||||
} else {
|
||||
let contextContentSource: ContextContentSource
|
||||
if peer.id.namespace == Namespaces.Peer.SecretChat, let node = node.subnodes?.first as? ContextExtractedContentContainingNode {
|
||||
contextContentSource = .extracted(ChatListHeaderBarContextExtractedContentSource(controller: strongSelf, sourceNode: node, keepInPlace: false))
|
||||
@ -1318,6 +1324,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: contextContentSource, items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
|
||||
strongSelf.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
}
|
||||
|
||||
self.tabContainerNode.tabSelected = { [weak self] id, isDisabled in
|
||||
guard let strongSelf = self else {
|
||||
@ -2048,8 +2055,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
|
||||
let navigationBarHeight = self.navigationBar?.frame.maxY ?? 0.0
|
||||
|
||||
transition.updateAlpha(node: self.tabContainerNode, alpha: self.chatListDisplayNode.inlineStackContainerTransitionFraction * 0.5 + (1.0 - self.chatListDisplayNode.inlineStackContainerTransitionFraction) * 1.0)
|
||||
self.tabContainerNode.isUserInteractionEnabled = self.chatListDisplayNode.inlineStackContainerNode == nil
|
||||
//transition.updateAlpha(node: self.tabContainerNode, alpha: self.chatListDisplayNode.inlineStackContainerTransitionFraction * 0.5 + (1.0 - self.chatListDisplayNode.inlineStackContainerTransitionFraction) * 1.0)
|
||||
//self.tabContainerNode.isUserInteractionEnabled = self.chatListDisplayNode.inlineStackContainerNode == nil
|
||||
|
||||
transition.updateFrame(node: self.tabContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight - self.additionalNavigationBarHeight - 46.0 + tabContainerOffset), size: CGSize(width: layout.size.width, height: 46.0)))
|
||||
self.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.mainContainerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing, canReorderAllChats: self.isPremium, filtersLimit: self.tabContainerData?.2, transitionFraction: self.chatListDisplayNode.effectiveContainerNode.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring))
|
||||
@ -4689,12 +4696,6 @@ private final class ChatListLocationContext {
|
||||
longTapped: {
|
||||
}
|
||||
)
|
||||
self.rightButton = AnyComponentWithIdentity(id: "done", component: AnyComponent(NavigationButtonComponent(
|
||||
content: .text(title: presentationData.strings.Common_Done, isBold: true),
|
||||
pressed: { [weak self] _ in
|
||||
self?.parentController?.donePressed()
|
||||
}
|
||||
)))
|
||||
} else {
|
||||
self.chatTitleComponent = ChatTitleComponent(
|
||||
context: self.context,
|
||||
@ -4724,7 +4725,16 @@ private final class ChatListLocationContext {
|
||||
self.parentController?.activateSearch()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if stateAndFilterId.state.editing {
|
||||
self.rightButton = AnyComponentWithIdentity(id: "done", component: AnyComponent(NavigationButtonComponent(
|
||||
content: .text(title: presentationData.strings.Common_Done, isBold: true),
|
||||
pressed: { [weak self] _ in
|
||||
self?.parentController?.donePressed()
|
||||
}
|
||||
)))
|
||||
} else {
|
||||
self.rightButton = AnyComponentWithIdentity(id: "more", component: AnyComponent(NavigationButtonComponent(
|
||||
content: .more,
|
||||
pressed: { [weak self] sourceView in
|
||||
|
@ -1510,7 +1510,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
})
|
||||
|
||||
let foundThreads: Signal<[EngineChatList.Item], NoError>
|
||||
if case .forum = location {
|
||||
if case .forum = location, (key == .topics || key == .chats) {
|
||||
foundThreads = chatListViewForLocation(chatListLocation: location, location: .initial(count: 1000, filter: nil), account: context.account)
|
||||
|> map { view -> [EngineChatList.Item] in
|
||||
var filteredItems: [EngineChatList.Item] = []
|
||||
|
@ -1142,7 +1142,12 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
|
||||
strongSelf.contextContainer.additionalActivationProgressLayer = nil
|
||||
if item.interaction.inlineNavigationLocation != nil {
|
||||
if let inlineNavigationLocation = item.interaction.inlineNavigationLocation {
|
||||
if case let .peer(peerId) = inlineNavigationLocation.location {
|
||||
if case let .chatList(index) = item.index, index.messageIndex.id.peerId == peerId {
|
||||
return false
|
||||
}
|
||||
}
|
||||
strongSelf.contextContainer.targetNodeForActivationProgress = strongSelf.avatarContainerNode
|
||||
} else if let value = strongSelf.hitTest(location, with: nil), value === strongSelf.compoundTextButtonNode?.view {
|
||||
strongSelf.contextContainer.targetNodeForActivationProgress = strongSelf.compoundTextButtonNode
|
||||
@ -1313,9 +1318,6 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
reallyHighlighted = true
|
||||
}
|
||||
}
|
||||
if item.interaction.inlineNavigationLocation != nil {
|
||||
reallyHighlighted = false
|
||||
}
|
||||
}
|
||||
return reallyHighlighted
|
||||
}
|
||||
@ -1403,7 +1405,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let textFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 15.0 / 17.0))
|
||||
let dateFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 14.0 / 17.0))
|
||||
let badgeFont = Font.with(size: floor(item.presentationData.fontSize.itemListBaseFontSize * 14.0 / 17.0), design: .regular, weight: .regular, traits: [.monospacedNumbers])
|
||||
let avatarBadgeFont = Font.with(size: 16.0, design: .regular, weight: .regular, traits: [.monospacedNumbers])
|
||||
let avatarBadgeFont = Font.with(size: floor(item.presentationData.fontSize.itemListBaseFontSize * 16.0 / 17.0), design: .regular, weight: .regular, traits: [.monospacedNumbers])
|
||||
|
||||
let account = item.context.account
|
||||
var messages: [EngineMessage]
|
||||
@ -1579,7 +1581,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
|
||||
let badgeDiameter = floor(item.presentationData.fontSize.baseDisplaySize * 20.0 / 17.0)
|
||||
let avatarBadgeDiameter: CGFloat = 22.0
|
||||
let avatarBadgeDiameter: CGFloat = floor(floor(item.presentationData.fontSize.itemListBaseFontSize * 22.0 / 17.0))
|
||||
|
||||
let currentAvatarBadgeCleanBackgroundImage: UIImage? = PresentationResourcesChatList.badgeBackgroundBorder(item.presentationData.theme, diameter: avatarBadgeDiameter + 4.0)
|
||||
|
||||
@ -2354,7 +2356,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
itemHeight += titleSpacing
|
||||
itemHeight += authorSpacing
|
||||
|
||||
let rawContentRect = CGRect(origin: CGPoint(x: 2.0, y: layoutOffset + 8.0), size: CGSize(width: rawContentWidth, height: itemHeight - 12.0 - 9.0))
|
||||
let rawContentRect = CGRect(origin: CGPoint(x: 2.0, y: layoutOffset + floor(item.presentationData.fontSize.itemListBaseFontSize * 8.0 / 17.0)), size: CGSize(width: rawContentWidth, height: itemHeight - 12.0 - 9.0))
|
||||
|
||||
let insets = ChatListItemNode.insets(first: first, last: last, firstWithHeader: firstWithHeader)
|
||||
var heightOffset: CGFloat = 0.0
|
||||
@ -2407,7 +2409,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
var mainContentAlpha: CGFloat = 1.0
|
||||
|
||||
if case .chatList = item.chatListLocation {
|
||||
mainContentFrame = CGRect(origin: CGPoint(x: params.leftInset + 72.0, y: 0.0), size: CGSize(width: layout.contentSize.width, height: layout.contentSize.height))
|
||||
mainContentFrame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: layout.contentSize.width, height: layout.contentSize.height))
|
||||
mainContentBoundsOffset = mainContentFrame.origin.x
|
||||
|
||||
if let inlineNavigationLocation = item.interaction.inlineNavigationLocation {
|
||||
@ -2497,7 +2499,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
var avatarScaleOffset: CGFloat = 0.0
|
||||
var avatarScale: CGFloat = 1.0
|
||||
if let inlineNavigationLocation = item.interaction.inlineNavigationLocation {
|
||||
let targetAvatarScale: CGFloat = 54.0 / avatarFrame.width
|
||||
let targetAvatarScale: CGFloat = floor(item.presentationData.fontSize.itemListBaseFontSize * 54.0 / 17.0) / avatarFrame.width
|
||||
avatarScale = targetAvatarScale * inlineNavigationLocation.progress + 1.0 * (1.0 - inlineNavigationLocation.progress)
|
||||
|
||||
let targetAvatarScaleOffset: CGFloat = -(avatarFrame.width - avatarFrame.width * avatarScale) * 0.5
|
||||
@ -2863,7 +2865,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
finalBottomRect
|
||||
], color: theme.pinnedItemBackgroundColor.mixedWith(theme.unreadBadgeInactiveBackgroundColor, alpha: 0.1))
|
||||
|
||||
compoundTextButtonNode.frame = compoundHighlightingNode.frame
|
||||
transition.updateFrame(node: compoundTextButtonNode, frame: compoundHighlightingNode.frame)
|
||||
|
||||
if let textArrowImage = textArrowImage {
|
||||
let textArrowNode: ASImageNode
|
||||
@ -2903,8 +2905,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
if let dustNode = strongSelf.dustNode {
|
||||
compoundTextButtonNode.addSubnode(dustNode)
|
||||
}
|
||||
}
|
||||
strongSelf.textNode.textNode.frame = textNodeFrame.offsetBy(dx: -compoundTextButtonNode.frame.minX, dy: -compoundTextButtonNode.frame.minY)
|
||||
}
|
||||
|
||||
strongSelf.authorNode.assignParentNode(parentNode: compoundTextButtonNode)
|
||||
} else {
|
||||
@ -2913,8 +2915,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
if let dustNode = strongSelf.dustNode {
|
||||
strongSelf.mainContentContainerNode.addSubnode(dustNode)
|
||||
}
|
||||
}
|
||||
strongSelf.textNode.textNode.frame = textNodeFrame
|
||||
}
|
||||
|
||||
strongSelf.authorNode.assignParentNode(parentNode: nil)
|
||||
}
|
||||
@ -3043,10 +3045,12 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let titlePosition = strongSelf.titleNode.position
|
||||
transition.animatePosition(node: strongSelf.titleNode, from: CGPoint(x: titlePosition.x - contentDelta.x, y: titlePosition.y - contentDelta.y))
|
||||
|
||||
if strongSelf.textNode.textNode.supernode === strongSelf.mainContentContainerNode {
|
||||
transition.animatePositionAdditive(node: strongSelf.textNode.textNode, offset: CGPoint(x: -contentDelta.x, y: -contentDelta.y))
|
||||
if let dustNode = strongSelf.dustNode {
|
||||
transition.animatePositionAdditive(node: dustNode, offset: CGPoint(x: -contentDelta.x, y: -contentDelta.y))
|
||||
}
|
||||
}
|
||||
|
||||
let authorPosition = strongSelf.authorNode.position
|
||||
transition.animatePosition(node: strongSelf.authorNode, from: CGPoint(x: authorPosition.x - contentDelta.x, y: authorPosition.y - contentDelta.y))
|
||||
|
@ -659,10 +659,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
hasTimer = true
|
||||
}
|
||||
|
||||
var hasSchedule = true
|
||||
if controller.chatLocation?.threadId != nil {
|
||||
hasSchedule = false
|
||||
}
|
||||
let hasSchedule = true
|
||||
|
||||
self.openingMedia = true
|
||||
|
||||
|
@ -1,21 +1,56 @@
|
||||
|
||||
final class MutableInvalidatedMessageHistoryTagSummariesView: MutablePostboxView {
|
||||
private let peerId: PeerId?
|
||||
private let threadId: Int64?
|
||||
private let namespace: MessageId.Namespace
|
||||
private let tagMask: MessageTags
|
||||
|
||||
var entries = Set<InvalidatedMessageHistoryTagsSummaryEntry>()
|
||||
|
||||
init(postbox: PostboxImpl, tagMask: MessageTags, namespace: MessageId.Namespace) {
|
||||
init(postbox: PostboxImpl, peerId: PeerId?, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace) {
|
||||
self.peerId = peerId
|
||||
self.threadId = threadId
|
||||
self.tagMask = tagMask
|
||||
self.namespace = namespace
|
||||
|
||||
if let peerId = self.peerId {
|
||||
if let entry = postbox.invalidatedMessageHistoryTagsSummaryTable.get(peerId: peerId, threadId: self.threadId, tagMask: self.tagMask, namespace: self.namespace) {
|
||||
self.entries.insert(entry)
|
||||
}
|
||||
} else {
|
||||
for entry in postbox.invalidatedMessageHistoryTagsSummaryTable.get(tagMask: tagMask, namespace: namespace) {
|
||||
self.entries.insert(entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool {
|
||||
var updated = false
|
||||
|
||||
if let peerId = self.peerId {
|
||||
var maybeUpdated = false
|
||||
loop: for operation in transaction.currentInvalidateMessageTagSummaries {
|
||||
switch operation {
|
||||
case let .add(entry):
|
||||
if entry.key.peerId == peerId && entry.key.threadId == self.threadId {
|
||||
maybeUpdated = true
|
||||
break loop
|
||||
}
|
||||
case let .remove(key):
|
||||
if key.peerId == peerId && key.threadId == self.threadId {
|
||||
maybeUpdated = true
|
||||
break loop
|
||||
}
|
||||
}
|
||||
}
|
||||
if maybeUpdated {
|
||||
self.entries.removeAll()
|
||||
if let entry = postbox.invalidatedMessageHistoryTagsSummaryTable.get(peerId: peerId, threadId: self.threadId, tagMask: self.tagMask, namespace: self.namespace) {
|
||||
self.entries.insert(entry)
|
||||
}
|
||||
updated = true
|
||||
}
|
||||
} else {
|
||||
for operation in transaction.currentInvalidateMessageTagSummaries {
|
||||
switch operation {
|
||||
case let .add(entry):
|
||||
@ -35,6 +70,8 @@ final class MutableInvalidatedMessageHistoryTagSummariesView: MutablePostboxView
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return updated
|
||||
}
|
||||
|
||||
|
@ -74,6 +74,10 @@ final class InvalidatedMessageHistoryTagsSummaryTable: Table {
|
||||
return entries
|
||||
}
|
||||
|
||||
func get(peerId: PeerId, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace) -> InvalidatedMessageHistoryTagsSummaryEntry? {
|
||||
return self.get(InvalidatedMessageHistoryTagsSummaryKey(peerId: peerId, namespace: namespace, tagMask: tagMask, threadId: threadId))
|
||||
}
|
||||
|
||||
private func get(_ key: InvalidatedMessageHistoryTagsSummaryKey) -> InvalidatedMessageHistoryTagsSummaryEntry? {
|
||||
if let value = self.valueBox.get(self.table, key: self.key(key)) {
|
||||
var version: Int32 = 0
|
||||
|
@ -10,7 +10,7 @@ public enum PostboxViewKey: Hashable {
|
||||
case globalMessageTags(globalTag: GlobalMessageTags, position: MessageIndex, count: Int, groupingPredicate: ((Message, Message) -> Bool)?)
|
||||
case peer(peerId: PeerId, components: PeerViewComponents)
|
||||
case pendingMessageActions(type: PendingMessageActionType)
|
||||
case invalidatedMessageHistoryTagSummaries(tagMask: MessageTags, namespace: MessageId.Namespace)
|
||||
case invalidatedMessageHistoryTagSummaries(peerId: PeerId?, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace)
|
||||
case pendingMessageActionsSummary(type: PendingMessageActionType, peerId: PeerId, namespace: MessageId.Namespace)
|
||||
case historyTagSummaryView(tag: MessageTags, peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace)
|
||||
case cachedPeerData(peerId: PeerId)
|
||||
@ -61,7 +61,9 @@ public enum PostboxViewKey: Hashable {
|
||||
hasher.combine(peerId)
|
||||
case let .pendingMessageActions(type):
|
||||
hasher.combine(type)
|
||||
case let .invalidatedMessageHistoryTagSummaries(tagMask, namespace):
|
||||
case let .invalidatedMessageHistoryTagSummaries(peerId, threadId, tagMask, namespace):
|
||||
hasher.combine(peerId)
|
||||
hasher.combine(threadId)
|
||||
hasher.combine(tagMask)
|
||||
hasher.combine(namespace)
|
||||
case let .pendingMessageActionsSummary(type, peerId, namespace):
|
||||
@ -191,8 +193,8 @@ public enum PostboxViewKey: Hashable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .invalidatedMessageHistoryTagSummaries:
|
||||
if case .invalidatedMessageHistoryTagSummaries = rhs {
|
||||
case let .invalidatedMessageHistoryTagSummaries(peerId, threadId, tagMask, namespace):
|
||||
if case .invalidatedMessageHistoryTagSummaries(peerId, threadId, tagMask, namespace) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -395,8 +397,8 @@ func postboxViewForKey(postbox: PostboxImpl, key: PostboxViewKey) -> MutablePost
|
||||
return MutablePeerView(postbox: postbox, peerId: peerId, components: components)
|
||||
case let .pendingMessageActions(type):
|
||||
return MutablePendingMessageActionsView(postbox: postbox, type: type)
|
||||
case let .invalidatedMessageHistoryTagSummaries(tagMask, namespace):
|
||||
return MutableInvalidatedMessageHistoryTagSummariesView(postbox: postbox, tagMask: tagMask, namespace: namespace)
|
||||
case let .invalidatedMessageHistoryTagSummaries(peerId, threadId, tagMask, namespace):
|
||||
return MutableInvalidatedMessageHistoryTagSummariesView(postbox: postbox, peerId: peerId, threadId: threadId, tagMask: tagMask, namespace: namespace)
|
||||
case let .pendingMessageActionsSummary(type, peerId, namespace):
|
||||
return MutablePendingMessageActionsSummaryView(postbox: postbox, type: type, peerId: peerId, namespace: namespace)
|
||||
case let .historyTagSummaryView(tag, peerId, threadId, namespace):
|
||||
|
@ -1091,7 +1091,6 @@ public class Account {
|
||||
self.managedOperationsDisposable.add(managedSynchronizeConsumeMessageContentOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
||||
self.managedOperationsDisposable.add(managedConsumePersonalMessagesActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
||||
self.managedOperationsDisposable.add(managedReadReactionActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
||||
self.managedOperationsDisposable.add(managedSynchronizeMessageHistoryTagSummaries(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
||||
self.managedOperationsDisposable.add(managedSynchronizeMarkAllUnseenPersonalMessagesOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
||||
self.managedOperationsDisposable.add(managedSynchronizeMarkAllUnseenReactionsOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
||||
self.managedOperationsDisposable.add(managedApplyPendingMessageReactionsActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
||||
|
@ -93,7 +93,7 @@ func managedConsumePersonalMessagesActions(postbox: Postbox, network: Network, s
|
||||
let helper = Atomic<ManagedConsumePersonalMessagesActionsHelper>(value: ManagedConsumePersonalMessagesActionsHelper())
|
||||
|
||||
let actionsKey = PostboxViewKey.pendingMessageActions(type: .consumeUnseenPersonalMessage)
|
||||
let invalidateKey = PostboxViewKey.invalidatedMessageHistoryTagSummaries(tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud)
|
||||
let invalidateKey = PostboxViewKey.invalidatedMessageHistoryTagSummaries(peerId: nil, threadId: nil, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud)
|
||||
let disposable = postbox.combinedView(keys: [actionsKey, invalidateKey]).start(next: { view in
|
||||
var entries: [PendingMessageActionsEntry] = []
|
||||
var invalidateEntries = Set<InvalidatedMessageHistoryTagsSummaryEntry>()
|
||||
@ -156,7 +156,7 @@ func managedReadReactionActions(postbox: Postbox, network: Network, stateManager
|
||||
let helper = Atomic<ManagedConsumePersonalMessagesActionsHelper>(value: ManagedConsumePersonalMessagesActionsHelper())
|
||||
|
||||
let actionsKey = PostboxViewKey.pendingMessageActions(type: .readReaction)
|
||||
let invalidateKey = PostboxViewKey.invalidatedMessageHistoryTagSummaries(tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud)
|
||||
let invalidateKey = PostboxViewKey.invalidatedMessageHistoryTagSummaries(peerId: nil, threadId: nil, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud)
|
||||
let disposable = postbox.combinedView(keys: [actionsKey, invalidateKey]).start(next: { view in
|
||||
var entries: [PendingMessageActionsEntry] = []
|
||||
var invalidateEntries = Set<InvalidatedMessageHistoryTagsSummaryEntry>()
|
||||
@ -435,11 +435,11 @@ private func synchronizeUnseenReactionsTag(postbox: Postbox, network: Network, e
|
||||
} |> switchToLatest
|
||||
}
|
||||
|
||||
func managedSynchronizeMessageHistoryTagSummaries(postbox: Postbox, network: Network, stateManager: AccountStateManager) -> Signal<Void, NoError> {
|
||||
func managedSynchronizeMessageHistoryTagSummaries(postbox: Postbox, network: Network, stateManager: AccountStateManager, peerId: PeerId, threadId: Int64) -> Signal<Void, NoError> {
|
||||
return Signal { _ in
|
||||
let helper = Atomic<ManagedConsumePersonalMessagesActionsHelper>(value: ManagedConsumePersonalMessagesActionsHelper())
|
||||
|
||||
let invalidateKey = PostboxViewKey.invalidatedMessageHistoryTagSummaries(tagMask: MessageTags(rawValue: 0), namespace: Namespaces.Message.Cloud)
|
||||
let invalidateKey = PostboxViewKey.invalidatedMessageHistoryTagSummaries(peerId: peerId, threadId: threadId, tagMask: MessageTags(rawValue: 0), namespace: Namespaces.Message.Cloud)
|
||||
let disposable = postbox.combinedView(keys: [invalidateKey]).start(next: { view in
|
||||
var invalidateEntries = Set<InvalidatedMessageHistoryTagsSummaryEntry>()
|
||||
if let v = view.views[invalidateKey] as? InvalidatedMessageHistoryTagSummariesView {
|
||||
@ -480,7 +480,7 @@ private func synchronizeMessageHistoryTagSummary(postbox: Postbox, network: Netw
|
||||
guard let threadId = entry.key.threadId else {
|
||||
return .complete()
|
||||
}
|
||||
if let peer = transaction.getPeer(entry.key.peerId), let inputPeer = apiInputPeer(peer) {
|
||||
if let peer = transaction.getPeer(entry.key.peerId) as? TelegramChannel, peer.flags.contains(.isForum), let inputPeer = apiInputPeer(peer) {
|
||||
return network.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: Int32(clamping: threadId), offsetId: 0, offsetDate: 0, addOffset: 0, limit: 1, maxId: 0, minId: 0, hash: 0))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.messages.Messages?, NoError> in
|
||||
|
@ -590,6 +590,14 @@ public struct ChatReplyThreadMessage: Equatable {
|
||||
self.initialAnchor = initialAnchor
|
||||
self.isNotAvailable = isNotAvailable
|
||||
}
|
||||
|
||||
public var normalized: ChatReplyThreadMessage {
|
||||
if self.isForumPost {
|
||||
return ChatReplyThreadMessage(messageId: self.messageId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)
|
||||
} else {
|
||||
return self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum FetchChannelReplyThreadMessageError {
|
||||
|
@ -494,5 +494,10 @@ public extension TelegramEngine {
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
|
||||
public func keepMessageCountersSyncrhonized(peerId: EnginePeer.Id, threadId: Int64) -> Signal<Never, NoError> {
|
||||
return managedSynchronizeMessageHistoryTagSummaries(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, peerId: peerId, threadId: threadId)
|
||||
|> ignoreValues
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ public func stringForFullAuthorName(message: EngineMessage, strings: Presentatio
|
||||
var authorName = ""
|
||||
if author.id == accountPeerId {
|
||||
authorName = strings.DialogList_You
|
||||
} else if author.isDeleted {
|
||||
authorName = strings.User_DeletedAccount
|
||||
} else {
|
||||
authorName = author.compactDisplayTitle
|
||||
}
|
||||
|
@ -317,7 +317,7 @@ final class AuthorizedApplicationContext {
|
||||
if let _ = threadData, let threadId = firstMessage.threadId {
|
||||
chatLocation = .replyThread(ChatReplyThreadMessage(
|
||||
messageId: MessageId(peerId: firstMessage.id.peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false
|
||||
))
|
||||
).normalized)
|
||||
} else {
|
||||
guard let peer = firstMessage.peers[firstMessage.id.peerId] else {
|
||||
return
|
||||
|
@ -295,6 +295,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
private var hasActiveGroupCallDisposable: Disposable?
|
||||
private var sendAsPeersDisposable: Disposable?
|
||||
private var preloadAttachBotIconsDisposables: DisposableSet?
|
||||
private var keepMessageCountersSyncrhonizedDisposable: Disposable?
|
||||
|
||||
private let editingMessage = ValuePromise<Float?>(nil, ignoreRepeated: true)
|
||||
private let startingBot = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
@ -5663,6 +5664,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.inviteRequestsDisposable.dispose()
|
||||
self.sendAsPeersDisposable?.dispose()
|
||||
self.preloadAttachBotIconsDisposables?.dispose()
|
||||
self.keepMessageCountersSyncrhonizedDisposable?.dispose()
|
||||
}
|
||||
deallocate()
|
||||
}
|
||||
@ -9326,7 +9328,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
guard case let .peer(peerId) = strongSelf.chatLocation else {
|
||||
guard let peerId = strongSelf.chatLocation.peerId else {
|
||||
return
|
||||
}
|
||||
guard let pinnedMessage = strongSelf.presentationInterfaceState.pinnedMessage else {
|
||||
@ -9370,7 +9372,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
})))
|
||||
}
|
||||
|
||||
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: .pinnedMessages(id: pinnedMessage.message.id), botStart: nil, mode: .standard(previewing: true))
|
||||
let chatLocation: ChatLocation
|
||||
if let _ = strongSelf.chatLocation.threadId {
|
||||
chatLocation = strongSelf.chatLocation
|
||||
} else {
|
||||
chatLocation = .peer(id: peerId)
|
||||
}
|
||||
|
||||
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: chatLocation, subject: .pinnedMessages(id: pinnedMessage.message.id), botStart: nil, mode: .standard(previewing: true))
|
||||
chatController.canReadHistory.set(false)
|
||||
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||
@ -9986,6 +9995,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
if case let .replyThread(message) = self.chatLocation, message.isForumPost {
|
||||
if self.keepMessageCountersSyncrhonizedDisposable == nil {
|
||||
self.keepMessageCountersSyncrhonizedDisposable = self.context.engine.messages.keepMessageCountersSyncrhonized(peerId: message.messageId.peerId, threadId: Int64(message.messageId.id)).start()
|
||||
}
|
||||
}
|
||||
|
||||
if let scheduledActivateInput = scheduledActivateInput, case .text = scheduledActivateInput {
|
||||
self.scheduledActivateInput = nil
|
||||
|
||||
@ -14964,16 +14979,27 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
if isPinnedMessages, let messageId = messageLocation.messageId {
|
||||
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: messageId.peerId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
let _ = (combineLatest(
|
||||
self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: messageId.peerId)),
|
||||
self.context.engine.messages.getMessagesLoadIfNecessary([messageId], strategy: .local)
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer, messages in
|
||||
guard let self, let peer = peer else {
|
||||
return
|
||||
}
|
||||
|
||||
if let navigationController = self.effectiveNavigationController {
|
||||
self.dismiss()
|
||||
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), subject: .message(id: .id(messageId), highlight: true, timecode: nil), keepStack: .always))
|
||||
guard let navigationController = self.effectiveNavigationController else {
|
||||
return
|
||||
}
|
||||
|
||||
self.dismiss()
|
||||
|
||||
let navigateToLocation: NavigateToChatControllerParams.Location
|
||||
if let threadId = messages.first?.threadId {
|
||||
navigateToLocation = .replyThread(ChatReplyThreadMessage(messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false))
|
||||
} else {
|
||||
navigateToLocation = .peer(peer)
|
||||
}
|
||||
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: navigateToLocation, subject: .message(id: .id(messageId), highlight: true, timecode: nil), keepStack: .always))
|
||||
})
|
||||
} else if case let .peer(peerId) = self.chatLocation, let messageId = messageLocation.messageId, (messageId.peerId != peerId && !forceInCurrentChat) || (isScheduledMessages && messageId.id != 0 && !Namespaces.Message.allScheduled.contains(messageId.namespace)) {
|
||||
let _ = (self.context.engine.data.get(
|
||||
|
@ -2368,24 +2368,57 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
})
|
||||
|
||||
if let loaded = displayedRange.visibleRange, let firstEntry = historyView.filteredEntries.first, let lastEntry = historyView.filteredEntries.last {
|
||||
if loaded.firstIndex < 5 && historyView.originalView.laterId != nil {
|
||||
if !"".isEmpty {
|
||||
print("load next")
|
||||
return
|
||||
|
||||
var mathesFirst = false
|
||||
if loaded.firstIndex <= 5 {
|
||||
var firstHasGroups = false
|
||||
for index in (max(0, historyView.filteredEntries.count - 5) ..< historyView.filteredEntries.count).reversed() {
|
||||
switch historyView.filteredEntries[index] {
|
||||
case .MessageEntry:
|
||||
break
|
||||
case .MessageGroupEntry:
|
||||
firstHasGroups = true
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
if firstHasGroups {
|
||||
mathesFirst = loaded.firstIndex <= 1
|
||||
} else {
|
||||
mathesFirst = loaded.firstIndex <= 5
|
||||
}
|
||||
}
|
||||
|
||||
var mathesLast = false
|
||||
if loaded.lastIndex >= historyView.filteredEntries.count - 5 {
|
||||
var lastHasGroups = false
|
||||
for index in 0 ..< min(5, historyView.filteredEntries.count) {
|
||||
switch historyView.filteredEntries[index] {
|
||||
case .MessageEntry:
|
||||
break
|
||||
case .MessageGroupEntry:
|
||||
lastHasGroups = true
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
if lastHasGroups {
|
||||
mathesLast = loaded.lastIndex >= historyView.filteredEntries.count - 1
|
||||
} else {
|
||||
mathesLast = loaded.lastIndex >= historyView.filteredEntries.count - 5
|
||||
}
|
||||
}
|
||||
|
||||
if mathesFirst && historyView.originalView.laterId != nil {
|
||||
let locationInput: ChatHistoryLocation = .Navigation(index: .message(lastEntry.index), anchorIndex: .message(lastEntry.index), count: historyMessageCount, highlight: false)
|
||||
if self.chatHistoryLocationValue?.content != locationInput {
|
||||
self.chatHistoryLocationValue = ChatHistoryLocationInput(content: locationInput, id: self.takeNextHistoryLocationId())
|
||||
}
|
||||
} else if loaded.firstIndex < 5, historyView.originalView.laterId == nil, !historyView.originalView.holeLater, let chatHistoryLocationValue = self.chatHistoryLocationValue, !chatHistoryLocationValue.isAtUpperBound, historyView.originalView.anchorIndex != .upperBound {
|
||||
} else if mathesFirst, historyView.originalView.laterId == nil, !historyView.originalView.holeLater, let chatHistoryLocationValue = self.chatHistoryLocationValue, !chatHistoryLocationValue.isAtUpperBound, historyView.originalView.anchorIndex != .upperBound {
|
||||
if self.chatHistoryLocationValue == historyView.locationInput {
|
||||
self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Navigation(index: .upperBound, anchorIndex: .upperBound, count: historyMessageCount, highlight: false), id: self.takeNextHistoryLocationId())
|
||||
}
|
||||
} else if loaded.lastIndex >= historyView.filteredEntries.count - 5 && historyView.originalView.earlierId != nil {
|
||||
if !"".isEmpty {
|
||||
print("load previous")
|
||||
return
|
||||
}
|
||||
} else if mathesLast && historyView.originalView.earlierId != nil {
|
||||
let locationInput: ChatHistoryLocation = .Navigation(index: .message(firstEntry.index), anchorIndex: .message(firstEntry.index), count: historyMessageCount, highlight: false)
|
||||
if self.chatHistoryLocationValue?.content != locationInput {
|
||||
self.chatHistoryLocationValue = ChatHistoryLocationInput(content: locationInput, id: self.takeNextHistoryLocationId())
|
||||
|
@ -92,10 +92,13 @@ func rightNavigationButtonForChatInterfaceState(_ presentationInterfaceState: Ch
|
||||
|
||||
if let channel = presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum), let moreInfoNavigationButton = moreInfoNavigationButton {
|
||||
if case .replyThread = presentationInterfaceState.chatLocation {
|
||||
} else {
|
||||
if case .pinnedMessages = presentationInterfaceState.subject {
|
||||
} else {
|
||||
return moreInfoNavigationButton
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var hasMessages = false
|
||||
if let chatHistoryState = presentationInterfaceState.chatHistoryState {
|
||||
|
@ -59,7 +59,11 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
|
||||
var isFirst = true
|
||||
if params.useExisting {
|
||||
for controller in params.navigationController.viewControllers.reversed() {
|
||||
if let controller = controller as? ChatControllerImpl, controller.chatLocation == params.chatLocation.asChatLocation && (controller.subject != .scheduledMessages || controller.subject == params.subject) {
|
||||
guard let controller = controller as? ChatControllerImpl else {
|
||||
isFirst = false
|
||||
continue
|
||||
}
|
||||
if controller.chatLocation.peerId == params.chatLocation.asChatLocation.peerId && controller.chatLocation.threadId == params.chatLocation.asChatLocation.threadId && (controller.subject != .scheduledMessages || controller.subject == params.subject) {
|
||||
if let updateTextInputState = params.updateTextInputState {
|
||||
controller.updateTextInputState(updateTextInputState)
|
||||
}
|
||||
|
@ -2180,7 +2180,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
self.usernameNode.displaysAsynchronously = false
|
||||
|
||||
self.buttonsContainerNode = SparseNode()
|
||||
self.buttonsContainerNode.clipsToBounds = false
|
||||
self.buttonsContainerNode.clipsToBounds = true
|
||||
|
||||
self.regularContentNode = PeerInfoHeaderRegularContentNode()
|
||||
var requestUpdateLayoutImpl: (() -> Void)?
|
||||
@ -2930,10 +2930,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
if subtitleIsButton {
|
||||
subtitleFrame.origin.y += 11.0
|
||||
}
|
||||
|
||||
let singleTitleLockOffset: CGFloat = (peer?.id == self.context.account.peerId || subtitleSize.height.isZero) ? 8.0 : 0.0
|
||||
|
||||
let titleLockOffset: CGFloat = 7.0 + singleTitleLockOffset
|
||||
@ -3028,6 +3024,19 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
avatarOffset = apparentTitleLockOffset + 0.0 * (1.0 - titleCollapseFraction) + 10.0 * titleCollapseFraction
|
||||
}
|
||||
|
||||
if subtitleIsButton {
|
||||
subtitleFrame.origin.y += 11.0 * (1.0 - titleCollapseFraction)
|
||||
if let subtitleBackgroundButton = self.subtitleBackgroundButton {
|
||||
transition.updateAlpha(node: subtitleBackgroundButton, alpha: (1.0 - titleCollapseFraction))
|
||||
}
|
||||
if let subtitleBackgroundNode = self.subtitleBackgroundNode {
|
||||
transition.updateAlpha(node: subtitleBackgroundNode, alpha: (1.0 - titleCollapseFraction))
|
||||
}
|
||||
if let subtitleArrowNode = self.subtitleArrowNode {
|
||||
transition.updateAlpha(node: subtitleArrowNode, alpha: (1.0 - titleCollapseFraction))
|
||||
}
|
||||
}
|
||||
|
||||
let avatarCornerRadius: CGFloat
|
||||
if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) {
|
||||
avatarCornerRadius = floor(avatarSize * 0.25)
|
||||
|
@ -677,6 +677,19 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
|
||||
return .peer(peer?._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return context.engine.messages.getMessagesLoadIfNecessary([messageId], strategy: .cloud(skipLocal: false))
|
||||
|> take(1)
|
||||
|> mapToSignal { messages -> Signal<ResolvedUrl?, NoError> in
|
||||
if let threadId = messages.first?.threadId {
|
||||
return context.engine.peers.fetchForumChannelTopic(id: channel.id, threadId: threadId)
|
||||
|> map { info -> ResolvedUrl? in
|
||||
if let _ = info {
|
||||
return .replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId)
|
||||
} else {
|
||||
return .peer(peer?._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return context.engine.peers.fetchForumChannelTopic(id: channel.id, threadId: Int64(messageId.id))
|
||||
|> map { info -> ResolvedUrl? in
|
||||
@ -687,6 +700,8 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let threadId = threadId {
|
||||
let replyThreadMessageId = MessageId(peerId: foundPeer.id, namespace: Namespaces.Message.Cloud, id: threadId)
|
||||
return context.engine.messages.fetchChannelReplyThreadMessage(messageId: replyThreadMessageId, atMessageId: nil)
|
||||
|
Loading…
x
Reference in New Issue
Block a user