diff --git a/TelegramCore/EnqueueMessage.swift b/TelegramCore/EnqueueMessage.swift index 0968025e0d..b7c99926c8 100644 --- a/TelegramCore/EnqueueMessage.swift +++ b/TelegramCore/EnqueueMessage.swift @@ -115,6 +115,31 @@ public func enqueueMessages(account: Account, peerId: PeerId, messages: [Enqueue } } +public func resendMessages(account: Account, messageIds: [MessageId]) -> Signal { + return account.postbox.modify { modifier -> Void in + var removeMessageIds: [MessageId] = [] + for (peerId, ids) in messagesIdsGroupedByPeerId(messageIds) { + var messages: [EnqueueMessage] = [] + for id in ids { + if let message = modifier.getMessage(id), !message.flags.contains(.Incoming) { + removeMessageIds.append(id) + + var replyToMessageId: MessageId? + for attribute in message.attributes { + if let attribute = attribute as? ReplyMessageAttribute { + replyToMessageId = attribute.messageId + } + } + + messages.append(.message(text: message.text, attributes: message.attributes, media: message.media.first, replyToMessageId: replyToMessageId)) + } + } + let _ = enqueueMessages(modifier: modifier, account: account, peerId: peerId, messages: messages.map { (false, $0) }) + } + modifier.deleteMessages(removeMessageIds) + } +} + func enqueueMessages(modifier: Modifier, account: Account, peerId: PeerId, messages: [(Bool, EnqueueMessage)]) -> [MessageId?] { if let peer = modifier.getPeer(peerId) { var storeMessages: [StoreMessage] = [] diff --git a/TelegramCore/MessageUtils.swift b/TelegramCore/MessageUtils.swift index a9541ced9e..f677287493 100644 --- a/TelegramCore/MessageUtils.swift +++ b/TelegramCore/MessageUtils.swift @@ -57,7 +57,6 @@ public extension Message { } } - func messagesIdsGroupedByPeerId(_ ids: Set) -> [PeerId: [MessageId]] { var dict: [PeerId: [MessageId]] = [:] @@ -73,6 +72,20 @@ func messagesIdsGroupedByPeerId(_ ids: Set) -> [PeerId: [MessageId]] return dict } +func messagesIdsGroupedByPeerId(_ ids: [MessageId]) -> [PeerId: [MessageId]] { + var dict: [PeerId: [MessageId]] = [:] + + for id in ids { + let peerId = id.peerId + if dict[peerId] == nil { + dict[peerId] = [id] + } else { + dict[peerId]!.append(id) + } + } + + return dict +} func locallyRenderedMessage(message: StoreMessage, peers: [PeerId: Peer]) -> Message? { guard case let .Id(id) = message.id else { diff --git a/TelegramCore/PrivacySettings.swift b/TelegramCore/PrivacySettings.swift index 55dafbdcd6..6df28b0500 100644 --- a/TelegramCore/PrivacySettings.swift +++ b/TelegramCore/PrivacySettings.swift @@ -32,6 +32,28 @@ public enum SelectivePrivacySettings: Equatable { } } } + + func withEnabledPeerIds(_ peerIds: Set) -> SelectivePrivacySettings { + switch self { + case let .disableEveryone(enableFor): + return .disableEveryone(enableFor: enableFor.union(peerIds)) + case let .enableContacts(enableFor, disableFor): + return .enableContacts(enableFor: enableFor.union(peerIds), disableFor: disableFor) + case .enableEveryone: + return self + } + } + + func withDisabledPeerIds(_ peerIds: Set) -> SelectivePrivacySettings { + switch self { + case .disableEveryone: + return self + case let .enableContacts(enableFor, disableFor): + return .enableContacts(enableFor: enableFor, disableFor: disableFor.union(peerIds)) + case let .enableEveryone(disableFor): + return .enableEveryone(disableFor: disableFor.union(peerIds)) + } + } } public struct AccountPrivacySettings: Equatable { @@ -41,6 +63,13 @@ public struct AccountPrivacySettings: Equatable { public let accountRemovalTimeout: Int32 + public init(presence: SelectivePrivacySettings, groupInvitations: SelectivePrivacySettings, voiceCalls: SelectivePrivacySettings, accountRemovalTimeout: Int32) { + self.presence = presence + self.groupInvitations = groupInvitations + self.voiceCalls = voiceCalls + self.accountRemovalTimeout = accountRemovalTimeout + } + public static func ==(lhs: AccountPrivacySettings, rhs: AccountPrivacySettings) -> Bool { if lhs.presence != rhs.presence { return false @@ -59,3 +88,31 @@ public struct AccountPrivacySettings: Equatable { return true } } + +extension SelectivePrivacySettings { + init(apiRules: [Api.PrivacyRule]) { + var current: SelectivePrivacySettings = .disableEveryone(enableFor: Set()) + + var disableFor = Set() + var enableFor = Set() + + for rule in apiRules { + switch rule { + case .privacyValueAllowAll: + current = .enableEveryone(disableFor: Set()) + case .privacyValueAllowContacts: + current = .enableContacts(enableFor: Set(), disableFor: Set()) + case let .privacyValueAllowUsers(users): + enableFor = Set(users.map { PeerId(namespace: Namespaces.Peer.CloudUser, id: $0) }) + case .privacyValueDisallowAll: + current = .disableEveryone(enableFor: Set()) + case .privacyValueDisallowContacts: + break + case let .privacyValueDisallowUsers(users): + disableFor = Set(users.map { PeerId(namespace: Namespaces.Peer.CloudUser, id: $0) }) + } + } + + self = current.withEnabledPeerIds(enableFor).withDisabledPeerIds(disableFor) + } +} diff --git a/TelegramCore/ProcessSecretChatIncomingDecryptedOperations.swift b/TelegramCore/ProcessSecretChatIncomingDecryptedOperations.swift index 4a96a37a2d..329a1d3038 100644 --- a/TelegramCore/ProcessSecretChatIncomingDecryptedOperations.swift +++ b/TelegramCore/ProcessSecretChatIncomingDecryptedOperations.swift @@ -158,7 +158,6 @@ func processSecretChatIncomingDecryptedOperations(mediaBox: MediaBox, modifier: } else { if let layer = SecretChatSequenceBasedLayer(rawValue: parsedLayer.rawValue) { let role = updatedState.role - let fromSeqNo: Int32 = (topProcessedCanonicalIncomingOperationIndex + 1) * 2 + (role == .creator ? 1 : 1) let toSeqNo: Int32 = (canonicalIncomingIndex - 1) * 2 + (role == .creator ? 1 : 0) updatedState = addSecretChatOutgoingOperation(modifier: modifier, peerId: peerId, operation: SecretChatOutgoingOperationContents.resendOperations(layer: layer, actionGloballyUniqueId: arc4random64(), fromSeqNo: fromSeqNo, toSeqNo: toSeqNo), state: updatedState) @@ -545,11 +544,11 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 return nil case .decryptedMessageActionFlushHistory: return nil - case let .decryptedMessageActionNotifyLayer(layer): + case .decryptedMessageActionNotifyLayer: return nil - case let .decryptedMessageActionReadMessages(randomIds): + case .decryptedMessageActionReadMessages: return nil - case let .decryptedMessageActionScreenshotMessages(randomIds): + case .decryptedMessageActionScreenshotMessages: return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, timestamp: timestamp, flags: [.Incoming], tags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]), []) case let .decryptedMessageActionSetMessageTTL(ttlSeconds): return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, timestamp: timestamp, flags: [.Incoming], tags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(ttlSeconds))]), []) diff --git a/TelegramCore/UpdatedAccountPrivacySettings.swift b/TelegramCore/UpdatedAccountPrivacySettings.swift index 0da2774e84..87601d1225 100644 --- a/TelegramCore/UpdatedAccountPrivacySettings.swift +++ b/TelegramCore/UpdatedAccountPrivacySettings.swift @@ -7,19 +7,118 @@ import Foundation import SwiftSignalKit #endif -public func updatedAccountPrivacySettings(account: Account) -> Signal { +public func requestAccountPrivacySettings(account: Account) -> Signal { let lastSeenPrivacy = account.network.request(Api.functions.account.getPrivacy(key: .inputPrivacyKeyStatusTimestamp)) let groupPrivacy = account.network.request(Api.functions.account.getPrivacy(key: .inputPrivacyKeyChatInvite)) let voiceCallPrivacy = account.network.request(Api.functions.account.getPrivacy(key: .inputPrivacyKeyPhoneCall)) let autoremoveTimeout = account.network.request(Api.functions.account.getAccountTTL()) return combineLatest(lastSeenPrivacy, groupPrivacy, voiceCallPrivacy, autoremoveTimeout) |> retryRequest - |> map { lastSeenPrivacy, groupPrivacy, voiceCallPrivacy, autoremoveTimeout -> AccountPrivacySettings in + |> mapToSignal { lastSeenPrivacy, groupPrivacy, voiceCallPrivacy, autoremoveTimeout -> Signal in let accountTimeoutSeconds: Int32 switch autoremoveTimeout { case let .accountDaysTTL(days): accountTimeoutSeconds = days * 24 * 60 * 60 } - return AccountPrivacySettings(presence: .enableEveryone(disableFor: Set()), groupInvitations: .enableEveryone(disableFor: Set()), voiceCalls: .enableEveryone(disableFor: Set()), accountRemovalTimeout: accountTimeoutSeconds) + + + let lastSeenRules: [Api.PrivacyRule] + let groupRules: [Api.PrivacyRule] + let voiceRules: [Api.PrivacyRule] + var apiUsers: [Api.User] = [] + + switch lastSeenPrivacy { + case let .privacyRules(rules, users): + apiUsers.append(contentsOf: users) + lastSeenRules = rules + } + + switch groupPrivacy { + case let .privacyRules(rules, users): + apiUsers.append(contentsOf: users) + groupRules = rules + } + + switch voiceCallPrivacy { + case let .privacyRules(rules, users): + apiUsers.append(contentsOf: users) + voiceRules = rules + } + + let peers = apiUsers.map { TelegramUser(user: $0) } + + return account.postbox.modify { modifier -> AccountPrivacySettings in + updatePeers(modifier: modifier, peers: peers, update: { _, updated in + return updated + }) + + return AccountPrivacySettings(presence: SelectivePrivacySettings(apiRules: lastSeenRules), groupInvitations: SelectivePrivacySettings(apiRules: groupRules), voiceCalls: SelectivePrivacySettings(apiRules: voiceRules), accountRemovalTimeout: accountTimeoutSeconds) + } } } + +public func updateAccountRemovalTimeout(account: Account, timeout: Int32) -> Signal { + return account.network.request(Api.functions.account.setAccountTTL(ttl: .accountDaysTTL(days: timeout / (24 * 60 * 60)))) + |> retryRequest + |> mapToSignal { _ -> Signal in + return .complete() + } +} + +public enum UpdateSelectiveAccountPrivacySettingsType { + case presence + case groupInvitations + case voiceCalls + + var apiKey: Api.InputPrivacyKey { + switch self { + case .presence: + return .inputPrivacyKeyStatusTimestamp + case .groupInvitations: + return .inputPrivacyKeyChatInvite + case .voiceCalls: + return .inputPrivacyKeyPhoneCall + } + } +} + +private func apiInputUsers(modifier: Modifier, peerIds: [PeerId]) -> [Api.InputUser] { + var result: [Api.InputUser] = [] + for peerId in peerIds { + if let peer = modifier.getPeer(peerId), let inputUser = apiInputUser(peer) { + result.append(inputUser) + } + } + return result +} + +public func updateSelectiveAccountPrivacySettings(account: Account, type: UpdateSelectiveAccountPrivacySettingsType, settings: SelectivePrivacySettings) -> Signal { + return account.postbox.modify { modifier -> Signal in + var rules: [Api.InputPrivacyRule] = [] + switch settings { + case let .disableEveryone(enableFor): + if !enableFor.isEmpty { + rules.append(Api.InputPrivacyRule.inputPrivacyValueAllowUsers(users: apiInputUsers(modifier: modifier, peerIds: Array(enableFor)))) + } + rules.append(Api.InputPrivacyRule.inputPrivacyValueDisallowAll) + case let .enableContacts(enableFor, disableFor): + if !enableFor.isEmpty { + rules.append(Api.InputPrivacyRule.inputPrivacyValueAllowUsers(users: apiInputUsers(modifier: modifier, peerIds: Array(enableFor)))) + } + if !disableFor.isEmpty { + rules.append(Api.InputPrivacyRule.inputPrivacyValueDisallowUsers(users: apiInputUsers(modifier: modifier, peerIds: Array(disableFor)))) + } + rules.append(Api.InputPrivacyRule.inputPrivacyValueAllowContacts) + case let.enableEveryone(disableFor): + if !disableFor.isEmpty { + rules.append(Api.InputPrivacyRule.inputPrivacyValueDisallowUsers(users: apiInputUsers(modifier: modifier, peerIds: Array(disableFor)))) + } + rules.append(Api.InputPrivacyRule.inputPrivacyValueAllowAll) + } + return account.network.request(Api.functions.account.setPrivacy(key: type.apiKey, rules: rules)) + |> retryRequest + |> mapToSignal { _ -> Signal in + return .complete() + } + } |> switchToLatest +}