mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Re-implement external sharing to secret chats
(cherry picked from commit 21af13cfdb347b76bf7b504ae5d36067ba693b18)
This commit is contained in:
parent
3d44192e2f
commit
96279df59b
@ -52,6 +52,30 @@ final class PeerMergedOperationLogIndexTable: Table {
|
||||
return result
|
||||
}
|
||||
|
||||
func getTagLocalIndices(tag: PeerOperationLogTag, peerId: PeerId, fromMergedIndex: Int32, limit: Int) -> [(PeerId, Int32, Int32)] {
|
||||
var result: [(PeerId, Int32, Int32)] = []
|
||||
self.valueBox.range(self.table, start: self.key(tag: tag, index: fromMergedIndex == 0 ? 0 : fromMergedIndex - 1), end: self.key(tag: tag, index: Int32.max), values: { key, value in
|
||||
assert(key.getUInt8(0) == tag.rawValue)
|
||||
var peerIdValue: Int64 = 0
|
||||
var tagLocalIndexValue: Int32 = 0
|
||||
value.read(&peerIdValue, offset: 0, length: 8)
|
||||
value.read(&tagLocalIndexValue, offset: 0, length: 4)
|
||||
|
||||
let parsedPeerId = PeerId(peerIdValue)
|
||||
if parsedPeerId != peerId {
|
||||
return true
|
||||
}
|
||||
|
||||
result.append((parsedPeerId, tagLocalIndexValue, key.getInt32(1)))
|
||||
if result.count >= limit {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}, limit: 0)
|
||||
return result
|
||||
}
|
||||
|
||||
func tailIndex(tag: PeerOperationLogTag) -> Int32? {
|
||||
var result: Int32?
|
||||
self.valueBox.range(self.table, start: self.key(tag: tag, index: Int32.max), end: self.key(tag: tag, index: 0), keys: {
|
||||
|
@ -2,23 +2,52 @@ import Foundation
|
||||
|
||||
final class MutablePeerMergedOperationLogView {
|
||||
let tag: PeerOperationLogTag
|
||||
let filterByPeerId: PeerId?
|
||||
var entries: [PeerMergedOperationLogEntry]
|
||||
var tailIndex: Int32?
|
||||
let limit: Int
|
||||
|
||||
init(postbox: PostboxImpl, tag: PeerOperationLogTag, limit: Int) {
|
||||
init(postbox: PostboxImpl, tag: PeerOperationLogTag, filterByPeerId: PeerId?, limit: Int) {
|
||||
self.tag = tag
|
||||
self.entries = postbox.peerOperationLogTable.getMergedEntries(tag: tag, fromIndex: 0, limit: limit)
|
||||
self.filterByPeerId = filterByPeerId
|
||||
if let filterByPeerId = self.filterByPeerId {
|
||||
self.entries = postbox.peerOperationLogTable.getMergedEntries(tag: tag, peerId: filterByPeerId, fromIndex: 0, limit: limit)
|
||||
} else {
|
||||
self.entries = postbox.peerOperationLogTable.getMergedEntries(tag: tag, fromIndex: 0, limit: limit)
|
||||
}
|
||||
self.tailIndex = postbox.peerMergedOperationLogIndexTable.tailIndex(tag: tag)
|
||||
self.limit = limit
|
||||
}
|
||||
|
||||
func replay(postbox: PostboxImpl, operations: [PeerMergedOperationLogOperation]) -> Bool {
|
||||
var updated = false
|
||||
var invalidatedTail = false
|
||||
|
||||
for operation in operations {
|
||||
switch operation {
|
||||
if let filterByPeerId = self.filterByPeerId {
|
||||
if operations.contains(where: { operation in
|
||||
switch operation {
|
||||
case let .append(entry):
|
||||
if entry.tag == self.tag && entry.peerId == filterByPeerId {
|
||||
return true
|
||||
}
|
||||
case let .remove(tag, peerId, _):
|
||||
if tag == self.tag && peerId == filterByPeerId {
|
||||
return true
|
||||
}
|
||||
case let .updateContents(entry):
|
||||
if entry.tag == self.tag && entry.peerId == filterByPeerId {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}) {
|
||||
self.entries = postbox.peerOperationLogTable.getMergedEntries(tag: tag, peerId: filterByPeerId, fromIndex: 0, limit: limit)
|
||||
updated = true
|
||||
}
|
||||
} else {
|
||||
var invalidatedTail = false
|
||||
|
||||
for operation in operations {
|
||||
switch operation {
|
||||
case let .append(entry):
|
||||
if entry.tag == self.tag {
|
||||
if let tailIndex = self.tailIndex {
|
||||
@ -39,15 +68,15 @@ final class MutablePeerMergedOperationLogView {
|
||||
}
|
||||
case let .updateContents(entry):
|
||||
if entry.tag == self.tag {
|
||||
loop: for i in 0 ..< self.entries.count {
|
||||
if self.entries[i].tagLocalIndex == entry.tagLocalIndex {
|
||||
self.entries[i] = entry
|
||||
updated = true
|
||||
break loop
|
||||
}
|
||||
loop: for i in 0 ..< self.entries.count {
|
||||
if self.entries[i].tagLocalIndex == entry.tagLocalIndex {
|
||||
self.entries[i] = entry
|
||||
updated = true
|
||||
break loop
|
||||
}
|
||||
}
|
||||
case let .remove(tag, mergedIndices):
|
||||
}
|
||||
case let .remove(tag, _, mergedIndices):
|
||||
if tag == self.tag {
|
||||
updated = true
|
||||
for i in (0 ..< self.entries.count).reversed() {
|
||||
@ -60,37 +89,38 @@ final class MutablePeerMergedOperationLogView {
|
||||
invalidatedTail = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if updated {
|
||||
if invalidatedTail {
|
||||
self.tailIndex = postbox.peerMergedOperationLogIndexTable.tailIndex(tag: self.tag)
|
||||
}
|
||||
if self.entries.count < self.limit {
|
||||
if let tailIndex = self.tailIndex {
|
||||
if self.entries.isEmpty || self.entries.last!.mergedIndex < tailIndex {
|
||||
var fromIndex: Int32 = 0
|
||||
if !self.entries.isEmpty {
|
||||
fromIndex = self.entries.last!.mergedIndex + 1
|
||||
}
|
||||
for entry in postbox.peerOperationLogTable.getMergedEntries(tag: self.tag, fromIndex: fromIndex, limit: self.limit - self.entries.count) {
|
||||
self.entries.append(entry)
|
||||
}
|
||||
for i in 0 ..< self.entries.count {
|
||||
if i != 0 {
|
||||
assert(self.entries[i].mergedIndex >= self.entries[i - 1].mergedIndex + 1)
|
||||
if updated {
|
||||
if invalidatedTail {
|
||||
self.tailIndex = postbox.peerMergedOperationLogIndexTable.tailIndex(tag: self.tag)
|
||||
}
|
||||
if self.entries.count < self.limit {
|
||||
if let tailIndex = self.tailIndex {
|
||||
if self.entries.isEmpty || self.entries.last!.mergedIndex < tailIndex {
|
||||
var fromIndex: Int32 = 0
|
||||
if !self.entries.isEmpty {
|
||||
fromIndex = self.entries.last!.mergedIndex + 1
|
||||
}
|
||||
for entry in postbox.peerOperationLogTable.getMergedEntries(tag: self.tag, fromIndex: fromIndex, limit: self.limit - self.entries.count) {
|
||||
self.entries.append(entry)
|
||||
}
|
||||
for i in 0 ..< self.entries.count {
|
||||
if i != 0 {
|
||||
assert(self.entries[i].mergedIndex >= self.entries[i - 1].mergedIndex + 1)
|
||||
}
|
||||
}
|
||||
if !self.entries.isEmpty {
|
||||
assert(self.entries.last!.mergedIndex <= tailIndex)
|
||||
}
|
||||
}
|
||||
if !self.entries.isEmpty {
|
||||
assert(self.entries.last!.mergedIndex <= tailIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert(self.tailIndex != nil)
|
||||
if let tailIndex = self.tailIndex {
|
||||
assert(self.entries.last!.mergedIndex <= tailIndex)
|
||||
} else {
|
||||
assert(self.tailIndex != nil)
|
||||
if let tailIndex = self.tailIndex {
|
||||
assert(self.entries.last!.mergedIndex <= tailIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import Foundation
|
||||
|
||||
enum PeerMergedOperationLogOperation {
|
||||
case append(PeerMergedOperationLogEntry)
|
||||
case remove(tag: PeerOperationLogTag, mergedIndices: Set<Int32>)
|
||||
case remove(tag: PeerOperationLogTag, peerId: PeerId, mergedIndices: Set<Int32>)
|
||||
case updateContents(PeerMergedOperationLogEntry)
|
||||
}
|
||||
|
||||
@ -197,7 +197,7 @@ final class PeerOperationLogTable: Table {
|
||||
|
||||
if !mergedIndices.isEmpty {
|
||||
self.mergedIndexTable.remove(tag: tag, mergedIndices: mergedIndices)
|
||||
operations.append(.remove(tag: tag, mergedIndices: Set(mergedIndices)))
|
||||
operations.append(.remove(tag: tag, peerId: peerId, mergedIndices: Set(mergedIndices)))
|
||||
}
|
||||
return removed
|
||||
}
|
||||
@ -224,7 +224,7 @@ final class PeerOperationLogTable: Table {
|
||||
|
||||
if !mergedIndices.isEmpty {
|
||||
self.mergedIndexTable.remove(tag: tag, mergedIndices: mergedIndices)
|
||||
operations.append(.remove(tag: tag, mergedIndices: Set(mergedIndices)))
|
||||
operations.append(.remove(tag: tag, peerId: peerId, mergedIndices: Set(mergedIndices)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -250,7 +250,7 @@ final class PeerOperationLogTable: Table {
|
||||
|
||||
if !mergedIndices.isEmpty {
|
||||
self.mergedIndexTable.remove(tag: tag, mergedIndices: mergedIndices)
|
||||
operations.append(.remove(tag: tag, mergedIndices: Set(mergedIndices)))
|
||||
operations.append(.remove(tag: tag, peerId: peerId, mergedIndices: Set(mergedIndices)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,6 +271,23 @@ final class PeerOperationLogTable: Table {
|
||||
return entries
|
||||
}
|
||||
|
||||
func getMergedEntries(tag: PeerOperationLogTag, peerId: PeerId, fromIndex: Int32, limit: Int) -> [PeerMergedOperationLogEntry] {
|
||||
var entries: [PeerMergedOperationLogEntry] = []
|
||||
for (peerId, tagLocalIndex, mergedIndex) in self.mergedIndexTable.getTagLocalIndices(tag: tag, peerId: peerId, fromMergedIndex: fromIndex, limit: limit) {
|
||||
if let value = self.valueBox.get(self.table, key: self.key(peerId: peerId, tag: tag, index: tagLocalIndex)) {
|
||||
if let entry = parseMergedEntry(peerId: peerId, tag: tag, tagLocalIndex: tagLocalIndex, value) {
|
||||
entries.append(entry)
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
} else {
|
||||
self.mergedIndexTable.remove(tag: tag, mergedIndices: [mergedIndex])
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
return entries
|
||||
}
|
||||
|
||||
func enumerateEntries(peerId: PeerId, tag: PeerOperationLogTag, _ f: (PeerOperationLogEntry) -> Bool) {
|
||||
self.valueBox.range(self.table, start: self.key(peerId: peerId, tag: tag, index: 0).predecessor, end: self.key(peerId: peerId, tag: tag, index: Int32.max).successor, values: { key, value in
|
||||
if let entry = parseEntry(peerId: peerId, tag: tag, tagLocalIndex: key.getInt32(9), value) {
|
||||
@ -317,12 +334,12 @@ final class PeerOperationLogTable: Table {
|
||||
if let mergedIndexValue = mergedIndex {
|
||||
mergedIndex = nil
|
||||
self.mergedIndexTable.remove(tag: tag, mergedIndices: [mergedIndexValue])
|
||||
operations.append(.remove(tag: tag, mergedIndices: Set([mergedIndexValue])))
|
||||
operations.append(.remove(tag: tag, peerId: peerId, mergedIndices: Set([mergedIndexValue])))
|
||||
}
|
||||
case .newAutomatic:
|
||||
if let mergedIndexValue = mergedIndex {
|
||||
self.mergedIndexTable.remove(tag: tag, mergedIndices: [mergedIndexValue])
|
||||
operations.append(.remove(tag: tag, mergedIndices: Set([mergedIndexValue])))
|
||||
operations.append(.remove(tag: tag, peerId: peerId, mergedIndices: Set([mergedIndexValue])))
|
||||
}
|
||||
let updatedMergedIndexValue = self.mergedIndexTable.add(peerId: peerId, tag: tag, tagLocalIndex: tagLocalIndex)
|
||||
mergedIndex = updatedMergedIndexValue
|
||||
|
@ -3654,9 +3654,9 @@ final class PostboxImpl {
|
||||
}
|
||||
}
|
||||
|
||||
public func mergedOperationLogView(tag: PeerOperationLogTag, limit: Int) -> Signal<PeerMergedOperationLogView, NoError> {
|
||||
public func mergedOperationLogView(tag: PeerOperationLogTag, filterByPeerId: PeerId?, limit: Int) -> Signal<PeerMergedOperationLogView, NoError> {
|
||||
return self.transactionSignal { subscriber, transaction in
|
||||
let view = MutablePeerMergedOperationLogView(postbox: self, tag: tag, limit: limit)
|
||||
let view = MutablePeerMergedOperationLogView(postbox: self, tag: tag, filterByPeerId: filterByPeerId, limit: limit)
|
||||
|
||||
subscriber.putNext(PeerMergedOperationLogView(view))
|
||||
|
||||
@ -4638,12 +4638,12 @@ public class Postbox {
|
||||
}
|
||||
}
|
||||
|
||||
public func mergedOperationLogView(tag: PeerOperationLogTag, limit: Int) -> Signal<PeerMergedOperationLogView, NoError> {
|
||||
public func mergedOperationLogView(tag: PeerOperationLogTag, filterByPeerId: PeerId? = nil, limit: Int) -> Signal<PeerMergedOperationLogView, NoError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
|
||||
self.impl.with { impl in
|
||||
disposable.set(impl.mergedOperationLogView(tag: tag, limit: limit).start(next: subscriber.putNext, error: subscriber.putError, completed: subscriber.putCompletion))
|
||||
disposable.set(impl.mergedOperationLogView(tag: tag, filterByPeerId: filterByPeerId, limit: limit).start(next: subscriber.putNext, error: subscriber.putError, completed: subscriber.putCompletion))
|
||||
}
|
||||
|
||||
return disposable
|
||||
|
@ -971,9 +971,6 @@ public final class ShareController: ViewController {
|
||||
if self.environment.isMainApp {
|
||||
useLegacy = true
|
||||
}
|
||||
if peerIds.contains(where: { $0.namespace == Namespaces.Peer.SecretChat }) {
|
||||
useLegacy = true
|
||||
}
|
||||
if let currentContext = self.currentContext as? ShareControllerAppAccountContext, let data = currentContext.context.currentAppConfiguration.with({ $0 }).data {
|
||||
if let _ = data["ios_disable_modern_sharing"] {
|
||||
useLegacy = true
|
||||
|
@ -1153,7 +1153,7 @@ public class Account {
|
||||
pendingMessageManager?.updatePendingMessageIds(view.ids)
|
||||
}))
|
||||
|
||||
self.managedOperationsDisposable.add(managedSecretChatOutgoingOperations(auxiliaryMethods: auxiliaryMethods, postbox: self.postbox, network: self.network).start())
|
||||
self.managedOperationsDisposable.add(managedSecretChatOutgoingOperations(auxiliaryMethods: auxiliaryMethods, postbox: self.postbox, network: self.network, accountPeerId: peerId, mode: .all).start())
|
||||
self.managedOperationsDisposable.add(managedCloudChatRemoveMessagesOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
||||
self.managedOperationsDisposable.add(managedAutoremoveMessageOperations(network: self.network, postbox: self.postbox, isRemove: true).start())
|
||||
self.managedOperationsDisposable.add(managedAutoremoveMessageOperations(network: self.network, postbox: self.postbox, isRemove: false).start())
|
||||
|
@ -963,5 +963,15 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
||||
}
|
||||
}
|
||||
}
|
||||
|> take(until: { result in
|
||||
var complete = false
|
||||
switch result {
|
||||
case .content:
|
||||
complete = true
|
||||
case .progress:
|
||||
complete = false
|
||||
}
|
||||
return SignalTakeAction(passthrough: true, complete: complete)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +125,12 @@ public func standaloneSendEnqueueMessages(
|
||||
threadId: Int64?,
|
||||
messages: [StandaloneSendEnqueueMessage]
|
||||
) -> Signal<StandaloneSendMessageStatus, StandaloneSendMessagesError> {
|
||||
let signals: [Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>] = messages.map { message in
|
||||
struct MessageResult {
|
||||
var result: PendingMessageUploadedContentResult
|
||||
var media: [Media]
|
||||
}
|
||||
|
||||
let signals: [Signal<MessageResult, PendingMessageUploadError>] = messages.map { message in
|
||||
var attributes: [MessageAttribute] = []
|
||||
var text: String = ""
|
||||
var media: [Media] = []
|
||||
@ -184,6 +189,9 @@ public func standaloneSendEnqueueMessages(
|
||||
contentResult = .single(value)
|
||||
}
|
||||
return contentResult
|
||||
|> map { contentResult in
|
||||
return MessageResult(result: contentResult, media: media)
|
||||
}
|
||||
}
|
||||
|
||||
return combineLatest(signals)
|
||||
@ -192,21 +200,21 @@ public func standaloneSendEnqueueMessages(
|
||||
}
|
||||
|> mapToSignal { contentResults -> Signal<StandaloneSendMessageStatus, StandaloneSendMessagesError> in
|
||||
var progressSum: Float = 0.0
|
||||
var allResults: [PendingMessageUploadedContentAndReuploadInfo] = []
|
||||
var allResults: [(result: PendingMessageUploadedContentAndReuploadInfo, media: [Media])] = []
|
||||
var allDone = true
|
||||
for status in contentResults {
|
||||
switch status {
|
||||
for result in contentResults {
|
||||
switch result.result {
|
||||
case let .progress(value):
|
||||
allDone = false
|
||||
progressSum += value
|
||||
case let .content(content):
|
||||
allResults.append(content)
|
||||
allResults.append((content, result.media))
|
||||
}
|
||||
}
|
||||
if allDone {
|
||||
var sendSignals: [Signal<Never, StandaloneSendMessagesError>] = []
|
||||
|
||||
for content in allResults {
|
||||
for (content, media) in allResults {
|
||||
var text: String = ""
|
||||
switch content.content {
|
||||
case let .text(textValue):
|
||||
@ -218,6 +226,7 @@ public func standaloneSendEnqueueMessages(
|
||||
}
|
||||
|
||||
sendSignals.append(sendUploadedMessageContent(
|
||||
auxiliaryMethods: auxiliaryMethods,
|
||||
postbox: postbox,
|
||||
network: network,
|
||||
stateManager: stateManager,
|
||||
@ -226,6 +235,7 @@ public func standaloneSendEnqueueMessages(
|
||||
content: content,
|
||||
text: text,
|
||||
attributes: [],
|
||||
media: media,
|
||||
threadId: threadId
|
||||
))
|
||||
}
|
||||
@ -241,12 +251,70 @@ public func standaloneSendEnqueueMessages(
|
||||
}
|
||||
}
|
||||
|
||||
private func sendUploadedMessageContent(postbox: Postbox, network: Network, stateManager: AccountStateManager, accountPeerId: PeerId, peerId: PeerId, content: PendingMessageUploadedContentAndReuploadInfo, text: String, attributes: [MessageAttribute], threadId: Int64?) -> Signal<Never, StandaloneSendMessagesError> {
|
||||
private func sendUploadedMessageContent(
|
||||
auxiliaryMethods: AccountAuxiliaryMethods,
|
||||
postbox: Postbox,
|
||||
network: Network,
|
||||
stateManager: AccountStateManager,
|
||||
accountPeerId: PeerId,
|
||||
peerId: PeerId,
|
||||
content: PendingMessageUploadedContentAndReuploadInfo,
|
||||
text: String,
|
||||
attributes: [MessageAttribute],
|
||||
media: [Media],
|
||||
threadId: Int64?
|
||||
) -> Signal<Never, StandaloneSendMessagesError> {
|
||||
return postbox.transaction { transaction -> Signal<Never, StandaloneSendMessagesError> in
|
||||
if peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
assertionFailure()
|
||||
//PendingMessageManager.sendSecretMessageContent(transaction: transaction, message: message, content: content)
|
||||
return .complete()
|
||||
var secretFile: SecretChatOutgoingFile?
|
||||
switch content.content {
|
||||
case let .secretMedia(file, size, key):
|
||||
if let fileReference = SecretChatOutgoingFileReference(file) {
|
||||
secretFile = SecretChatOutgoingFile(reference: fileReference, size: size, key: key)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
var layer: SecretChatLayer?
|
||||
let state = transaction.getPeerChatState(peerId) as? SecretChatState
|
||||
if let state = state {
|
||||
switch state.embeddedState {
|
||||
case .terminated, .handshake:
|
||||
break
|
||||
case .basicLayer:
|
||||
layer = .layer8
|
||||
case let .sequenceBasedLayer(sequenceState):
|
||||
layer = sequenceState.layerNegotiationState.activeLayer.secretChatLayer
|
||||
}
|
||||
}
|
||||
|
||||
if let state = state, let layer = layer {
|
||||
let messageContents = StandaloneSecretMessageContents(
|
||||
id: Int64.random(in: Int64.min ... Int64.max),
|
||||
text: text,
|
||||
attributes: attributes,
|
||||
media: media.first,
|
||||
file: secretFile
|
||||
)
|
||||
|
||||
let updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: .sendStandaloneMessage(layer: layer, contents: messageContents), state: state)
|
||||
if updatedState != state {
|
||||
transaction.setPeerChatState(peerId, state: updatedState)
|
||||
}
|
||||
|
||||
return managedSecretChatOutgoingOperations(
|
||||
auxiliaryMethods: auxiliaryMethods,
|
||||
postbox: postbox,
|
||||
network: network,
|
||||
accountPeerId: accountPeerId,
|
||||
mode: .standaloneComplete(peerId: peerId)
|
||||
)
|
||||
|> castError(StandaloneSendMessagesError.self)
|
||||
|> ignoreValues
|
||||
} else {
|
||||
return .fail(StandaloneSendMessagesError(peerId: peerId, reason: .none))
|
||||
}
|
||||
} else if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||
var uniqueId: Int64 = 0
|
||||
var forwardSourceInfoAttribute: ForwardSourceInfoAttribute?
|
||||
|
@ -105,11 +105,20 @@ private func takenImmutableOperation(postbox: Postbox, peerId: PeerId, tagLocalI
|
||||
}
|
||||
}
|
||||
|
||||
func managedSecretChatOutgoingOperations(auxiliaryMethods: AccountAuxiliaryMethods, postbox: Postbox, network: Network) -> Signal<Void, NoError> {
|
||||
return Signal { _ in
|
||||
enum ManagedSecretChatOutgoingOperationsMode {
|
||||
case all
|
||||
case standaloneComplete(peerId: PeerId)
|
||||
}
|
||||
|
||||
func managedSecretChatOutgoingOperations(auxiliaryMethods: AccountAuxiliaryMethods, postbox: Postbox, network: Network, accountPeerId: PeerId, mode: ManagedSecretChatOutgoingOperationsMode) -> Signal<Void, NoError> {
|
||||
return Signal { subscriber in
|
||||
let helper = Atomic<ManagedSecretChatOutgoingOperationsHelper>(value: ManagedSecretChatOutgoingOperationsHelper())
|
||||
|
||||
let disposable = postbox.mergedOperationLogView(tag: OperationLogTags.SecretOutgoing, limit: 10).start(next: { view in
|
||||
var filterByPeerId: PeerId?
|
||||
if case let .standaloneComplete(peerId) = mode {
|
||||
filterByPeerId = peerId
|
||||
}
|
||||
let disposable = postbox.mergedOperationLogView(tag: OperationLogTags.SecretOutgoing, filterByPeerId: filterByPeerId, limit: 10).start(next: { view in
|
||||
let (disposeOperations, beginOperations) = helper.with { helper -> (disposeOperations: [Disposable], beginOperations: [(PeerMergedOperationLogEntry, MetaDisposable)]) in
|
||||
return helper.update(view.entries)
|
||||
}
|
||||
@ -118,6 +127,10 @@ func managedSecretChatOutgoingOperations(auxiliaryMethods: AccountAuxiliaryMetho
|
||||
disposable.dispose()
|
||||
}
|
||||
|
||||
if case .standaloneComplete = mode, view.entries.isEmpty {
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
|
||||
for (entry, disposable) in beginOperations {
|
||||
let signal = takenImmutableOperation(postbox: postbox, peerId: entry.peerId, tagLocalIndex: entry.tagLocalIndex)
|
||||
|> mapToSignal { entry -> Signal<Void, NoError> in
|
||||
@ -128,6 +141,8 @@ func managedSecretChatOutgoingOperations(auxiliaryMethods: AccountAuxiliaryMetho
|
||||
return initialHandshakeAccept(postbox: postbox, network: network, peerId: entry.peerId, accessHash: accessHash, gA: gA, b: b, tagLocalIndex: entry.tagLocalIndex)
|
||||
case let .sendMessage(layer, id, file):
|
||||
return sendMessage(auxiliaryMethods: auxiliaryMethods, postbox: postbox, network: network, messageId: id, file: file, tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered, layer: layer)
|
||||
case let .sendStandaloneMessage(layer, contents):
|
||||
return sendStandaloneMessage(auxiliaryMethods: auxiliaryMethods, postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: entry.peerId, contents: contents, tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered, layer: layer)
|
||||
case let .reportLayerSupport(layer, actionGloballyUniqueId, layerSupport):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .reportLayerSupport(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, layerSupport: layerSupport), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .deleteMessages(layer, actionGloballyUniqueId, globallyUniqueIds):
|
||||
@ -1711,9 +1726,9 @@ private func resourceThumbnailData(auxiliaryMethods: AccountAuxiliaryMethods, me
|
||||
}
|
||||
}
|
||||
|
||||
private func messageWithThumbnailData(auxiliaryMethods: AccountAuxiliaryMethods, mediaBox: MediaBox, message: Message) -> Signal<[MediaId: (PixelDimensions, Data)], NoError> {
|
||||
private func messageWithThumbnailData(auxiliaryMethods: AccountAuxiliaryMethods, mediaBox: MediaBox, media: [Media]) -> Signal<[MediaId: (PixelDimensions, Data)], NoError> {
|
||||
var signals: [Signal<(MediaId, PixelDimensions, Data)?, NoError>] = []
|
||||
for media in message.media {
|
||||
for media in media {
|
||||
if let image = media as? TelegramMediaImage {
|
||||
if let smallestRepresentation = smallestImageRepresentation(image.representations) {
|
||||
signals.append(resourceThumbnailData(auxiliaryMethods: auxiliaryMethods, mediaBox: mediaBox, resource: smallestRepresentation.resource, mediaId: image.imageId))
|
||||
@ -1739,7 +1754,7 @@ private func messageWithThumbnailData(auxiliaryMethods: AccountAuxiliaryMethods,
|
||||
private func sendMessage(auxiliaryMethods: AccountAuxiliaryMethods, postbox: Postbox, network: Network, messageId: MessageId, file: SecretChatOutgoingFile?, tagLocalIndex: Int32, wasDelivered: Bool, layer: SecretChatLayer) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Signal<[MediaId: (PixelDimensions, Data)], NoError> in
|
||||
if let message = transaction.getMessage(messageId) {
|
||||
return messageWithThumbnailData(auxiliaryMethods: auxiliaryMethods, mediaBox: postbox.mediaBox, message: message)
|
||||
return messageWithThumbnailData(auxiliaryMethods: auxiliaryMethods, mediaBox: postbox.mediaBox, media: message.media)
|
||||
} else {
|
||||
return .single([:])
|
||||
}
|
||||
@ -1840,6 +1855,166 @@ private func sendMessage(auxiliaryMethods: AccountAuxiliaryMethods, postbox: Pos
|
||||
}
|
||||
}
|
||||
|
||||
private func sendStandaloneMessage(auxiliaryMethods: AccountAuxiliaryMethods, postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, contents: StandaloneSecretMessageContents, tagLocalIndex: Int32, wasDelivered: Bool, layer: SecretChatLayer) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Signal<[MediaId: (PixelDimensions, Data)], NoError> in
|
||||
var media: [Media] = []
|
||||
if let value = contents.media {
|
||||
media.append(value)
|
||||
}
|
||||
return messageWithThumbnailData(auxiliaryMethods: auxiliaryMethods, mediaBox: postbox.mediaBox, media: media)
|
||||
}
|
||||
|> switchToLatest
|
||||
|> mapToSignal { thumbnailData -> Signal<Void, NoError> in
|
||||
return postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
guard let state = transaction.getPeerChatState(peerId) as? SecretChatState, let peer = transaction.getPeer(peerId) as? TelegramSecretChat else {
|
||||
return .complete()
|
||||
}
|
||||
|
||||
let globallyUniqueId = contents.id
|
||||
|
||||
var media: [Media] = []
|
||||
if let value = contents.media {
|
||||
media.append(value)
|
||||
}
|
||||
let message = Message(
|
||||
stableId: 1,
|
||||
stableVersion: 0,
|
||||
id: MessageId(peerId: peerId, namespace: Namespaces.Message.Local, id: 1),
|
||||
globallyUniqueId: globallyUniqueId,
|
||||
groupingKey: nil,
|
||||
groupInfo: nil,
|
||||
threadId: nil,
|
||||
timestamp: 1,
|
||||
flags: [],
|
||||
tags: [],
|
||||
globalTags: [],
|
||||
localTags: [],
|
||||
forwardInfo: nil,
|
||||
author: nil,
|
||||
text: contents.text,
|
||||
attributes: contents.attributes,
|
||||
media: media,
|
||||
peers: SimpleDictionary(),
|
||||
associatedMessages: SimpleDictionary(),
|
||||
associatedMessageIds: [],
|
||||
associatedMedia: [:],
|
||||
associatedThreadInfo: nil,
|
||||
associatedStories: [:]
|
||||
)
|
||||
|
||||
let decryptedMessage = boxedDecryptedMessage(transaction: transaction, message: message, globallyUniqueId: globallyUniqueId, uploadedFile: contents.file, thumbnailData: [:], layer: layer)
|
||||
return sendBoxedDecryptedMessage(postbox: postbox, network: network, peer: peer, state: state, operationIndex: tagLocalIndex, decryptedMessage: decryptedMessage, globallyUniqueId: globallyUniqueId, file: contents.file, silent: message.muted, asService: wasDelivered, wasDelivered: wasDelivered)
|
||||
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||
return postbox.transaction { transaction -> Void in
|
||||
let forceRemove: Bool
|
||||
switch result {
|
||||
case .message:
|
||||
forceRemove = false
|
||||
case .error:
|
||||
forceRemove = true
|
||||
}
|
||||
markOutgoingOperationAsCompleted(transaction: transaction, peerId: peerId, tagLocalIndex: tagLocalIndex, forceRemove: forceRemove)
|
||||
|
||||
var timestamp: Int32?
|
||||
var encryptedFile: SecretChatFileReference?
|
||||
if case let .message(result) = result {
|
||||
switch result {
|
||||
case let .sentEncryptedMessage(date):
|
||||
timestamp = date
|
||||
case let .sentEncryptedFile(date, file):
|
||||
timestamp = date
|
||||
encryptedFile = SecretChatFileReference(file)
|
||||
}
|
||||
}
|
||||
|
||||
if let timestamp = timestamp {
|
||||
var updatedMedia: [Media] = []
|
||||
for item in media {
|
||||
if let file = item as? TelegramMediaFile, let encryptedFile = encryptedFile, let sourceFile = contents.file {
|
||||
let updatedFile = TelegramMediaFile(
|
||||
fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: encryptedFile.id),
|
||||
partialReference: nil,
|
||||
resource: SecretFileMediaResource(fileId: encryptedFile.id, accessHash: encryptedFile.accessHash, containerSize: encryptedFile.size, decryptedSize: sourceFile.size, datacenterId: Int(encryptedFile.datacenterId), key: sourceFile.key),
|
||||
previewRepresentations: file.previewRepresentations,
|
||||
videoThumbnails: file.videoThumbnails,
|
||||
immediateThumbnailData: file.immediateThumbnailData,
|
||||
mimeType: file.mimeType,
|
||||
size: file.size,
|
||||
attributes: file.attributes
|
||||
)
|
||||
updatedMedia.append(updatedFile)
|
||||
} else if let image = item as? TelegramMediaImage, let encryptedFile = encryptedFile, let sourceFile = contents.file, let representation = image.representations.last {
|
||||
let updatedImage = TelegramMediaImage(
|
||||
imageId: MediaId(namespace: Namespaces.Media.CloudSecretImage, id: encryptedFile.id),
|
||||
representations: [
|
||||
TelegramMediaImageRepresentation(
|
||||
dimensions: representation.dimensions,
|
||||
resource: SecretFileMediaResource(fileId: encryptedFile.id, accessHash: encryptedFile.accessHash, containerSize: encryptedFile.size, decryptedSize: sourceFile.size, datacenterId: Int(encryptedFile.datacenterId), key: sourceFile.key),
|
||||
progressiveSizes: [],
|
||||
immediateThumbnailData: image.immediateThumbnailData,
|
||||
hasVideo: false,
|
||||
isPersonal: false
|
||||
)],
|
||||
immediateThumbnailData: nil,
|
||||
reference: nil,
|
||||
partialReference: nil,
|
||||
flags: []
|
||||
)
|
||||
updatedMedia.append(updatedImage)
|
||||
} else {
|
||||
updatedMedia.append(item)
|
||||
}
|
||||
}
|
||||
|
||||
let entitiesAttribute = message.textEntitiesAttribute
|
||||
let (tags, globalTags) = tagsForStoreMessage(incoming: false, attributes: contents.attributes, media: updatedMedia, textEntities: entitiesAttribute?.entities, isPinned: false)
|
||||
|
||||
let storedMessage = StoreMessage(
|
||||
peerId: peerId,
|
||||
namespace: Namespaces.Message.Local,
|
||||
globallyUniqueId: globallyUniqueId,
|
||||
groupingKey: nil,
|
||||
threadId: nil,
|
||||
timestamp: timestamp,
|
||||
flags: [],
|
||||
tags: tags,
|
||||
globalTags: globalTags,
|
||||
localTags: [],
|
||||
forwardInfo: nil,
|
||||
authorId: accountPeerId,
|
||||
text: message.text,
|
||||
attributes: message.attributes,
|
||||
media: updatedMedia
|
||||
)
|
||||
|
||||
let idMapping = transaction.addMessages([storedMessage], location: .Random)
|
||||
if let id = idMapping[globallyUniqueId] {
|
||||
maybeReadSecretOutgoingMessage(transaction: transaction, index: MessageIndex(id: id, timestamp: timestamp))
|
||||
}
|
||||
|
||||
var sentStickers: [TelegramMediaFile] = []
|
||||
for media in message.media {
|
||||
if let file = media as? TelegramMediaFile {
|
||||
if file.isSticker {
|
||||
sentStickers.append(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for file in sentStickers {
|
||||
addRecentlyUsedSticker(transaction: transaction, fileReference: .standalone(media: file))
|
||||
}
|
||||
|
||||
if case .error(.chatCancelled) = result {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|> switchToLatest
|
||||
}
|
||||
}
|
||||
|
||||
private func sendServiceActionMessage(postbox: Postbox, network: Network, peerId: PeerId, action: SecretMessageAction, tagLocalIndex: Int32, wasDelivered: Bool) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
if let state = transaction.getPeerChatState(peerId) as? SecretChatState, let peer = transaction.getPeer(peerId) as? TelegramSecretChat {
|
||||
|
@ -7,47 +7,97 @@ private enum SecretChatOutgoingFileValue: Int32 {
|
||||
case uploadedLarge = 2
|
||||
}
|
||||
|
||||
public enum SecretChatOutgoingFileReference: PostboxCoding {
|
||||
public enum SecretChatOutgoingFileReference: PostboxCoding, Codable {
|
||||
case remote(id: Int64, accessHash: Int64)
|
||||
case uploadedRegular(id: Int64, partCount: Int32, md5Digest: String, keyFingerprint: Int32)
|
||||
case uploadedLarge(id: Int64, partCount: Int32, keyFingerprint: Int32)
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
switch decoder.decodeInt32ForKey("v", orElse: 0) {
|
||||
case SecretChatOutgoingFileValue.remote.rawValue:
|
||||
self = .remote(id: decoder.decodeInt64ForKey("i", orElse: 0), accessHash: decoder.decodeInt64ForKey("a", orElse: 0))
|
||||
case SecretChatOutgoingFileValue.uploadedRegular.rawValue:
|
||||
self = .uploadedRegular(id: decoder.decodeInt64ForKey("i", orElse: 0), partCount: decoder.decodeInt32ForKey("p", orElse: 0), md5Digest: decoder.decodeStringForKey("d", orElse: ""), keyFingerprint: decoder.decodeInt32ForKey("f", orElse: 0))
|
||||
case SecretChatOutgoingFileValue.uploadedLarge.rawValue:
|
||||
self = .uploadedLarge(id: decoder.decodeInt64ForKey("i", orElse: 0), partCount: decoder.decodeInt32ForKey("p", orElse: 0), keyFingerprint: decoder.decodeInt32ForKey("f", orElse: 0))
|
||||
default:
|
||||
assertionFailure()
|
||||
self = .remote(id: 0, accessHash: 0)
|
||||
case SecretChatOutgoingFileValue.remote.rawValue:
|
||||
self = .remote(id: decoder.decodeInt64ForKey("i", orElse: 0), accessHash: decoder.decodeInt64ForKey("a", orElse: 0))
|
||||
case SecretChatOutgoingFileValue.uploadedRegular.rawValue:
|
||||
self = .uploadedRegular(id: decoder.decodeInt64ForKey("i", orElse: 0), partCount: decoder.decodeInt32ForKey("p", orElse: 0), md5Digest: decoder.decodeStringForKey("d", orElse: ""), keyFingerprint: decoder.decodeInt32ForKey("f", orElse: 0))
|
||||
case SecretChatOutgoingFileValue.uploadedLarge.rawValue:
|
||||
self = .uploadedLarge(id: decoder.decodeInt64ForKey("i", orElse: 0), partCount: decoder.decodeInt32ForKey("p", orElse: 0), keyFingerprint: decoder.decodeInt32ForKey("f", orElse: 0))
|
||||
default:
|
||||
assertionFailure()
|
||||
self = .remote(id: 0, accessHash: 0)
|
||||
}
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: StringCodingKey.self)
|
||||
|
||||
switch try container.decode(Int32.self, forKey: "v") {
|
||||
case SecretChatOutgoingFileValue.remote.rawValue:
|
||||
self = .remote(
|
||||
id: try container.decode(Int64.self, forKey: "i"),
|
||||
accessHash: try container.decode(Int64.self, forKey: "a")
|
||||
)
|
||||
case SecretChatOutgoingFileValue.uploadedRegular.rawValue:
|
||||
self = .uploadedRegular(
|
||||
id: try container.decode(Int64.self, forKey: "i"),
|
||||
partCount: try container.decode(Int32.self, forKey: "p"),
|
||||
md5Digest: try container.decode(String.self, forKey: "d"),
|
||||
keyFingerprint: try container.decode(Int32.self, forKey: "f")
|
||||
)
|
||||
case SecretChatOutgoingFileValue.uploadedLarge.rawValue:
|
||||
self = .uploadedLarge(
|
||||
id: try container.decode(Int64.self, forKey: "i"),
|
||||
partCount: try container.decode(Int32.self, forKey: "p"),
|
||||
keyFingerprint: try container.decode(Int32.self, forKey: "f")
|
||||
)
|
||||
default:
|
||||
assertionFailure()
|
||||
self = .remote(id: 0, accessHash: 0)
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
switch self {
|
||||
case let .remote(id, accessHash):
|
||||
encoder.encodeInt32(SecretChatOutgoingFileValue.remote.rawValue, forKey: "v")
|
||||
encoder.encodeInt64(id, forKey: "i")
|
||||
encoder.encodeInt64(accessHash, forKey: "a")
|
||||
case let .uploadedRegular(id, partCount, md5Digest, keyFingerprint):
|
||||
encoder.encodeInt32(SecretChatOutgoingFileValue.uploadedRegular.rawValue, forKey: "v")
|
||||
encoder.encodeInt64(id, forKey: "i")
|
||||
encoder.encodeInt32(partCount, forKey: "p")
|
||||
encoder.encodeString(md5Digest, forKey: "d")
|
||||
encoder.encodeInt32(keyFingerprint, forKey: "f")
|
||||
case let .uploadedLarge(id, partCount, keyFingerprint):
|
||||
encoder.encodeInt32(SecretChatOutgoingFileValue.uploadedLarge.rawValue, forKey: "v")
|
||||
encoder.encodeInt64(id, forKey: "i")
|
||||
encoder.encodeInt32(partCount, forKey: "p")
|
||||
encoder.encodeInt32(keyFingerprint, forKey: "f")
|
||||
case let .remote(id, accessHash):
|
||||
encoder.encodeInt32(SecretChatOutgoingFileValue.remote.rawValue, forKey: "v")
|
||||
encoder.encodeInt64(id, forKey: "i")
|
||||
encoder.encodeInt64(accessHash, forKey: "a")
|
||||
case let .uploadedRegular(id, partCount, md5Digest, keyFingerprint):
|
||||
encoder.encodeInt32(SecretChatOutgoingFileValue.uploadedRegular.rawValue, forKey: "v")
|
||||
encoder.encodeInt64(id, forKey: "i")
|
||||
encoder.encodeInt32(partCount, forKey: "p")
|
||||
encoder.encodeString(md5Digest, forKey: "d")
|
||||
encoder.encodeInt32(keyFingerprint, forKey: "f")
|
||||
case let .uploadedLarge(id, partCount, keyFingerprint):
|
||||
encoder.encodeInt32(SecretChatOutgoingFileValue.uploadedLarge.rawValue, forKey: "v")
|
||||
encoder.encodeInt64(id, forKey: "i")
|
||||
encoder.encodeInt32(partCount, forKey: "p")
|
||||
encoder.encodeInt32(keyFingerprint, forKey: "f")
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: StringCodingKey.self)
|
||||
|
||||
switch self {
|
||||
case let .remote(id, accessHash):
|
||||
try container.encode(SecretChatOutgoingFileValue.remote.rawValue, forKey: "v")
|
||||
try container.encode(id, forKey: "i")
|
||||
try container.encode(accessHash, forKey: "a")
|
||||
case let .uploadedRegular(id, partCount, md5Digest, keyFingerprint):
|
||||
try container.encode(SecretChatOutgoingFileValue.uploadedRegular.rawValue, forKey: "v")
|
||||
try container.encode(id, forKey: "i")
|
||||
try container.encode(partCount, forKey: "p")
|
||||
try container.encode(md5Digest, forKey: "d")
|
||||
try container.encode(keyFingerprint, forKey: "f")
|
||||
case let .uploadedLarge(id, partCount, keyFingerprint):
|
||||
try container.encode(SecretChatOutgoingFileValue.uploadedLarge.rawValue, forKey: "v")
|
||||
try container.encode(id, forKey: "i")
|
||||
try container.encode(partCount, forKey: "p")
|
||||
try container.encode(keyFingerprint, forKey: "f")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct SecretChatOutgoingFile: PostboxCoding {
|
||||
public struct SecretChatOutgoingFile: PostboxCoding, Codable {
|
||||
public let reference: SecretChatOutgoingFileReference
|
||||
public let size: Int64
|
||||
public let key: SecretFileEncryptionKey
|
||||
@ -68,12 +118,32 @@ public struct SecretChatOutgoingFile: PostboxCoding {
|
||||
self.key = SecretFileEncryptionKey(aesKey: decoder.decodeBytesForKey("k")!.makeData(), aesIv: decoder.decodeBytesForKey("i")!.makeData())
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: StringCodingKey.self)
|
||||
|
||||
self.reference = try container.decode(SecretChatOutgoingFileReference.self, forKey: "r")
|
||||
self.size = try container.decode(Int64.self, forKey: "s64")
|
||||
self.key = SecretFileEncryptionKey(
|
||||
aesKey: try container.decode(Data.self, forKey: "k"),
|
||||
aesIv: try container.decode(Data.self, forKey: "i")
|
||||
)
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeObject(self.reference, forKey: "r")
|
||||
encoder.encodeInt64(self.size, forKey: "s64")
|
||||
encoder.encodeBytes(MemoryBuffer(data: self.key.aesKey), forKey: "k")
|
||||
encoder.encodeBytes(MemoryBuffer(data: self.key.aesIv), forKey: "i")
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: StringCodingKey.self)
|
||||
|
||||
try container.encode(self.reference, forKey: "r")
|
||||
try container.encode(self.size, forKey: "s64")
|
||||
try container.encode(self.key.aesKey, forKey: "k")
|
||||
try container.encode(self.key.aesIv, forKey: "i")
|
||||
}
|
||||
}
|
||||
|
||||
public enum SecretChatSequenceBasedLayer: Int32 {
|
||||
@ -112,11 +182,72 @@ private enum SecretChatOutgoingOperationValue: Int32 {
|
||||
case noop = 12
|
||||
case setMessageAutoremoveTimeout = 13
|
||||
case terminate = 14
|
||||
case sendStandaloneMessage = 15
|
||||
}
|
||||
|
||||
public struct StandaloneSecretMessageContents: Codable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case id = "i"
|
||||
case text = "t"
|
||||
case attributes = "a"
|
||||
case media = "m"
|
||||
case file = "f"
|
||||
}
|
||||
|
||||
public var id: Int64
|
||||
public var text: String
|
||||
public var attributes: [MessageAttribute]
|
||||
public var media: Media?
|
||||
public var file: SecretChatOutgoingFile?
|
||||
|
||||
public init(id: Int64, text: String, attributes: [MessageAttribute], media: Media?, file: SecretChatOutgoingFile?) {
|
||||
self.id = id
|
||||
self.text = text
|
||||
self.attributes = attributes
|
||||
self.media = media
|
||||
self.file = file
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self.id = try container.decode(Int64.self, forKey: .id)
|
||||
self.text = try container.decode(String.self, forKey: .text)
|
||||
|
||||
let attributes = try container.decode([Data].self, forKey: .attributes)
|
||||
self.attributes = attributes.compactMap { attribute -> MessageAttribute? in
|
||||
return PostboxDecoder(buffer: MemoryBuffer(data: attribute)).decodeRootObject() as? MessageAttribute
|
||||
}
|
||||
self.media = (try container.decodeIfPresent(Data.self, forKey: .media)).flatMap { media in
|
||||
return PostboxDecoder(buffer: MemoryBuffer(data: media)).decodeRootObject() as? Media
|
||||
}
|
||||
self.file = try container.decodeIfPresent(SecretChatOutgoingFile.self, forKey: .file)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
try container.encode(self.id, forKey: .id)
|
||||
try container.encode(self.text, forKey: .text)
|
||||
let attributes = self.attributes.map { attribute -> Data in
|
||||
let innerEncoder = PostboxEncoder()
|
||||
innerEncoder.encodeRootObject(attribute)
|
||||
return innerEncoder.makeData()
|
||||
}
|
||||
try container.encode(attributes, forKey: .attributes)
|
||||
try container.encodeIfPresent(self.media.flatMap { media in
|
||||
let innerEncoder = PostboxEncoder()
|
||||
innerEncoder.encodeRootObject(media)
|
||||
return innerEncoder.makeData()
|
||||
}, forKey: .media)
|
||||
try container.encodeIfPresent(self.file, forKey: .file)
|
||||
}
|
||||
}
|
||||
|
||||
public enum SecretChatOutgoingOperationContents: PostboxCoding {
|
||||
case initialHandshakeAccept(gA: MemoryBuffer, accessHash: Int64, b: MemoryBuffer)
|
||||
case sendMessage(layer: SecretChatLayer, id: MessageId, file: SecretChatOutgoingFile?)
|
||||
case sendStandaloneMessage(layer: SecretChatLayer, contents: StandaloneSecretMessageContents)
|
||||
case readMessagesContent(layer: SecretChatLayer, actionGloballyUniqueId: Int64, globallyUniqueIds: [Int64])
|
||||
case deleteMessages(layer: SecretChatLayer, actionGloballyUniqueId: Int64, globallyUniqueIds: [Int64])
|
||||
case screenshotMessages(layer: SecretChatLayer, actionGloballyUniqueId: Int64, globallyUniqueIds: [Int64], messageId: MessageId)
|
||||
@ -137,6 +268,11 @@ public enum SecretChatOutgoingOperationContents: PostboxCoding {
|
||||
self = .initialHandshakeAccept(gA: decoder.decodeBytesForKey("g")!, accessHash: decoder.decodeInt64ForKey("h", orElse: 0), b: decoder.decodeBytesForKey("b")!)
|
||||
case SecretChatOutgoingOperationValue.sendMessage.rawValue:
|
||||
self = .sendMessage(layer: SecretChatLayer(rawValue: decoder.decodeInt32ForKey("l", orElse: 0))!, id: MessageId(peerId: PeerId(decoder.decodeInt64ForKey("i.p", orElse: 0)), namespace: decoder.decodeInt32ForKey("i.n", orElse: 0), id: decoder.decodeInt32ForKey("i.i", orElse: 0)), file: decoder.decodeObjectForKey("f", decoder: { SecretChatOutgoingFile(decoder: $0) }) as? SecretChatOutgoingFile)
|
||||
case SecretChatOutgoingOperationValue.sendStandaloneMessage.rawValue:
|
||||
self = .sendStandaloneMessage(
|
||||
layer: SecretChatLayer(rawValue: decoder.decodeInt32ForKey("l", orElse: 0))!,
|
||||
contents: decoder.decodeCodable(StandaloneSecretMessageContents.self, forKey: "c") ?? StandaloneSecretMessageContents(id: 0, text: "", attributes: [], media: nil, file: nil)
|
||||
)
|
||||
case SecretChatOutgoingOperationValue.readMessagesContent.rawValue:
|
||||
self = .readMessagesContent(layer: SecretChatLayer(rawValue: decoder.decodeInt32ForKey("l", orElse: 0))!, actionGloballyUniqueId: decoder.decodeInt64ForKey("i", orElse: 0), globallyUniqueIds: decoder.decodeInt64ArrayForKey("u"))
|
||||
case SecretChatOutgoingOperationValue.deleteMessages.rawValue:
|
||||
@ -187,6 +323,10 @@ public enum SecretChatOutgoingOperationContents: PostboxCoding {
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "f")
|
||||
}
|
||||
case let .sendStandaloneMessage(layer, contents):
|
||||
encoder.encodeInt32(SecretChatOutgoingOperationValue.sendStandaloneMessage.rawValue, forKey: "r")
|
||||
encoder.encodeInt32(layer.rawValue, forKey: "l")
|
||||
encoder.encodeCodable(contents, forKey: "c")
|
||||
case let .readMessagesContent(layer, actionGloballyUniqueId, globallyUniqueIds):
|
||||
encoder.encodeInt32(SecretChatOutgoingOperationValue.readMessagesContent.rawValue, forKey: "r")
|
||||
encoder.encodeInt32(layer.rawValue, forKey: "l")
|
||||
|
@ -520,7 +520,7 @@ func urlPreviewStateForInputText(_ inputText: NSAttributedString?, context: Acco
|
||||
}
|
||||
if let _ = dataDetector {
|
||||
let detectedUrls = detectUrls(inputText)
|
||||
if detectedUrls != currentQuery?.detectedUrls {
|
||||
if detectedUrls != (currentQuery?.detectedUrls ?? []) {
|
||||
if !detectedUrls.isEmpty {
|
||||
return (UrlPreviewState(detectedUrls: detectedUrls), webpagePreview(account: context.account, urls: detectedUrls)
|
||||
|> mapToSignal { result -> Signal<(TelegramMediaWebpage, String)?, NoError> in
|
||||
|
@ -3741,17 +3741,19 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
|
||||
} else {
|
||||
var children: [UIAction] = []
|
||||
|
||||
children.append(UIAction(title: self.strings?.TextFormat_Quote ?? "Quote", image: nil) { [weak self] (action) in
|
||||
if let strongSelf = self {
|
||||
strongSelf.formatAttributesQuote(strongSelf)
|
||||
}
|
||||
})
|
||||
|
||||
var hasSpoilers = true
|
||||
if self.presentationInterfaceState?.chatLocation.peerId?.namespace == Namespaces.Peer.SecretChat {
|
||||
hasSpoilers = false
|
||||
}
|
||||
|
||||
if hasSpoilers {
|
||||
children.append(UIAction(title: self.strings?.TextFormat_Quote ?? "Quote", image: nil) { [weak self] (action) in
|
||||
if let strongSelf = self {
|
||||
strongSelf.formatAttributesQuote(strongSelf)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if hasSpoilers {
|
||||
children.append(UIAction(title: self.strings?.TextFormat_Spoiler ?? "Spoiler", image: nil) { [weak self] (action) in
|
||||
if let strongSelf = self {
|
||||
|
Loading…
x
Reference in New Issue
Block a user