mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Comment [WIP]
This commit is contained in:
parent
4076d3dc4d
commit
e2f8229ae2
@ -1556,6 +1556,7 @@
|
||||
|
||||
"Channel.ErrorAccessDenied" = "Sorry, this channel is private.";
|
||||
"Group.ErrorAccessDenied" = "Sorry, this group is private.";
|
||||
"CommentsGroup.ErrorAccessDenied" = "Sorry, you can't access this chat because you were banned by an admin";
|
||||
"Conversation.InputTextBroadcastPlaceholder" = "Broadcast";
|
||||
|
||||
"Channel.NotificationLoading" = "Loading...";
|
||||
@ -5810,3 +5811,6 @@ Any member of this group will be able to see messages in the channel.";
|
||||
"Channel.CommentsGroup.Header" = "Select a group chat that will be used to host comments from your channel.";
|
||||
"Channel.CommentsGroup.HeaderSet" = "%@ is selected as the group that will be used to host comments for your channel.";
|
||||
"Channel.CommentsGroup.HeaderGroupSet" = "%@ is linking the group as it's discussion board.";
|
||||
|
||||
"RepliesChat.DescriptionTitle" = "What is it?";
|
||||
"RepliesChat.DescriptionText" = "This is a chat with replies.";
|
||||
|
@ -149,7 +149,9 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
|
||||
}
|
||||
|
||||
let status: ContactsPeerItemStatus
|
||||
if let user = primaryPeer as? TelegramUser {
|
||||
if primaryPeer.id.isReplies {
|
||||
status = .none
|
||||
} else if let user = primaryPeer as? TelegramUser {
|
||||
let servicePeer = isServicePeer(primaryPeer)
|
||||
if user.flags.contains(.isSupport) && !servicePeer {
|
||||
status = .custom(string: strings.Bot_GenericSupportStatus, multiline: false)
|
||||
|
@ -1843,7 +1843,9 @@ private func statusStringForPeerType(accountPeerId: PeerId, strings: Presentatio
|
||||
}
|
||||
}
|
||||
|
||||
if let user = peer as? TelegramUser {
|
||||
if peer.id.isReplies {
|
||||
return nil
|
||||
} else if let user = peer as? TelegramUser {
|
||||
if user.botInfo != nil || user.flags.contains(.isSupport) {
|
||||
return (strings.ChatList_PeerTypeBot, false)
|
||||
} else if isContact {
|
||||
|
@ -425,6 +425,9 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo
|
||||
} else if peer.flags.contains(.isSupport), !servicePeer {
|
||||
statusText = item.presentationData.strings.Bot_GenericSupportStatus
|
||||
statusColor = item.presentationData.theme.list.itemSecondaryTextColor
|
||||
} else if peer.id.isReplies {
|
||||
statusText = ""
|
||||
statusColor = item.presentationData.theme.list.itemPrimaryTextColor
|
||||
} else if let _ = peer.botInfo {
|
||||
statusText = item.presentationData.strings.Bot_GenericBotStatus
|
||||
statusColor = item.presentationData.theme.list.itemSecondaryTextColor
|
||||
|
@ -139,6 +139,7 @@ private final class OverlayStatusControllerImpl: ViewController, StandalonePrese
|
||||
private let type: OverlayStatusControllerType
|
||||
|
||||
private var animatedDidAppear = false
|
||||
private var isDismissed = false
|
||||
|
||||
private var controllerNode: OverlayStatusControllerNode {
|
||||
return self.displayNode as! OverlayStatusControllerNode
|
||||
@ -181,8 +182,12 @@ private final class OverlayStatusControllerImpl: ViewController, StandalonePrese
|
||||
}
|
||||
|
||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||
if self.isDismissed {
|
||||
completion?()
|
||||
return
|
||||
}
|
||||
self.isDismissed = true
|
||||
self.controllerNode.dismiss()
|
||||
completion?()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,80 @@
|
||||
import Foundation
|
||||
|
||||
public struct PeerChatThreadId: Hashable {
|
||||
public var peerId: PeerId
|
||||
public var threadId: Int64
|
||||
|
||||
public init(peerId: PeerId, threadId: Int64) {
|
||||
self.peerId = peerId
|
||||
self.threadId = threadId
|
||||
}
|
||||
}
|
||||
|
||||
final class PeerChatThreadInterfaceStateTable: Table {
|
||||
static func tableSpec(_ id: Int32) -> ValueBoxTable {
|
||||
return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: false)
|
||||
}
|
||||
|
||||
private var states: [PeerChatThreadId: PeerChatInterfaceState?] = [:]
|
||||
private var peerIdsWithUpdatedStates = Set<PeerChatThreadId>()
|
||||
|
||||
private let sharedKey = ValueBoxKey(length: 8 + 8)
|
||||
|
||||
private func key(_ peerId: PeerChatThreadId, sharedKey: ValueBoxKey = ValueBoxKey(length: 8)) -> ValueBoxKey {
|
||||
sharedKey.setInt64(0, value: peerId.peerId.toInt64())
|
||||
sharedKey.setInt64(8, value: peerId.threadId)
|
||||
return sharedKey
|
||||
}
|
||||
|
||||
func get(_ peerId: PeerChatThreadId) -> PeerChatInterfaceState? {
|
||||
if let cachedValue = self.states[peerId] {
|
||||
return cachedValue
|
||||
} else if let value = self.valueBox.get(self.table, key: self.key(peerId, sharedKey: self.sharedKey)), let state = PostboxDecoder(buffer: value).decodeRootObject() as? PeerChatInterfaceState {
|
||||
self.states[peerId] = state
|
||||
return state
|
||||
} else {
|
||||
self.states[peerId] = nil
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func set(_ peerId: PeerChatThreadId, state: PeerChatInterfaceState?) -> Bool {
|
||||
let currentState = self.get(peerId)
|
||||
var updated = false
|
||||
if let currentState = currentState, let state = state {
|
||||
if !currentState.isEqual(to: state) {
|
||||
updated = true
|
||||
}
|
||||
} else if (currentState != nil) != (state != nil) {
|
||||
updated = true
|
||||
}
|
||||
if updated {
|
||||
self.states[peerId] = state
|
||||
self.peerIdsWithUpdatedStates.insert(peerId)
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
override func clearMemoryCache() {
|
||||
self.states.removeAll()
|
||||
self.peerIdsWithUpdatedStates.removeAll()
|
||||
}
|
||||
|
||||
override func beforeCommit() {
|
||||
if !self.peerIdsWithUpdatedStates.isEmpty {
|
||||
let sharedEncoder = PostboxEncoder()
|
||||
for peerId in self.peerIdsWithUpdatedStates {
|
||||
if let state = self.states[peerId] {
|
||||
if let state = state {
|
||||
sharedEncoder.reset()
|
||||
sharedEncoder.encodeRootObject(state)
|
||||
self.valueBox.set(self.table, key: self.key(peerId, sharedKey: self.sharedKey), value: sharedEncoder.readBufferNoCopy())
|
||||
} else {
|
||||
self.valueBox.remove(self.table, key: self.key(peerId, sharedKey: self.sharedKey), secure: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.peerIdsWithUpdatedStates.removeAll()
|
||||
}
|
||||
}
|
||||
}
|
@ -274,6 +274,11 @@ public final class Transaction {
|
||||
self.postbox?.updatePeerChatInterfaceState(id, update: update)
|
||||
}
|
||||
|
||||
public func updatePeerChatThreadInterfaceState(_ id: PeerId, threadId: Int64, update: (PeerChatInterfaceState?) -> (PeerChatInterfaceState?)) {
|
||||
assert(!self.disposed)
|
||||
self.postbox?.updatePeerChatThreadInterfaceState(id, threadId: threadId, update: update)
|
||||
}
|
||||
|
||||
public func getPeer(_ id: PeerId) -> Peer? {
|
||||
assert(!self.disposed)
|
||||
return self.postbox?.peerTable.get(id)
|
||||
@ -1294,6 +1299,7 @@ public final class Postbox {
|
||||
let itemCollectionItemTable: ItemCollectionItemTable
|
||||
let itemCollectionReverseIndexTable: ReverseIndexReferenceTable<ItemCollectionItemReverseIndexReference>
|
||||
let peerChatInterfaceStateTable: PeerChatInterfaceStateTable
|
||||
let peerChatThreadInterfaceStateTable: PeerChatThreadInterfaceStateTable
|
||||
let itemCacheMetaTable: ItemCacheMetaTable
|
||||
let itemCacheTable: ItemCacheTable
|
||||
let peerNameTokenIndexTable: ReverseIndexReferenceTable<PeerIdReverseIndexReference>
|
||||
@ -1394,6 +1400,7 @@ public final class Postbox {
|
||||
self.itemCollectionReverseIndexTable = ReverseIndexReferenceTable<ItemCollectionItemReverseIndexReference>(valueBox: self.valueBox, table: ReverseIndexReferenceTable<ItemCollectionItemReverseIndexReference>.tableSpec(36))
|
||||
self.itemCollectionItemTable = ItemCollectionItemTable(valueBox: self.valueBox, table: ItemCollectionItemTable.tableSpec(22), reverseIndexTable: self.itemCollectionReverseIndexTable)
|
||||
self.peerChatInterfaceStateTable = PeerChatInterfaceStateTable(valueBox: self.valueBox, table: PeerChatInterfaceStateTable.tableSpec(23))
|
||||
self.peerChatThreadInterfaceStateTable = PeerChatThreadInterfaceStateTable(valueBox: self.valueBox, table: PeerChatThreadInterfaceStateTable.tableSpec(64))
|
||||
self.itemCacheMetaTable = ItemCacheMetaTable(valueBox: self.valueBox, table: ItemCacheMetaTable.tableSpec(24))
|
||||
self.itemCacheTable = ItemCacheTable(valueBox: self.valueBox, table: ItemCacheTable.tableSpec(25))
|
||||
self.chatListIndexTable = ChatListIndexTable(valueBox: self.valueBox, table: ChatListIndexTable.tableSpec(8), peerNameIndexTable: self.peerNameIndexTable, metadataTable: self.messageHistoryMetadataTable, readStateTable: self.readStateTable, notificationSettingsTable: self.peerNotificationSettingsTable)
|
||||
@ -1445,6 +1452,7 @@ public final class Postbox {
|
||||
tables.append(self.itemCollectionItemTable)
|
||||
tables.append(self.itemCollectionReverseIndexTable)
|
||||
tables.append(self.peerChatInterfaceStateTable)
|
||||
tables.append(self.peerChatThreadInterfaceStateTable)
|
||||
tables.append(self.itemCacheMetaTable)
|
||||
tables.append(self.itemCacheTable)
|
||||
tables.append(self.peerNameIndexTable)
|
||||
@ -2119,6 +2127,11 @@ public final class Postbox {
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func updatePeerChatThreadInterfaceState(_ id: PeerId, threadId: Int64, update: (PeerChatInterfaceState?) -> (PeerChatInterfaceState?)) {
|
||||
let updatedState = update(self.peerChatThreadInterfaceStateTable.get(PeerChatThreadId(peerId: id, threadId: threadId)))
|
||||
let _ = self.peerChatThreadInterfaceStateTable.set(PeerChatThreadId(peerId: id, threadId: threadId), state: updatedState)
|
||||
}
|
||||
|
||||
fileprivate func replaceRemoteContactCount(_ count: Int32) {
|
||||
self.metadataTable.setRemoteContactCount(count)
|
||||
self.currentReplaceRemoteContactCount = count
|
||||
@ -2666,11 +2679,11 @@ public final class Postbox {
|
||||
let initialData: InitialMessageHistoryData
|
||||
switch peerIds {
|
||||
case let .single(peerId):
|
||||
initialData = self.initialMessageHistoryData(peerId: peerId)
|
||||
initialData = self.initialMessageHistoryData(peerId: peerId, threadId: nil)
|
||||
case let .associated(peerId, _):
|
||||
initialData = self.initialMessageHistoryData(peerId: peerId)
|
||||
initialData = self.initialMessageHistoryData(peerId: peerId, threadId: nil)
|
||||
case let .external(input):
|
||||
initialData = self.initialMessageHistoryData(peerId: input.peerId)
|
||||
initialData = self.initialMessageHistoryData(peerId: input.peerId, threadId: input.threadId)
|
||||
}
|
||||
|
||||
subscriber.putNext((MessageHistoryView(mutableView), initialUpdateType, initialData))
|
||||
@ -2687,17 +2700,30 @@ public final class Postbox {
|
||||
}
|
||||
}
|
||||
|
||||
private func initialMessageHistoryData(peerId: PeerId) -> InitialMessageHistoryData {
|
||||
let chatInterfaceState = self.peerChatInterfaceStateTable.get(peerId)
|
||||
var associatedMessages: [MessageId: Message] = [:]
|
||||
if let chatInterfaceState = chatInterfaceState {
|
||||
for id in chatInterfaceState.associatedMessageIds {
|
||||
if let message = self.getMessage(id) {
|
||||
associatedMessages[message.id] = message
|
||||
private func initialMessageHistoryData(peerId: PeerId, threadId: Int64?) -> InitialMessageHistoryData {
|
||||
if let threadId = threadId {
|
||||
let chatInterfaceState = self.peerChatThreadInterfaceStateTable.get(PeerChatThreadId(peerId: peerId, threadId: threadId))
|
||||
var associatedMessages: [MessageId: Message] = [:]
|
||||
if let chatInterfaceState = chatInterfaceState {
|
||||
for id in chatInterfaceState.associatedMessageIds {
|
||||
if let message = self.getMessage(id) {
|
||||
associatedMessages[message.id] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
return InitialMessageHistoryData(peer: self.peerTable.get(peerId), chatInterfaceState: chatInterfaceState, associatedMessages: associatedMessages)
|
||||
} else {
|
||||
let chatInterfaceState = self.peerChatInterfaceStateTable.get(peerId)
|
||||
var associatedMessages: [MessageId: Message] = [:]
|
||||
if let chatInterfaceState = chatInterfaceState {
|
||||
for id in chatInterfaceState.associatedMessageIds {
|
||||
if let message = self.getMessage(id) {
|
||||
associatedMessages[message.id] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
return InitialMessageHistoryData(peer: self.peerTable.get(peerId), chatInterfaceState: chatInterfaceState, associatedMessages: associatedMessages)
|
||||
}
|
||||
return InitialMessageHistoryData(peer: self.peerTable.get(peerId), chatInterfaceState: chatInterfaceState, associatedMessages: associatedMessages)
|
||||
}
|
||||
|
||||
public func messageIndexAtId(_ id: MessageId) -> Signal<MessageIndex?, NoError> {
|
||||
|
@ -159,7 +159,7 @@ struct FetchMessageHistoryHoleResult: Equatable {
|
||||
var actualThreadId: Int64?
|
||||
}
|
||||
|
||||
func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryHoleSource, postbox: Postbox, peerInput: FetchMessageHistoryHoleThreadInput, namespace: MessageId.Namespace, direction: MessageHistoryViewRelativeHoleDirection, space: MessageHistoryHoleSpace, count rawCount: Int) -> Signal<FetchMessageHistoryHoleResult, NoError> {
|
||||
func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryHoleSource, postbox: Postbox, peerInput: FetchMessageHistoryHoleThreadInput, namespace: MessageId.Namespace, direction: MessageHistoryViewRelativeHoleDirection, space: MessageHistoryHoleSpace, count rawCount: Int) -> Signal<FetchMessageHistoryHoleResult?, NoError> {
|
||||
let count = min(100, rawCount)
|
||||
|
||||
return postbox.stateView()
|
||||
@ -171,7 +171,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
|
||||
}
|
||||
}
|
||||
|> take(1)
|
||||
|> mapToSignal { _ -> Signal<FetchMessageHistoryHoleResult, NoError> in
|
||||
|> mapToSignal { _ -> Signal<FetchMessageHistoryHoleResult?, NoError> in
|
||||
return postbox.transaction { transaction -> (Api.InputPeer?, Int32) in
|
||||
switch peerInput {
|
||||
case let .direct(peerId, _):
|
||||
@ -180,7 +180,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
|
||||
return (transaction.getPeer(channelMessageId.peerId).flatMap(forceApiInputPeer), 0)
|
||||
}
|
||||
}
|
||||
|> mapToSignal { (inputPeer, hash) -> Signal<FetchMessageHistoryHoleResult, NoError> in
|
||||
|> mapToSignal { (inputPeer, hash) -> Signal<FetchMessageHistoryHoleResult?, NoError> in
|
||||
guard let inputPeer = inputPeer else {
|
||||
return .single(FetchMessageHistoryHoleResult(removedIndices: IndexSet(), strictRemovedIndices: IndexSet(), actualPeerId: nil, actualThreadId: nil))
|
||||
}
|
||||
@ -404,8 +404,27 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
|
||||
}
|
||||
|
||||
return request
|
||||
|> retryRequest
|
||||
|> mapToSignal { result -> Signal<FetchMessageHistoryHoleResult, NoError> in
|
||||
|> retry(retryOnError: { error in
|
||||
if error.errorDescription == "CHANNEL_PRIVATE" {
|
||||
switch peerInput {
|
||||
case let .direct(_, threadId):
|
||||
if threadId != nil {
|
||||
return false
|
||||
}
|
||||
case .threadFromChannel:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}, delayIncrement: 0.1, maxDelay: 2.0, maxRetries: 0, onQueue: .concurrentDefaultQueue())
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.messages.Messages?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<FetchMessageHistoryHoleResult?, NoError> in
|
||||
guard let result = result else {
|
||||
return .single(nil)
|
||||
}
|
||||
let messages: [Api.Message]
|
||||
let chats: [Api.Chat]
|
||||
let users: [Api.User]
|
||||
|
@ -225,7 +225,7 @@ private class ReplyThreadHistoryContextImpl {
|
||||
self.currentHole?.1.dispose()
|
||||
if let entry = entry {
|
||||
self.currentHole = (entry, self.fetchHole(entry: entry).start(next: { [weak self] removedHoleIndices in
|
||||
guard let strongSelf = self else {
|
||||
guard let strongSelf = self, let removedHoleIndices = removedHoleIndices else {
|
||||
return
|
||||
}
|
||||
if var currentHoles = strongSelf.stateValue?.holeIndices[Namespaces.Message.Cloud] {
|
||||
@ -239,7 +239,7 @@ private class ReplyThreadHistoryContextImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private func fetchHole(entry: MessageHistoryHolesViewEntry) -> Signal<FetchMessageHistoryHoleResult, NoError> {
|
||||
private func fetchHole(entry: MessageHistoryHolesViewEntry) -> Signal<FetchMessageHistoryHoleResult?, NoError> {
|
||||
switch entry.hole {
|
||||
case let .peer(hole):
|
||||
let fetchCount = min(entry.count, 100)
|
||||
@ -386,8 +386,9 @@ public struct ChatReplyThreadMessage: Equatable {
|
||||
public var maxReadOutgoingMessageId: MessageId?
|
||||
public var initialFilledHoles: IndexSet
|
||||
public var initialAnchor: Anchor
|
||||
public var isNotAvailable: Bool
|
||||
|
||||
fileprivate init(messageId: MessageId, isChannelPost: Bool, maxMessage: MessageId?, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?, initialFilledHoles: IndexSet, initialAnchor: Anchor) {
|
||||
fileprivate init(messageId: MessageId, isChannelPost: Bool, maxMessage: MessageId?, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?, initialFilledHoles: IndexSet, initialAnchor: Anchor, isNotAvailable: Bool) {
|
||||
self.messageId = messageId
|
||||
self.isChannelPost = isChannelPost
|
||||
self.maxMessage = maxMessage
|
||||
@ -395,6 +396,7 @@ public struct ChatReplyThreadMessage: Equatable {
|
||||
self.maxReadOutgoingMessageId = maxReadOutgoingMessageId
|
||||
self.initialFilledHoles = initialFilledHoles
|
||||
self.initialAnchor = initialAnchor
|
||||
self.isNotAvailable = isNotAvailable
|
||||
}
|
||||
}
|
||||
|
||||
@ -590,11 +592,11 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
|
||||
}
|
||||
|
||||
let preloadedHistory = preloadedHistoryPosition
|
||||
|> mapToSignal { peerInput, commentsPeerId, threadMessageId, anchor, maxMessageId -> Signal<(FetchMessageHistoryHoleResult, ChatReplyThreadMessage.Anchor), FetchChannelReplyThreadMessageError> in
|
||||
|> mapToSignal { peerInput, commentsPeerId, threadMessageId, anchor, maxMessageId -> Signal<(FetchMessageHistoryHoleResult?, ChatReplyThreadMessage.Anchor), FetchChannelReplyThreadMessageError> in
|
||||
guard let maxMessageId = maxMessageId else {
|
||||
return .single((FetchMessageHistoryHoleResult(removedIndices: IndexSet(integersIn: 1 ..< Int(Int32.max - 1)), strictRemovedIndices: IndexSet()), .automatic))
|
||||
}
|
||||
return account.postbox.transaction { transaction -> Signal<(FetchMessageHistoryHoleResult, ChatReplyThreadMessage.Anchor), FetchChannelReplyThreadMessageError> in
|
||||
return account.postbox.transaction { transaction -> Signal<(FetchMessageHistoryHoleResult?, ChatReplyThreadMessage.Anchor), FetchChannelReplyThreadMessageError> in
|
||||
if let threadMessageId = threadMessageId {
|
||||
var holes = transaction.getThreadIndexHoles(peerId: threadMessageId.peerId, threadId: makeMessageThreadId(threadMessageId), namespace: Namespaces.Message.Cloud)
|
||||
holes.remove(integersIn: Int(maxMessageId.id + 1) ..< Int(Int32.max))
|
||||
@ -660,8 +662,11 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
|
||||
}
|
||||
return fetchMessageHistoryHole(accountPeerId: account.peerId, source: .network(account.network), postbox: account.postbox, peerInput: peerInput, namespace: Namespaces.Message.Cloud, direction: direction, space: .everywhere, count: 40)
|
||||
|> castError(FetchChannelReplyThreadMessageError.self)
|
||||
|> mapToSignal { result -> Signal<(FetchMessageHistoryHoleResult, ChatReplyThreadMessage.Anchor), FetchChannelReplyThreadMessageError> in
|
||||
return account.postbox.transaction { transaction -> (FetchMessageHistoryHoleResult, ChatReplyThreadMessage.Anchor) in
|
||||
|> mapToSignal { result -> Signal<(FetchMessageHistoryHoleResult?, ChatReplyThreadMessage.Anchor), FetchChannelReplyThreadMessageError> in
|
||||
return account.postbox.transaction { transaction -> (FetchMessageHistoryHoleResult?, ChatReplyThreadMessage.Anchor) in
|
||||
guard let result = result else {
|
||||
return (nil, .automatic)
|
||||
}
|
||||
let initialAnchor: ChatReplyThreadMessage.Anchor
|
||||
switch anchor {
|
||||
case .lowerBound:
|
||||
@ -700,8 +705,10 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
|
||||
}
|
||||
let (initialFilledHoles, initialAnchor) = initialFilledHolesAndInitialAnchor
|
||||
return account.postbox.transaction { transaction -> Signal<ChatReplyThreadMessage, FetchChannelReplyThreadMessageError> in
|
||||
for range in initialFilledHoles.strictRemovedIndices.rangeView {
|
||||
transaction.removeThreadIndexHole(peerId: discussionMessage.messageId.peerId, threadId: makeMessageThreadId(discussionMessage.messageId), namespace: Namespaces.Message.Cloud, space: .everywhere, range: Int32(range.lowerBound) ... Int32(range.upperBound))
|
||||
if let initialFilledHoles = initialFilledHoles {
|
||||
for range in initialFilledHoles.strictRemovedIndices.rangeView {
|
||||
transaction.removeThreadIndexHole(peerId: discussionMessage.messageId.peerId, threadId: makeMessageThreadId(discussionMessage.messageId), namespace: Namespaces.Message.Cloud, space: .everywhere, range: Int32(range.lowerBound) ... Int32(range.upperBound))
|
||||
}
|
||||
}
|
||||
|
||||
return .single(ChatReplyThreadMessage(
|
||||
@ -710,8 +717,9 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
|
||||
maxMessage: discussionMessage.maxMessage,
|
||||
maxReadIncomingMessageId: discussionMessage.maxReadIncomingMessageId,
|
||||
maxReadOutgoingMessageId: discussionMessage.maxReadOutgoingMessageId,
|
||||
initialFilledHoles: initialFilledHoles.removedIndices,
|
||||
initialAnchor: initialAnchor
|
||||
initialFilledHoles: initialFilledHoles?.removedIndices ?? IndexSet(),
|
||||
initialAnchor: initialAnchor,
|
||||
isNotAvailable: initialFilledHoles == nil
|
||||
))
|
||||
}
|
||||
|> castError(FetchChannelReplyThreadMessageError.self)
|
||||
|
@ -4,18 +4,24 @@ import SwiftSignalKit
|
||||
|
||||
import SyncCore
|
||||
|
||||
public func updatePeerChatInterfaceState(account: Account, peerId: PeerId, state: SynchronizeableChatInterfaceState) -> Signal<Void, NoError> {
|
||||
public func updatePeerChatInterfaceState(account: Account, peerId: PeerId, threadId: Int64?, state: SynchronizeableChatInterfaceState) -> Signal<Void, NoError> {
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
let currentInputState = (transaction.getPeerChatInterfaceState(peerId) as? SynchronizeableChatInterfaceState)?.synchronizeableInputState
|
||||
let updatedInputState = state.synchronizeableInputState
|
||||
if let threadId = threadId {
|
||||
transaction.updatePeerChatThreadInterfaceState(peerId, threadId: threadId, update: { _ in
|
||||
return state
|
||||
})
|
||||
} else {
|
||||
let currentInputState = (transaction.getPeerChatInterfaceState(peerId) as? SynchronizeableChatInterfaceState)?.synchronizeableInputState
|
||||
let updatedInputState = state.synchronizeableInputState
|
||||
|
||||
if currentInputState != updatedInputState {
|
||||
if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudChannel || peerId.namespace == Namespaces.Peer.CloudGroup {
|
||||
addSynchronizeChatInputStateOperation(transaction: transaction, peerId: peerId)
|
||||
if currentInputState != updatedInputState {
|
||||
if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudChannel || peerId.namespace == Namespaces.Peer.CloudGroup {
|
||||
addSynchronizeChatInputStateOperation(transaction: transaction, peerId: peerId)
|
||||
}
|
||||
}
|
||||
transaction.updatePeerChatInterfaceState(peerId, update: { _ in
|
||||
return state
|
||||
})
|
||||
}
|
||||
transaction.updatePeerChatInterfaceState(peerId, update: { _ in
|
||||
return state
|
||||
})
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -17,11 +17,13 @@ private let messageBoldItalicFont = Font.semiboldItalic(17.0)
|
||||
private let messageFixedFont = UIFont(name: "Menlo-Regular", size: 16.0) ?? UIFont.systemFont(ofSize: 17.0)
|
||||
|
||||
final class ChatBotInfoItem: ListViewItem {
|
||||
fileprivate let title: String
|
||||
fileprivate let text: String
|
||||
fileprivate let controllerInteraction: ChatControllerInteraction
|
||||
fileprivate let presentationData: ChatPresentationData
|
||||
|
||||
init(text: String, controllerInteraction: ChatControllerInteraction, presentationData: ChatPresentationData) {
|
||||
init(title: String, text: String, controllerInteraction: ChatControllerInteraction, presentationData: ChatPresentationData) {
|
||||
self.title = title
|
||||
self.text = text
|
||||
self.controllerInteraction = controllerInteraction
|
||||
self.presentationData = presentationData
|
||||
@ -163,7 +165,7 @@ final class ChatBotInfoItemNode: ListViewItemNode {
|
||||
let verticalItemInset: CGFloat = 10.0
|
||||
let verticalContentInset: CGFloat = 8.0
|
||||
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Bot_DescriptionTitle, font: messageBoldFont, textColor: item.presentationData.theme.theme.chat.message.infoPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - horizontalEdgeInset * 2.0 - horizontalContentInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.title, font: messageBoldFont, textColor: item.presentationData.theme.theme.chat.message.infoPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - horizontalEdgeInset * 2.0 - horizontalContentInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - horizontalEdgeInset * 2.0 - horizontalContentInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
@ -174,7 +176,7 @@ final class ChatBotInfoItemNode: ListViewItemNode {
|
||||
let titleFrame = CGRect(origin: CGPoint(x: backgroundFrame.origin.x + horizontalContentInset, y: backgroundFrame.origin.y + verticalContentInset), size: titleLayout.size)
|
||||
let textFrame = CGRect(origin: CGPoint(x: backgroundFrame.origin.x + horizontalContentInset, y: backgroundFrame.origin.y + verticalContentInset + titleLayout.size.height + textSpacing), size: textLayout.size)
|
||||
|
||||
let itemLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: textLayout.size.height + verticalItemInset * 2.0 + verticalContentInset * 2.0 + 4.0), insets: UIEdgeInsets())
|
||||
let itemLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: textLayout.size.height + verticalItemInset * 2.0 + verticalContentInset * 2.0 + 18.0), insets: UIEdgeInsets())
|
||||
return (itemLayout, { _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.theme = item.presentationData.theme
|
||||
|
@ -3231,14 +3231,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
guard let strongSelf = self, let combinedInitialData = combinedInitialData else {
|
||||
return
|
||||
}
|
||||
if var interfaceState = combinedInitialData.initialData?.chatInterfaceState as? ChatInterfaceState {
|
||||
switch strongSelf.chatLocation {
|
||||
case .peer:
|
||||
break
|
||||
default:
|
||||
interfaceState = ChatInterfaceState()
|
||||
}
|
||||
|
||||
if let interfaceState = combinedInitialData.initialData?.chatInterfaceState as? ChatInterfaceState {
|
||||
var pinnedMessageId: MessageId?
|
||||
var peerIsBlocked: Bool = false
|
||||
var callsAvailable: Bool = true
|
||||
@ -5715,16 +5708,24 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
private func saveInterfaceState(includeScrollState: Bool = true) {
|
||||
if case let .peer(peerId) = self.chatLocation {
|
||||
let timestamp = Int32(Date().timeIntervalSince1970)
|
||||
var interfaceState = self.presentationInterfaceState.interfaceState.withUpdatedTimestamp(timestamp)
|
||||
if includeScrollState {
|
||||
let scrollState = self.chatDisplayNode.historyNode.immediateScrollState()
|
||||
interfaceState = interfaceState.withUpdatedHistoryScrollState(scrollState)
|
||||
}
|
||||
interfaceState = interfaceState.withUpdatedInputLanguage(self.chatDisplayNode.currentTextInputLanguage)
|
||||
let _ = updatePeerChatInterfaceState(account: self.context.account, peerId: peerId, state: interfaceState).start()
|
||||
var peerId: PeerId
|
||||
var threadId: Int64?
|
||||
switch self.chatLocation {
|
||||
case let .peer(peerIdValue):
|
||||
peerId = peerIdValue
|
||||
case let .replyThread(replyThreadMessage):
|
||||
peerId = replyThreadMessage.messageId.peerId
|
||||
threadId = makeMessageThreadId(replyThreadMessage.messageId)
|
||||
}
|
||||
|
||||
let timestamp = Int32(Date().timeIntervalSince1970)
|
||||
var interfaceState = self.presentationInterfaceState.interfaceState.withUpdatedTimestamp(timestamp)
|
||||
if includeScrollState && threadId == nil {
|
||||
let scrollState = self.chatDisplayNode.historyNode.immediateScrollState()
|
||||
interfaceState = interfaceState.withUpdatedHistoryScrollState(scrollState)
|
||||
}
|
||||
interfaceState = interfaceState.withUpdatedInputLanguage(self.chatDisplayNode.currentTextInputLanguage)
|
||||
let _ = updatePeerChatInterfaceState(account: self.context.account, peerId: peerId, threadId: threadId, state: interfaceState).start()
|
||||
}
|
||||
|
||||
override public func viewDidDisappear(_ animated: Bool) {
|
||||
@ -8359,7 +8360,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
controllerInteraction.currentMessageWithLoadingReplyThread = displayProgressInMessage
|
||||
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(displayProgressInMessage)
|
||||
if let previousId = previousId {
|
||||
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(displayProgressInMessage)
|
||||
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(previousId)
|
||||
}
|
||||
}
|
||||
|
||||
@ -8368,7 +8369,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
guard let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction else {
|
||||
return
|
||||
}
|
||||
if let displayProgressInMessage = displayProgressInMessage, controllerInteraction.currentMessageWithLoadingReplyThread == messageId {
|
||||
if let displayProgressInMessage = displayProgressInMessage, controllerInteraction.currentMessageWithLoadingReplyThread == displayProgressInMessage {
|
||||
controllerInteraction.currentMessageWithLoadingReplyThread = nil
|
||||
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(displayProgressInMessage)
|
||||
}
|
||||
|
@ -1964,7 +1964,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
if let peer = chatPresentationInterfaceState.renderedPeer?.peer, let restrictionTextValue = peer.restrictionText(platform: "ios", contentSettings: self.context.currentContentSettings.with { $0 }), !restrictionTextValue.isEmpty {
|
||||
restrictionText = restrictionTextValue
|
||||
} else if chatPresentationInterfaceState.isNotAccessible {
|
||||
if let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, case .broadcast = peer.info {
|
||||
if case .replyThread = self.chatLocation {
|
||||
restrictionText = chatPresentationInterfaceState.strings.CommentsGroup_ErrorAccessDenied
|
||||
} else if let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, case .broadcast = peer.info {
|
||||
restrictionText = chatPresentationInterfaceState.strings.Channel_ErrorAccessDenied
|
||||
} else {
|
||||
restrictionText = chatPresentationInterfaceState.strings.Group_ErrorAccessDenied
|
||||
|
@ -182,7 +182,7 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
|
||||
}
|
||||
|
||||
if includeChatInfoEntry {
|
||||
if view.earlierId == nil {
|
||||
if view.earlierId == nil, !view.isLoading {
|
||||
var cachedPeerData: CachedPeerData?
|
||||
for entry in view.additionalData {
|
||||
if case let .cachedPeerData(_, data) = entry {
|
||||
@ -190,8 +190,10 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
|
||||
break
|
||||
}
|
||||
}
|
||||
if let cachedPeerData = cachedPeerData as? CachedUserData, let botInfo = cachedPeerData.botInfo, !botInfo.description.isEmpty {
|
||||
entries.insert(.ChatInfoEntry(botInfo.description, presentationData), at: 0)
|
||||
if case let .peer(peerId) = location, peerId.isReplies {
|
||||
entries.insert(.ChatInfoEntry(presentationData.strings.RepliesChat_DescriptionTitle, presentationData.strings.RepliesChat_DescriptionText, presentationData), at: 0)
|
||||
} else if let cachedPeerData = cachedPeerData as? CachedUserData, let botInfo = cachedPeerData.botInfo, !botInfo.description.isEmpty {
|
||||
entries.insert(.ChatInfoEntry(presentationData.strings.Bot_DescriptionTitle, botInfo.description, presentationData), at: 0)
|
||||
} else {
|
||||
var isEmpty = true
|
||||
if entries.count <= 3 {
|
||||
|
@ -38,7 +38,7 @@ enum ChatHistoryEntry: Identifiable, Comparable {
|
||||
case MessageGroupEntry(MessageGroupInfo, [(Message, Bool, ChatHistoryMessageSelection, ChatMessageEntryAttributes)], ChatPresentationData)
|
||||
case UnreadEntry(MessageIndex, ChatPresentationData)
|
||||
case ReplyCountEntry(MessageIndex, Bool, Int, ChatPresentationData)
|
||||
case ChatInfoEntry(String, ChatPresentationData)
|
||||
case ChatInfoEntry(String, String, ChatPresentationData)
|
||||
case SearchEntry(PresentationTheme, PresentationStrings)
|
||||
|
||||
var stableId: UInt64 {
|
||||
@ -193,8 +193,8 @@ enum ChatHistoryEntry: Identifiable, Comparable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .ChatInfoEntry(lhsText, lhsPresentationData):
|
||||
if case let .ChatInfoEntry(rhsText, rhsPresentationData) = rhs, lhsText == rhsText, lhsPresentationData === rhsPresentationData {
|
||||
case let .ChatInfoEntry(lhsTitle, lhsText, lhsPresentationData):
|
||||
if case let .ChatInfoEntry(rhsTitle, rhsText, rhsPresentationData) = rhs, lhsTitle == rhsTitle, lhsText == rhsText, lhsPresentationData === rhsPresentationData {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
|
@ -277,8 +277,8 @@ private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLoca
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatUnreadItem(index: entry.entry.index, presentationData: presentationData, context: context), directionHint: entry.directionHint)
|
||||
case let .ReplyCountEntry(_, isComments, count, presentationData):
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatReplyCountItem(index: entry.entry.index, isComments: isComments, count: count, presentationData: presentationData, context: context), directionHint: entry.directionHint)
|
||||
case let .ChatInfoEntry(text, presentationData):
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(text: text, controllerInteraction: controllerInteraction, presentationData: presentationData), directionHint: entry.directionHint)
|
||||
case let .ChatInfoEntry(title, text, presentationData):
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(title: title, text: text, controllerInteraction: controllerInteraction, presentationData: presentationData), directionHint: entry.directionHint)
|
||||
case let .SearchEntry(theme, strings):
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(theme: theme, placeholder: strings.Common_Search, activate: {
|
||||
controllerInteraction.openSearch()
|
||||
@ -322,8 +322,8 @@ private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLoca
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatUnreadItem(index: entry.entry.index, presentationData: presentationData, context: context), directionHint: entry.directionHint)
|
||||
case let .ReplyCountEntry(_, isComments, count, presentationData):
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatReplyCountItem(index: entry.entry.index, isComments: isComments, count: count, presentationData: presentationData, context: context), directionHint: entry.directionHint)
|
||||
case let .ChatInfoEntry(text, presentationData):
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(text: text, controllerInteraction: controllerInteraction, presentationData: presentationData), directionHint: entry.directionHint)
|
||||
case let .ChatInfoEntry(title, text, presentationData):
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(title: title, text: text, controllerInteraction: controllerInteraction, presentationData: presentationData), directionHint: entry.directionHint)
|
||||
case let .SearchEntry(theme, strings):
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(theme: theme, placeholder: strings.Common_Search, activate: {
|
||||
controllerInteraction.openSearch()
|
||||
|
@ -348,6 +348,16 @@ func fetchAndPreloadReplyThreadInfo(context: AccountContext, subject: ReplyThrea
|
||||
scrollToLowerBoundMessage = index
|
||||
}
|
||||
|
||||
if replyThreadMessage.isNotAvailable {
|
||||
return .single(ReplyThreadInfo(
|
||||
message: replyThreadMessage,
|
||||
isChannelPost: replyThreadMessage.isChannelPost,
|
||||
isEmpty: false,
|
||||
scrollToLowerBoundMessage: nil,
|
||||
contextHolder: chatLocationContextHolder
|
||||
))
|
||||
}
|
||||
|
||||
let preloadSignal = preloadedChatHistoryViewForLocation(
|
||||
input,
|
||||
context: context,
|
||||
|
Binary file not shown.
@ -449,12 +449,12 @@ public final class WalletStrings: Equatable {
|
||||
public var Wallet_Send_ConfirmationConfirm: String { return self._s[218]! }
|
||||
public var Wallet_Created_ExportErrorTitle: String { return self._s[219]! }
|
||||
public var Wallet_Info_TransactionPendingHeader: String { return self._s[220]! }
|
||||
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
|
||||
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
|
||||
let form = getPluralizationForm(self.lc, value)
|
||||
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
||||
return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, stringValue)
|
||||
}
|
||||
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
|
||||
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
|
||||
let form = getPluralizationForm(self.lc, value)
|
||||
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
||||
return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue)
|
||||
|
Loading…
x
Reference in New Issue
Block a user