mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
fe406cd27c
@ -229,7 +229,6 @@ static int32_t fixedTimeDifferenceValue = 0;
|
|||||||
_useTempAuthKeys = useTempAuthKeys;
|
_useTempAuthKeys = useTempAuthKeys;
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
_tempKeyExpiration = 1 * 60 * 60;
|
_tempKeyExpiration = 1 * 60 * 60;
|
||||||
_tempKeyExpiration = 5;
|
|
||||||
#else
|
#else
|
||||||
_tempKeyExpiration = 24 * 60 * 60;
|
_tempKeyExpiration = 24 * 60 * 60;
|
||||||
#endif
|
#endif
|
||||||
|
@ -1105,7 +1105,7 @@ func debugRestoreState(basePath:String, name: String) {
|
|||||||
|
|
||||||
private let sharedQueue = Queue(name: "org.telegram.postbox.Postbox")
|
private let sharedQueue = Queue(name: "org.telegram.postbox.Postbox")
|
||||||
|
|
||||||
public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration, encryptionParameters: ValueBoxEncryptionParameters) -> Signal<PostboxResult, NoError> {
|
public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration, encryptionParameters: ValueBoxEncryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32) -> Signal<PostboxResult, NoError> {
|
||||||
let queue = sharedQueue
|
let queue = sharedQueue
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
queue.async {
|
queue.async {
|
||||||
@ -1177,7 +1177,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration,
|
|||||||
let endTime = CFAbsoluteTimeGetCurrent()
|
let endTime = CFAbsoluteTimeGetCurrent()
|
||||||
print("Postbox load took \((endTime - startTime) * 1000.0) ms")
|
print("Postbox load took \((endTime - startTime) * 1000.0) ms")
|
||||||
|
|
||||||
subscriber.putNext(.postbox(Postbox(queue: queue, basePath: basePath, seedConfiguration: seedConfiguration, valueBox: valueBox)))
|
subscriber.putNext(.postbox(Postbox(queue: queue, basePath: basePath, seedConfiguration: seedConfiguration, valueBox: valueBox, timestampForAbsoluteTimeBasedOperations: timestampForAbsoluteTimeBasedOperations)))
|
||||||
subscriber.putCompletion()
|
subscriber.putCompletion()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1330,7 +1330,7 @@ public final class Postbox {
|
|||||||
|
|
||||||
var installedMessageActionsByPeerId: [PeerId: Bag<([StoreMessage], Transaction) -> Void>] = [:]
|
var installedMessageActionsByPeerId: [PeerId: Bag<([StoreMessage], Transaction) -> Void>] = [:]
|
||||||
|
|
||||||
init(queue: Queue, basePath: String, seedConfiguration: SeedConfiguration, valueBox: SqliteValueBox) {
|
init(queue: Queue, basePath: String, seedConfiguration: SeedConfiguration, valueBox: SqliteValueBox, timestampForAbsoluteTimeBasedOperations: Int32) {
|
||||||
assert(queue.isCurrent())
|
assert(queue.isCurrent())
|
||||||
|
|
||||||
let startTime = CFAbsoluteTimeGetCurrent()
|
let startTime = CFAbsoluteTimeGetCurrent()
|
||||||
@ -1534,6 +1534,9 @@ public final class Postbox {
|
|||||||
for id in self.messageHistoryUnsentTable.get() {
|
for id in self.messageHistoryUnsentTable.get() {
|
||||||
transaction.updateMessage(id, update: { message in
|
transaction.updateMessage(id, update: { message in
|
||||||
if !message.flags.contains(.Failed) {
|
if !message.flags.contains(.Failed) {
|
||||||
|
if message.timestamp + 60 * 10 > timestampForAbsoluteTimeBasedOperations {
|
||||||
|
return .skip
|
||||||
|
}
|
||||||
var flags = StoreMessageFlags(message.flags)
|
var flags = StoreMessageFlags(message.flags)
|
||||||
flags.remove(.Unsent)
|
flags.remove(.Unsent)
|
||||||
flags.remove(.Sending)
|
flags.remove(.Sending)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import Foundation
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import Postbox
|
import Postbox
|
||||||
|
|
||||||
@ -67,7 +68,7 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = {
|
|||||||
|
|
||||||
public func accountTransaction<T>(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters, transaction: @escaping (Transaction) -> T) -> Signal<T, NoError> {
|
public func accountTransaction<T>(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters, transaction: @escaping (Transaction) -> T) -> Signal<T, NoError> {
|
||||||
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
|
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
|
||||||
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters)
|
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970))
|
||||||
return postbox
|
return postbox
|
||||||
|> mapToSignal { value -> Signal<T, NoError> in
|
|> mapToSignal { value -> Signal<T, NoError> in
|
||||||
switch value {
|
switch value {
|
||||||
|
@ -160,7 +160,7 @@ public enum AccountPreferenceEntriesResult {
|
|||||||
|
|
||||||
public func accountPreferenceEntries(rootPath: String, id: AccountRecordId, keys: Set<ValueBoxKey>, encryptionParameters: ValueBoxEncryptionParameters) -> Signal<AccountPreferenceEntriesResult, NoError> {
|
public func accountPreferenceEntries(rootPath: String, id: AccountRecordId, keys: Set<ValueBoxKey>, encryptionParameters: ValueBoxEncryptionParameters) -> Signal<AccountPreferenceEntriesResult, NoError> {
|
||||||
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
|
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
|
||||||
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters)
|
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970))
|
||||||
return postbox
|
return postbox
|
||||||
|> mapToSignal { value -> Signal<AccountPreferenceEntriesResult, NoError> in
|
|> mapToSignal { value -> Signal<AccountPreferenceEntriesResult, NoError> in
|
||||||
switch value {
|
switch value {
|
||||||
@ -187,7 +187,7 @@ public enum AccountNoticeEntriesResult {
|
|||||||
|
|
||||||
public func accountNoticeEntries(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters) -> Signal<AccountNoticeEntriesResult, NoError> {
|
public func accountNoticeEntries(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters) -> Signal<AccountNoticeEntriesResult, NoError> {
|
||||||
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
|
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
|
||||||
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters)
|
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970))
|
||||||
return postbox
|
return postbox
|
||||||
|> mapToSignal { value -> Signal<AccountNoticeEntriesResult, NoError> in
|
|> mapToSignal { value -> Signal<AccountNoticeEntriesResult, NoError> in
|
||||||
switch value {
|
switch value {
|
||||||
@ -208,7 +208,7 @@ public enum LegacyAccessChallengeDataResult {
|
|||||||
|
|
||||||
public func accountLegacyAccessChallengeData(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters) -> Signal<LegacyAccessChallengeDataResult, NoError> {
|
public func accountLegacyAccessChallengeData(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters) -> Signal<LegacyAccessChallengeDataResult, NoError> {
|
||||||
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
|
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
|
||||||
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters)
|
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970))
|
||||||
return postbox
|
return postbox
|
||||||
|> mapToSignal { value -> Signal<LegacyAccessChallengeDataResult, NoError> in
|
|> mapToSignal { value -> Signal<LegacyAccessChallengeDataResult, NoError> in
|
||||||
switch value {
|
switch value {
|
||||||
@ -225,7 +225,7 @@ public func accountLegacyAccessChallengeData(rootPath: String, id: AccountRecord
|
|||||||
public func accountWithId(accountManager: AccountManager, networkArguments: NetworkInitializationArguments, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters, supplementary: Bool, rootPath: String, beginWithTestingEnvironment: Bool, backupData: AccountBackupData?, auxiliaryMethods: AccountAuxiliaryMethods, shouldKeepAutoConnection: Bool = true) -> Signal<AccountResult, NoError> {
|
public func accountWithId(accountManager: AccountManager, networkArguments: NetworkInitializationArguments, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters, supplementary: Bool, rootPath: String, beginWithTestingEnvironment: Bool, backupData: AccountBackupData?, auxiliaryMethods: AccountAuxiliaryMethods, shouldKeepAutoConnection: Bool = true) -> Signal<AccountResult, NoError> {
|
||||||
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
|
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
|
||||||
|
|
||||||
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters)
|
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970))
|
||||||
|
|
||||||
return postbox
|
return postbox
|
||||||
|> mapToSignal { result -> Signal<AccountResult, NoError> in
|
|> mapToSignal { result -> Signal<AccountResult, NoError> in
|
||||||
|
@ -209,7 +209,7 @@ public func temporaryAccount(manager: AccountManager, rootPath: String, encrypti
|
|||||||
return manager.allocatedTemporaryAccountId()
|
return manager.allocatedTemporaryAccountId()
|
||||||
|> mapToSignal { id -> Signal<TemporaryAccount, NoError> in
|
|> mapToSignal { id -> Signal<TemporaryAccount, NoError> in
|
||||||
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
|
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
|
||||||
return openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters)
|
return openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970))
|
||||||
|> mapToSignal { result -> Signal<TemporaryAccount, NoError> in
|
|> mapToSignal { result -> Signal<TemporaryAccount, NoError> in
|
||||||
switch result {
|
switch result {
|
||||||
case .upgrading:
|
case .upgrading:
|
||||||
|
@ -291,6 +291,7 @@ public final class AccountViewTracker {
|
|||||||
private var seenLiveLocationDisposables = DisposableDict<Int32>()
|
private var seenLiveLocationDisposables = DisposableDict<Int32>()
|
||||||
|
|
||||||
private var updatedUnsupportedMediaMessageIdsAndTimestamps: [MessageId: Int32] = [:]
|
private var updatedUnsupportedMediaMessageIdsAndTimestamps: [MessageId: Int32] = [:]
|
||||||
|
private var refreshSecretChatMediaMessageIdsAndTimestamps: [MessageId: Int32] = [:]
|
||||||
private var nextUpdatedUnsupportedMediaDisposableId: Int32 = 0
|
private var nextUpdatedUnsupportedMediaDisposableId: Int32 = 0
|
||||||
private var updatedUnsupportedMediaDisposables = DisposableDict<Int32>()
|
private var updatedUnsupportedMediaDisposables = DisposableDict<Int32>()
|
||||||
|
|
||||||
@ -631,6 +632,18 @@ public final class AccountViewTracker {
|
|||||||
|> runOn(self.queue)
|
|> runOn(self.queue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func updateReplyInfoForMessageId(_ id: MessageId, info: UpdatedMessageReplyInfo) {
|
||||||
|
self.queue.async { [weak self] in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let current = strongSelf.updatedViewCountMessageIdsAndTimestamps[id] else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.updatedViewCountMessageIdsAndTimestamps[id] = ViewCountContextState(timestamp: Int32(CFAbsoluteTimeGetCurrent()), clientId: current.clientId, result: ViewCountContextState.ReplyInfo(commentsPeerId: info.commentsPeerId, maxReadIncomingMessageId: info.maxReadIncomingMessageId, maxMessageId: info.maxMessageId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func updateViewCountForMessageIds(messageIds: Set<MessageId>, clientId: Int32) {
|
public func updateViewCountForMessageIds(messageIds: Set<MessageId>, clientId: Int32) {
|
||||||
self.queue.async {
|
self.queue.async {
|
||||||
var addedMessageIds: [MessageId] = []
|
var addedMessageIds: [MessageId] = []
|
||||||
@ -1037,6 +1050,98 @@ public final class AccountViewTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func refreshSecretMediaMediaForMessageIds(messageIds: Set<MessageId>) {
|
||||||
|
self.queue.async {
|
||||||
|
var addedMessageIds: [MessageId] = []
|
||||||
|
let timestamp = Int32(CFAbsoluteTimeGetCurrent())
|
||||||
|
for messageId in messageIds {
|
||||||
|
let messageTimestamp = self.refreshSecretChatMediaMessageIdsAndTimestamps[messageId]
|
||||||
|
if messageTimestamp == nil {
|
||||||
|
self.refreshSecretChatMediaMessageIdsAndTimestamps[messageId] = timestamp
|
||||||
|
addedMessageIds.append(messageId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !addedMessageIds.isEmpty {
|
||||||
|
for (_, messageIds) in messagesIdsGroupedByPeerId(Set(addedMessageIds)) {
|
||||||
|
let disposableId = self.nextUpdatedUnsupportedMediaDisposableId
|
||||||
|
self.nextUpdatedUnsupportedMediaDisposableId += 1
|
||||||
|
|
||||||
|
if let account = self.account {
|
||||||
|
let signal = account.postbox.transaction { transaction -> [TelegramMediaFile] in
|
||||||
|
var result: [TelegramMediaFile] = []
|
||||||
|
for id in messageIds {
|
||||||
|
if let message = transaction.getMessage(id) {
|
||||||
|
for media in message.media {
|
||||||
|
if let file = media as? TelegramMediaFile, file.isAnimatedSticker {
|
||||||
|
result.append(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|> mapToSignal { files -> Signal<Void, NoError> in
|
||||||
|
guard !files.isEmpty else {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
|
||||||
|
var stickerPacks = Set<StickerPackReference>()
|
||||||
|
for file in files {
|
||||||
|
for attribute in file.attributes {
|
||||||
|
if case let .Sticker(_, packReferenceValue, _) = attribute, let packReference = packReferenceValue {
|
||||||
|
if case .id = packReference {
|
||||||
|
stickerPacks.insert(packReference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var requests: [Signal<Api.messages.StickerSet?, NoError>] = []
|
||||||
|
for reference in stickerPacks {
|
||||||
|
if case let .id(id, accessHash) = reference {
|
||||||
|
requests.append(account.network.request(Api.functions.messages.getStickerSet(stickerset: .inputStickerSetID(id: id, accessHash: accessHash)))
|
||||||
|
|> map(Optional.init)
|
||||||
|
|> `catch` { _ -> Signal<Api.messages.StickerSet?, NoError> in
|
||||||
|
return .single(nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if requests.isEmpty {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
|
||||||
|
return combineLatest(requests)
|
||||||
|
|> mapToSignal { results -> Signal<Void, NoError> in
|
||||||
|
return account.postbox.transaction { transaction -> Void in
|
||||||
|
for result in results {
|
||||||
|
switch result {
|
||||||
|
case let .stickerSet(_, _, documents):
|
||||||
|
for document in documents {
|
||||||
|
if let file = telegramMediaFileFromApiDocument(document) {
|
||||||
|
if transaction.getMedia(file.fileId) != nil {
|
||||||
|
let _ = transaction.updateMedia(file.fileId, update: file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> afterDisposed { [weak self] in
|
||||||
|
self?.queue.async {
|
||||||
|
self?.updatedUnsupportedMediaDisposables.set(nil, forKey: disposableId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.updatedUnsupportedMediaDisposables.set(signal.start(), forKey: disposableId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func updateMarkAllMentionsSeen(peerId: PeerId) {
|
public func updateMarkAllMentionsSeen(peerId: PeerId) {
|
||||||
self.queue.async {
|
self.queue.async {
|
||||||
guard let account = self.account else {
|
guard let account = self.account else {
|
||||||
|
@ -130,6 +130,21 @@ private func requestActivity(postbox: Postbox, network: Network, accountPeerId:
|
|||||||
if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
|
if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
|
if let _ = peer as? TelegramUser {
|
||||||
|
if let presence = transaction.getPeerPresence(peerId: peerId) as? TelegramUserPresence {
|
||||||
|
switch presence.status {
|
||||||
|
case .none, .recently, .lastWeek, .lastMonth:
|
||||||
|
return .complete()
|
||||||
|
case let .present(statusTimestamp):
|
||||||
|
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||||
|
if statusTimestamp < timestamp {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let inputPeer = apiInputPeer(peer) {
|
if let inputPeer = apiInputPeer(peer) {
|
||||||
var flags: Int32 = 0
|
var flags: Int32 = 0
|
||||||
|
@ -145,10 +145,12 @@ private class ReplyThreadHistoryContextImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var channelMessageId: MessageId?
|
var channelMessageId: MessageId?
|
||||||
|
var replyThreadAttribute: ReplyThreadMessageAttribute?
|
||||||
for attribute in topMessage.attributes {
|
for attribute in topMessage.attributes {
|
||||||
if let attribute = attribute as? SourceReferenceMessageAttribute {
|
if let attribute = attribute as? SourceReferenceMessageAttribute {
|
||||||
channelMessageId = attribute.messageId
|
channelMessageId = attribute.messageId
|
||||||
break
|
} else if let attribute = attribute as? ReplyThreadMessageAttribute {
|
||||||
|
replyThreadAttribute = attribute
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,14 +197,41 @@ private class ReplyThreadHistoryContextImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let maxReadIncomingMessageId = readInboxMaxId.flatMap { readMaxId in
|
||||||
|
MessageId(peerId: parsedIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: readMaxId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let channelMessageId = channelMessageId, let replyThreadAttribute = replyThreadAttribute {
|
||||||
|
account.viewTracker.updateReplyInfoForMessageId(channelMessageId, info: AccountViewTracker.UpdatedMessageReplyInfo(
|
||||||
|
timestamp: Int32(CFAbsoluteTimeGetCurrent()),
|
||||||
|
commentsPeerId: parsedIndex.id.peerId,
|
||||||
|
maxReadIncomingMessageId: maxReadIncomingMessageId,
|
||||||
|
maxMessageId: resolvedMaxMessage
|
||||||
|
))
|
||||||
|
|
||||||
|
transaction.updateMessage(channelMessageId, update: { currentMessage in
|
||||||
|
var attributes = currentMessage.attributes
|
||||||
|
loop: for j in 0 ..< attributes.count {
|
||||||
|
if let attribute = attributes[j] as? ReplyThreadMessageAttribute {
|
||||||
|
attributes[j] = ReplyThreadMessageAttribute(
|
||||||
|
count: replyThreadAttribute.count,
|
||||||
|
latestUsers: attribute.latestUsers,
|
||||||
|
commentsPeerId: attribute.commentsPeerId,
|
||||||
|
maxMessageId: replyThreadAttribute.maxMessageId,
|
||||||
|
maxReadMessageId: replyThreadAttribute.maxReadMessageId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return .single(DiscussionMessage(
|
return .single(DiscussionMessage(
|
||||||
messageId: parsedIndex.id,
|
messageId: parsedIndex.id,
|
||||||
channelMessageId: channelMessageId,
|
channelMessageId: channelMessageId,
|
||||||
isChannelPost: isChannelPost,
|
isChannelPost: isChannelPost,
|
||||||
maxMessage: resolvedMaxMessage,
|
maxMessage: resolvedMaxMessage,
|
||||||
maxReadIncomingMessageId: readInboxMaxId.flatMap { readMaxId in
|
maxReadIncomingMessageId: maxReadIncomingMessageId,
|
||||||
MessageId(peerId: parsedIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: readMaxId)
|
|
||||||
},
|
|
||||||
maxReadOutgoingMessageId: readOutboxMaxId.flatMap { readMaxId in
|
maxReadOutgoingMessageId: readOutboxMaxId.flatMap { readMaxId in
|
||||||
MessageId(peerId: parsedIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: readMaxId)
|
MessageId(peerId: parsedIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: readMaxId)
|
||||||
}
|
}
|
||||||
|
@ -3068,6 +3068,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
var wasInForeground = true
|
||||||
self.applicationInForegroundDisposable = (context.sharedContext.applicationBindings.applicationInForeground
|
self.applicationInForegroundDisposable = (context.sharedContext.applicationBindings.applicationInForeground
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
|> deliverOn(Queue.mainQueue())).start(next: { [weak self] value in
|
|> deliverOn(Queue.mainQueue())).start(next: { [weak self] value in
|
||||||
@ -3077,7 +3078,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
strongSelf.raiseToListen?.applicationResignedActive()
|
strongSelf.raiseToListen?.applicationResignedActive()
|
||||||
|
|
||||||
strongSelf.stopMediaRecorder()
|
strongSelf.stopMediaRecorder()
|
||||||
|
} else {
|
||||||
|
if !wasInForeground {
|
||||||
|
strongSelf.chatDisplayNode.recursivelyEnsureDisplaySynchronously(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
wasInForeground = value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -514,6 +514,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
private let messageReactionsProcessingManager = ChatMessageThrottledProcessingManager()
|
private let messageReactionsProcessingManager = ChatMessageThrottledProcessingManager()
|
||||||
private let seenLiveLocationProcessingManager = ChatMessageThrottledProcessingManager()
|
private let seenLiveLocationProcessingManager = ChatMessageThrottledProcessingManager()
|
||||||
private let unsupportedMessageProcessingManager = ChatMessageThrottledProcessingManager()
|
private let unsupportedMessageProcessingManager = ChatMessageThrottledProcessingManager()
|
||||||
|
private let refreshMediaProcessingManager = ChatMessageThrottledProcessingManager()
|
||||||
private let messageMentionProcessingManager = ChatMessageThrottledProcessingManager(delay: 0.2)
|
private let messageMentionProcessingManager = ChatMessageThrottledProcessingManager(delay: 0.2)
|
||||||
let prefetchManager: InChatPrefetchManager
|
let prefetchManager: InChatPrefetchManager
|
||||||
private var currentEarlierPrefetchMessages: [(Message, Media)] = []
|
private var currentEarlierPrefetchMessages: [(Message, Media)] = []
|
||||||
@ -605,6 +606,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
self.unsupportedMessageProcessingManager.process = { [weak context] messageIds in
|
self.unsupportedMessageProcessingManager.process = { [weak context] messageIds in
|
||||||
context?.account.viewTracker.updateUnsupportedMediaForMessageIds(messageIds: messageIds)
|
context?.account.viewTracker.updateUnsupportedMediaForMessageIds(messageIds: messageIds)
|
||||||
}
|
}
|
||||||
|
self.refreshMediaProcessingManager.process = { [weak context] messageIds in
|
||||||
|
context?.account.viewTracker.refreshSecretMediaMediaForMessageIds(messageIds: messageIds)
|
||||||
|
}
|
||||||
self.messageMentionProcessingManager.process = { [weak context] messageIds in
|
self.messageMentionProcessingManager.process = { [weak context] messageIds in
|
||||||
context?.account.viewTracker.updateMarkMentionsSeenForMessageIds(messageIds: messageIds)
|
context?.account.viewTracker.updateMarkMentionsSeenForMessageIds(messageIds: messageIds)
|
||||||
}
|
}
|
||||||
@ -1174,6 +1178,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
var messageIdsWithUpdateableReactions: [MessageId] = []
|
var messageIdsWithUpdateableReactions: [MessageId] = []
|
||||||
var messageIdsWithLiveLocation: [MessageId] = []
|
var messageIdsWithLiveLocation: [MessageId] = []
|
||||||
var messageIdsWithUnsupportedMedia: [MessageId] = []
|
var messageIdsWithUnsupportedMedia: [MessageId] = []
|
||||||
|
var messageIdsWithRefreshMedia: [MessageId] = []
|
||||||
var messageIdsWithUnseenPersonalMention: [MessageId] = []
|
var messageIdsWithUnseenPersonalMention: [MessageId] = []
|
||||||
var messagesWithPreloadableMediaToEarlier: [(Message, Media)] = []
|
var messagesWithPreloadableMediaToEarlier: [(Message, Media)] = []
|
||||||
var messagesWithPreloadableMediaToLater: [(Message, Media)] = []
|
var messagesWithPreloadableMediaToLater: [(Message, Media)] = []
|
||||||
@ -1192,6 +1197,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var contentRequiredValidation = false
|
var contentRequiredValidation = false
|
||||||
|
var mediaRequiredValidation = false
|
||||||
for attribute in message.attributes {
|
for attribute in message.attributes {
|
||||||
if attribute is ViewCountMessageAttribute {
|
if attribute is ViewCountMessageAttribute {
|
||||||
if message.id.namespace == Namespaces.Message.Cloud {
|
if message.id.namespace == Namespaces.Message.Cloud {
|
||||||
@ -1218,6 +1224,24 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
}
|
}
|
||||||
} else if let _ = media as? TelegramMediaAction {
|
} else if let _ = media as? TelegramMediaAction {
|
||||||
isAction = true
|
isAction = true
|
||||||
|
} else if let telegramFile = media as? TelegramMediaFile {
|
||||||
|
if telegramFile.isAnimatedSticker, (message.id.peerId.namespace == Namespaces.Peer.SecretChat || !telegramFile.previewRepresentations.isEmpty), let size = telegramFile.size, size > 0 && size <= 128 * 1024 {
|
||||||
|
if message.id.peerId.namespace == Namespaces.Peer.SecretChat {
|
||||||
|
if telegramFile.fileId.namespace == Namespaces.Media.CloudFile {
|
||||||
|
var isValidated = false
|
||||||
|
attributes: for attribute in telegramFile.attributes {
|
||||||
|
if case .hintIsValidated = attribute {
|
||||||
|
isValidated = true
|
||||||
|
break attributes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isValidated {
|
||||||
|
mediaRequiredValidation = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !isAction && message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
if !isAction && message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||||
@ -1226,6 +1250,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
if contentRequiredValidation {
|
if contentRequiredValidation {
|
||||||
messageIdsWithUnsupportedMedia.append(message.id)
|
messageIdsWithUnsupportedMedia.append(message.id)
|
||||||
}
|
}
|
||||||
|
if mediaRequiredValidation {
|
||||||
|
messageIdsWithRefreshMedia.append(message.id)
|
||||||
|
}
|
||||||
if hasUnconsumedMention && !hasUnconsumedContent {
|
if hasUnconsumedMention && !hasUnconsumedContent {
|
||||||
messageIdsWithUnseenPersonalMention.append(message.id)
|
messageIdsWithUnseenPersonalMention.append(message.id)
|
||||||
}
|
}
|
||||||
@ -1346,6 +1373,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
if !messageIdsWithUnsupportedMedia.isEmpty {
|
if !messageIdsWithUnsupportedMedia.isEmpty {
|
||||||
self.unsupportedMessageProcessingManager.add(messageIdsWithUnsupportedMedia)
|
self.unsupportedMessageProcessingManager.add(messageIdsWithUnsupportedMedia)
|
||||||
}
|
}
|
||||||
|
if !messageIdsWithRefreshMedia.isEmpty {
|
||||||
|
self.refreshMediaProcessingManager.add(messageIdsWithRefreshMedia)
|
||||||
|
}
|
||||||
if !messageIdsWithUnseenPersonalMention.isEmpty {
|
if !messageIdsWithUnseenPersonalMention.isEmpty {
|
||||||
self.messageMentionProcessingManager.add(messageIdsWithUnseenPersonalMention)
|
self.messageMentionProcessingManager.add(messageIdsWithUnseenPersonalMention)
|
||||||
}
|
}
|
||||||
|
@ -1066,7 +1066,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
} else if let attribute = attribute as? ReplyMarkupMessageAttribute, attribute.flags.contains(.inline), !attribute.rows.isEmpty && !isPreview {
|
} else if let attribute = attribute as? ReplyMarkupMessageAttribute, attribute.flags.contains(.inline), !attribute.rows.isEmpty && !isPreview {
|
||||||
replyMarkup = attribute
|
replyMarkup = attribute
|
||||||
} else if let attribute = attribute as? AuthorSignatureMessageAttribute {
|
} else if let attribute = attribute as? AuthorSignatureMessageAttribute {
|
||||||
if firstMessage.author is TelegramChannel, !attribute.signature.isEmpty {
|
if let chatPeer = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, case .group = chatPeer.info, firstMessage.author is TelegramChannel, !attribute.signature.isEmpty {
|
||||||
authorRank = .custom(attribute.signature)
|
authorRank = .custom(attribute.signature)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import PresentationDataUtils
|
|||||||
import SearchUI
|
import SearchUI
|
||||||
import TelegramPermissionsUI
|
import TelegramPermissionsUI
|
||||||
import AppBundle
|
import AppBundle
|
||||||
|
import DeviceAccess
|
||||||
|
|
||||||
public class ComposeController: ViewController {
|
public class ComposeController: ViewController {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
@ -183,6 +184,42 @@ public class ComposeController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.contactsNode.openCreateContact = { [weak self] in
|
||||||
|
let _ = (DeviceAccess.authorizationStatus(subject: .contacts)
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue).start(next: { status in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch status {
|
||||||
|
case .allowed:
|
||||||
|
let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: "", lastName: "", phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!<Mobile>!$_", value: "+")]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
|
||||||
|
(strongSelf.navigationController as? NavigationController)?.pushViewController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .create(peer: nil, contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let peer = peer {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||||
|
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(strongSelf.navigationController as? NavigationController)?.replaceAllButRootController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .vcard(nil, stableId, contactData), completed: nil, cancelled: nil), animated: true)
|
||||||
|
}
|
||||||
|
}), completed: nil, cancelled: nil))
|
||||||
|
case .notDetermined:
|
||||||
|
DeviceAccess.authorizeAccess(to: .contacts)
|
||||||
|
default:
|
||||||
|
let presentationData = strongSelf.presentationData
|
||||||
|
strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.AccessDenied_Title, text: presentationData.strings.Contacts_AccessDeniedError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: {
|
||||||
|
self?.context.sharedContext.applicationBindings.openSettings()
|
||||||
|
})]), in: .window(.root))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
self.contactsNode.openCreateNewChannel = { [weak self] in
|
self.contactsNode.openCreateNewChannel = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
@ -27,6 +27,7 @@ final class ComposeControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
var openCreateNewGroup: (() -> Void)?
|
var openCreateNewGroup: (() -> Void)?
|
||||||
var openCreateNewSecretChat: (() -> Void)?
|
var openCreateNewSecretChat: (() -> Void)?
|
||||||
|
var openCreateContact: (() -> Void)?
|
||||||
var openCreateNewChannel: (() -> Void)?
|
var openCreateNewChannel: (() -> Void)?
|
||||||
|
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
@ -39,14 +40,15 @@ final class ComposeControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
var openCreateNewGroupImpl: (() -> Void)?
|
var openCreateNewGroupImpl: (() -> Void)?
|
||||||
var openCreateNewSecretChatImpl: (() -> Void)?
|
var openCreateNewSecretChatImpl: (() -> Void)?
|
||||||
|
var openCreateContactImpl: (() -> Void)?
|
||||||
var openCreateNewChannelImpl: (() -> Void)?
|
var openCreateNewChannelImpl: (() -> Void)?
|
||||||
|
|
||||||
self.contactListNode = ContactListNode(context: context, presentation: .single(.natural(options: [
|
self.contactListNode = ContactListNode(context: context, presentation: .single(.natural(options: [
|
||||||
ContactListAdditionalOption(title: self.presentationData.strings.Compose_NewGroup, icon: .generic(UIImage(bundleImageName: "Contact List/CreateGroupActionIcon")!), action: {
|
ContactListAdditionalOption(title: self.presentationData.strings.Compose_NewGroup, icon: .generic(UIImage(bundleImageName: "Contact List/CreateGroupActionIcon")!), action: {
|
||||||
openCreateNewGroupImpl?()
|
openCreateNewGroupImpl?()
|
||||||
}),
|
}),
|
||||||
ContactListAdditionalOption(title: self.presentationData.strings.Compose_NewEncryptedChat, icon: .generic(UIImage(bundleImageName: "Contact List/CreateSecretChatActionIcon")!), action: {
|
ContactListAdditionalOption(title: self.presentationData.strings.NewContact_Title, icon: .generic(UIImage(bundleImageName: "Contact List/AddMemberIcon")!), action: {
|
||||||
openCreateNewSecretChatImpl?()
|
openCreateContactImpl?()
|
||||||
}),
|
}),
|
||||||
ContactListAdditionalOption(title: self.presentationData.strings.Compose_NewChannel, icon: .generic(UIImage(bundleImageName: "Contact List/CreateChannelActionIcon")!), action: {
|
ContactListAdditionalOption(title: self.presentationData.strings.Compose_NewChannel, icon: .generic(UIImage(bundleImageName: "Contact List/CreateChannelActionIcon")!), action: {
|
||||||
openCreateNewChannelImpl?()
|
openCreateNewChannelImpl?()
|
||||||
@ -69,6 +71,10 @@ final class ComposeControllerNode: ASDisplayNode {
|
|||||||
openCreateNewSecretChatImpl = { [weak self] in
|
openCreateNewSecretChatImpl = { [weak self] in
|
||||||
self?.openCreateNewSecretChat?()
|
self?.openCreateNewSecretChat?()
|
||||||
}
|
}
|
||||||
|
openCreateContactImpl = { [weak self] in
|
||||||
|
self?.contactListNode.listNode.clearHighlightAnimated(true)
|
||||||
|
self?.openCreateContact?()
|
||||||
|
}
|
||||||
openCreateNewChannelImpl = { [weak self] in
|
openCreateNewChannelImpl = { [weak self] in
|
||||||
self?.openCreateNewChannel?()
|
self?.openCreateNewChannel?()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user