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
645bd9789b
2
Makefile
2
Makefile
@ -3,7 +3,7 @@
|
||||
include Utils.makefile
|
||||
|
||||
|
||||
APP_VERSION="7.1.1"
|
||||
APP_VERSION="7.1.2"
|
||||
CORE_COUNT=$(shell sysctl -n hw.logicalcpu)
|
||||
CORE_COUNT_MINUS_ONE=$(shell expr ${CORE_COUNT} \- 1)
|
||||
|
||||
|
@ -229,7 +229,6 @@ static int32_t fixedTimeDifferenceValue = 0;
|
||||
_useTempAuthKeys = useTempAuthKeys;
|
||||
#if DEBUG
|
||||
_tempKeyExpiration = 1 * 60 * 60;
|
||||
_tempKeyExpiration = 5;
|
||||
#else
|
||||
_tempKeyExpiration = 24 * 60 * 60;
|
||||
#endif
|
||||
|
@ -1105,7 +1105,7 @@ func debugRestoreState(basePath:String, name: String) {
|
||||
|
||||
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
|
||||
return Signal { subscriber in
|
||||
queue.async {
|
||||
@ -1177,7 +1177,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration,
|
||||
let endTime = CFAbsoluteTimeGetCurrent()
|
||||
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()
|
||||
break
|
||||
}
|
||||
@ -1330,7 +1330,7 @@ public final class Postbox {
|
||||
|
||||
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())
|
||||
|
||||
let startTime = CFAbsoluteTimeGetCurrent()
|
||||
@ -1534,6 +1534,9 @@ public final class Postbox {
|
||||
for id in self.messageHistoryUnsentTable.get() {
|
||||
transaction.updateMessage(id, update: { message in
|
||||
if !message.flags.contains(.Failed) {
|
||||
if message.timestamp + 60 * 10 > timestampForAbsoluteTimeBasedOperations {
|
||||
return .skip
|
||||
}
|
||||
var flags = StoreMessageFlags(message.flags)
|
||||
flags.remove(.Unsent)
|
||||
flags.remove(.Sending)
|
||||
|
@ -1,3 +1,4 @@
|
||||
import Foundation
|
||||
import SwiftSignalKit
|
||||
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> {
|
||||
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
|
||||
|> mapToSignal { value -> Signal<T, NoError> in
|
||||
switch value {
|
||||
|
@ -160,7 +160,7 @@ public enum AccountPreferenceEntriesResult {
|
||||
|
||||
public func accountPreferenceEntries(rootPath: String, id: AccountRecordId, keys: Set<ValueBoxKey>, encryptionParameters: ValueBoxEncryptionParameters) -> Signal<AccountPreferenceEntriesResult, NoError> {
|
||||
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
|
||||
|> mapToSignal { value -> Signal<AccountPreferenceEntriesResult, NoError> in
|
||||
switch value {
|
||||
@ -187,7 +187,7 @@ public enum AccountNoticeEntriesResult {
|
||||
|
||||
public func accountNoticeEntries(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters) -> Signal<AccountNoticeEntriesResult, NoError> {
|
||||
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
|
||||
|> mapToSignal { value -> Signal<AccountNoticeEntriesResult, NoError> in
|
||||
switch value {
|
||||
@ -208,7 +208,7 @@ public enum LegacyAccessChallengeDataResult {
|
||||
|
||||
public func accountLegacyAccessChallengeData(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters) -> Signal<LegacyAccessChallengeDataResult, NoError> {
|
||||
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
|
||||
|> mapToSignal { value -> Signal<LegacyAccessChallengeDataResult, NoError> in
|
||||
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> {
|
||||
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
|
||||
|> mapToSignal { result -> Signal<AccountResult, NoError> in
|
||||
|
@ -209,7 +209,7 @@ public func temporaryAccount(manager: AccountManager, rootPath: String, encrypti
|
||||
return manager.allocatedTemporaryAccountId()
|
||||
|> mapToSignal { id -> Signal<TemporaryAccount, NoError> in
|
||||
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
|
||||
switch result {
|
||||
case .upgrading:
|
||||
|
@ -291,6 +291,7 @@ public final class AccountViewTracker {
|
||||
private var seenLiveLocationDisposables = DisposableDict<Int32>()
|
||||
|
||||
private var updatedUnsupportedMediaMessageIdsAndTimestamps: [MessageId: Int32] = [:]
|
||||
private var refreshSecretChatMediaMessageIdsAndTimestamps: [MessageId: Int32] = [:]
|
||||
private var nextUpdatedUnsupportedMediaDisposableId: Int32 = 0
|
||||
private var updatedUnsupportedMediaDisposables = DisposableDict<Int32>()
|
||||
|
||||
@ -631,6 +632,18 @@ public final class AccountViewTracker {
|
||||
|> 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) {
|
||||
self.queue.async {
|
||||
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) {
|
||||
self.queue.async {
|
||||
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 {
|
||||
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) {
|
||||
var flags: Int32 = 0
|
||||
|
@ -145,10 +145,12 @@ private class ReplyThreadHistoryContextImpl {
|
||||
}
|
||||
|
||||
var channelMessageId: MessageId?
|
||||
var replyThreadAttribute: ReplyThreadMessageAttribute?
|
||||
for attribute in topMessage.attributes {
|
||||
if let attribute = attribute as? SourceReferenceMessageAttribute {
|
||||
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(
|
||||
messageId: parsedIndex.id,
|
||||
channelMessageId: channelMessageId,
|
||||
isChannelPost: isChannelPost,
|
||||
maxMessage: resolvedMaxMessage,
|
||||
maxReadIncomingMessageId: readInboxMaxId.flatMap { readMaxId in
|
||||
MessageId(peerId: parsedIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: readMaxId)
|
||||
},
|
||||
maxReadIncomingMessageId: maxReadIncomingMessageId,
|
||||
maxReadOutgoingMessageId: readOutboxMaxId.flatMap { readMaxId in
|
||||
MessageId(peerId: parsedIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: readMaxId)
|
||||
}
|
||||
|
@ -3072,6 +3072,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
})
|
||||
|
||||
var wasInForeground = true
|
||||
self.applicationInForegroundDisposable = (context.sharedContext.applicationBindings.applicationInForeground
|
||||
|> distinctUntilChanged
|
||||
|> deliverOn(Queue.mainQueue())).start(next: { [weak self] value in
|
||||
@ -3081,7 +3082,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.raiseToListen?.applicationResignedActive()
|
||||
|
||||
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 seenLiveLocationProcessingManager = ChatMessageThrottledProcessingManager()
|
||||
private let unsupportedMessageProcessingManager = ChatMessageThrottledProcessingManager()
|
||||
private let refreshMediaProcessingManager = ChatMessageThrottledProcessingManager()
|
||||
private let messageMentionProcessingManager = ChatMessageThrottledProcessingManager(delay: 0.2)
|
||||
let prefetchManager: InChatPrefetchManager
|
||||
private var currentEarlierPrefetchMessages: [(Message, Media)] = []
|
||||
@ -605,6 +606,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
self.unsupportedMessageProcessingManager.process = { [weak context] messageIds in
|
||||
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
|
||||
context?.account.viewTracker.updateMarkMentionsSeenForMessageIds(messageIds: messageIds)
|
||||
}
|
||||
@ -1174,6 +1178,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
var messageIdsWithUpdateableReactions: [MessageId] = []
|
||||
var messageIdsWithLiveLocation: [MessageId] = []
|
||||
var messageIdsWithUnsupportedMedia: [MessageId] = []
|
||||
var messageIdsWithRefreshMedia: [MessageId] = []
|
||||
var messageIdsWithUnseenPersonalMention: [MessageId] = []
|
||||
var messagesWithPreloadableMediaToEarlier: [(Message, Media)] = []
|
||||
var messagesWithPreloadableMediaToLater: [(Message, Media)] = []
|
||||
@ -1192,6 +1197,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
}
|
||||
}
|
||||
var contentRequiredValidation = false
|
||||
var mediaRequiredValidation = false
|
||||
for attribute in message.attributes {
|
||||
if attribute is ViewCountMessageAttribute {
|
||||
if message.id.namespace == Namespaces.Message.Cloud {
|
||||
@ -1218,6 +1224,24 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
}
|
||||
} else if let _ = media as? TelegramMediaAction {
|
||||
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 {
|
||||
@ -1226,6 +1250,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
if contentRequiredValidation {
|
||||
messageIdsWithUnsupportedMedia.append(message.id)
|
||||
}
|
||||
if mediaRequiredValidation {
|
||||
messageIdsWithRefreshMedia.append(message.id)
|
||||
}
|
||||
if hasUnconsumedMention && !hasUnconsumedContent {
|
||||
messageIdsWithUnseenPersonalMention.append(message.id)
|
||||
}
|
||||
@ -1346,6 +1373,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
if !messageIdsWithUnsupportedMedia.isEmpty {
|
||||
self.unsupportedMessageProcessingManager.add(messageIdsWithUnsupportedMedia)
|
||||
}
|
||||
if !messageIdsWithRefreshMedia.isEmpty {
|
||||
self.refreshMediaProcessingManager.add(messageIdsWithRefreshMedia)
|
||||
}
|
||||
if !messageIdsWithUnseenPersonalMention.isEmpty {
|
||||
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 {
|
||||
replyMarkup = attribute
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import PresentationDataUtils
|
||||
import SearchUI
|
||||
import TelegramPermissionsUI
|
||||
import AppBundle
|
||||
import DeviceAccess
|
||||
|
||||
public class ComposeController: ViewController {
|
||||
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
|
||||
if let strongSelf = self {
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
@ -27,6 +27,7 @@ final class ComposeControllerNode: ASDisplayNode {
|
||||
|
||||
var openCreateNewGroup: (() -> Void)?
|
||||
var openCreateNewSecretChat: (() -> Void)?
|
||||
var openCreateContact: (() -> Void)?
|
||||
var openCreateNewChannel: (() -> Void)?
|
||||
|
||||
private var presentationData: PresentationData
|
||||
@ -39,14 +40,15 @@ final class ComposeControllerNode: ASDisplayNode {
|
||||
|
||||
var openCreateNewGroupImpl: (() -> Void)?
|
||||
var openCreateNewSecretChatImpl: (() -> Void)?
|
||||
var openCreateContactImpl: (() -> Void)?
|
||||
var openCreateNewChannelImpl: (() -> Void)?
|
||||
|
||||
self.contactListNode = ContactListNode(context: context, presentation: .single(.natural(options: [
|
||||
ContactListAdditionalOption(title: self.presentationData.strings.Compose_NewGroup, icon: .generic(UIImage(bundleImageName: "Contact List/CreateGroupActionIcon")!), action: {
|
||||
openCreateNewGroupImpl?()
|
||||
}),
|
||||
ContactListAdditionalOption(title: self.presentationData.strings.Compose_NewEncryptedChat, icon: .generic(UIImage(bundleImageName: "Contact List/CreateSecretChatActionIcon")!), action: {
|
||||
openCreateNewSecretChatImpl?()
|
||||
ContactListAdditionalOption(title: self.presentationData.strings.NewContact_Title, icon: .generic(UIImage(bundleImageName: "Contact List/AddMemberIcon")!), action: {
|
||||
openCreateContactImpl?()
|
||||
}),
|
||||
ContactListAdditionalOption(title: self.presentationData.strings.Compose_NewChannel, icon: .generic(UIImage(bundleImageName: "Contact List/CreateChannelActionIcon")!), action: {
|
||||
openCreateNewChannelImpl?()
|
||||
@ -69,6 +71,10 @@ final class ComposeControllerNode: ASDisplayNode {
|
||||
openCreateNewSecretChatImpl = { [weak self] in
|
||||
self?.openCreateNewSecretChat?()
|
||||
}
|
||||
openCreateContactImpl = { [weak self] in
|
||||
self?.contactListNode.listNode.clearHighlightAnimated(true)
|
||||
self?.openCreateContact?()
|
||||
}
|
||||
openCreateNewChannelImpl = { [weak self] in
|
||||
self?.openCreateNewChannel?()
|
||||
}
|
||||
|
@ -1250,7 +1250,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
||||
}))
|
||||
}
|
||||
}
|
||||
if !isPublic && cachedData.linkedDiscussionPeerId == nil {
|
||||
if !isPublic, case .known(nil) = cachedData.linkedDiscussionPeerId {
|
||||
items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPreHistory, label: .text(cachedData.flags.contains(.preHistoryEnabled) ? presentationData.strings.GroupInfo_GroupHistoryVisible : presentationData.strings.GroupInfo_GroupHistoryHidden), text: presentationData.strings.GroupInfo_GroupHistory, action: {
|
||||
interaction.editingOpenPreHistorySetup()
|
||||
}))
|
||||
|
@ -627,7 +627,7 @@ public final class OngoingCallContext {
|
||||
|
||||
let context = OngoingCallThreadLocalContextWebrtc(version: version, queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForTypeWebrtc(initialNetworkType), dataSaving: ongoingDataSavingForTypeWebrtc(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, connections: filteredConnections, maxLayer: maxLayer, allowP2P: allowP2P, allowTCP: enableTCP, enableStunMarking: enableStunMarking, logPath: tempLogPath, statsLogPath: tempStatsLogPath, sendSignalingData: { [weak callSessionManager] data in
|
||||
callSessionManager?.sendSignalingData(internalId: internalId, data: data)
|
||||
}, videoCapturer: video?.impl, preferredVideoCodec: preferredVideoCodec)
|
||||
}, videoCapturer: video?.impl, preferredVideoCodec: preferredVideoCodec, audioInputDeviceId: "")
|
||||
|
||||
strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context))
|
||||
context.stateChanged = { [weak callSessionManager] state, videoState, remoteVideoState, remoteAudioState, remoteBatteryLevel, _ in
|
||||
|
@ -128,7 +128,7 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) {
|
||||
@property (nonatomic, copy) void (^ _Nullable stateChanged)(OngoingCallStateWebrtc, OngoingCallVideoStateWebrtc, OngoingCallRemoteVideoStateWebrtc, OngoingCallRemoteAudioStateWebrtc, OngoingCallRemoteBatteryLevelWebrtc, float);
|
||||
@property (nonatomic, copy) void (^ _Nullable signalBarsChanged)(int32_t);
|
||||
|
||||
- (instancetype _Nonnull)initWithVersion:(NSString * _Nonnull)version queue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing connections:(NSArray<OngoingCallConnectionDescriptionWebrtc *> * _Nonnull)connections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P allowTCP:(BOOL)allowTCP enableStunMarking:(BOOL)enableStunMarking logPath:(NSString * _Nonnull)logPath statsLogPath:(NSString * _Nonnull)statsLogPath sendSignalingData:(void (^ _Nonnull)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer preferredVideoCodec:(NSString * _Nullable)preferredVideoCodec;
|
||||
- (instancetype _Nonnull)initWithVersion:(NSString * _Nonnull)version queue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing connections:(NSArray<OngoingCallConnectionDescriptionWebrtc *> * _Nonnull)connections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P allowTCP:(BOOL)allowTCP enableStunMarking:(BOOL)enableStunMarking logPath:(NSString * _Nonnull)logPath statsLogPath:(NSString * _Nonnull)statsLogPath sendSignalingData:(void (^ _Nonnull)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer preferredVideoCodec:(NSString * _Nullable)preferredVideoCodec audioInputDeviceId: (NSString * _Nonnull)audioInputDeviceId;
|
||||
|
||||
- (void)beginTermination;
|
||||
- (void)stop:(void (^_Nullable)(NSString * _Nullable debugLog, int64_t bytesSentWifi, int64_t bytesReceivedWifi, int64_t bytesSentMobile, int64_t bytesReceivedMobile))completion;
|
||||
|
@ -328,7 +328,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype _Nonnull)initWithVersion:(NSString * _Nonnull)version queue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing connections:(NSArray<OngoingCallConnectionDescriptionWebrtc *> * _Nonnull)connections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P allowTCP:(BOOL)allowTCP enableStunMarking:(BOOL)enableStunMarking logPath:(NSString * _Nonnull)logPath statsLogPath:(NSString * _Nonnull)statsLogPath sendSignalingData:(void (^)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer preferredVideoCodec:(NSString * _Nullable)preferredVideoCodec {
|
||||
- (instancetype _Nonnull)initWithVersion:(NSString * _Nonnull)version queue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing connections:(NSArray<OngoingCallConnectionDescriptionWebrtc *> * _Nonnull)connections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P allowTCP:(BOOL)allowTCP enableStunMarking:(BOOL)enableStunMarking logPath:(NSString * _Nonnull)logPath statsLogPath:(NSString * _Nonnull)statsLogPath sendSignalingData:(void (^)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer preferredVideoCodec:(NSString * _Nullable)preferredVideoCodec audioInputDeviceId: (NSString * _Nonnull)audioInputDeviceId {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_version = version;
|
||||
@ -427,6 +427,9 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
||||
dispatch_once(&onceToken, ^{
|
||||
tgcalls::Register<tgcalls::InstanceImpl>();
|
||||
});
|
||||
|
||||
|
||||
|
||||
_tgVoip = tgcalls::Meta::Create([version UTF8String], (tgcalls::Descriptor){
|
||||
.config = config,
|
||||
.persistentState = (tgcalls::PersistentState){ derivedStateValue },
|
||||
@ -435,6 +438,10 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
||||
.rtcServers = parsedRtcServers,
|
||||
.initialNetworkType = callControllerNetworkTypeForType(networkType),
|
||||
.encryptionKey = encryptionKey,
|
||||
.mediaDevicesConfig = tgcalls::MediaDevicesConfig {
|
||||
.audioInputId = [audioInputDeviceId UTF8String],
|
||||
.audioOutputId = [@"" UTF8String]
|
||||
},
|
||||
.videoCapture = [_videoCapturer getInterface],
|
||||
.stateUpdated = [weakSelf, queue](tgcalls::State state) {
|
||||
[queue dispatch:^{
|
||||
|
Loading…
x
Reference in New Issue
Block a user