mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Add experimental standalone share
This commit is contained in:
parent
7a1cc644a0
commit
eaaade13f2
File diff suppressed because it is too large
Load Diff
@ -24,6 +24,426 @@ public enum StandaloneSendMessageError {
|
||||
case generic
|
||||
}
|
||||
|
||||
public enum StandaloneSendMessageStatus {
|
||||
case progress(Float)
|
||||
case done
|
||||
}
|
||||
|
||||
public struct StandaloneSendMessagesError {
|
||||
public var peerId: PeerId
|
||||
public var reason: PendingMessageFailureReason?
|
||||
|
||||
init(
|
||||
peerId: PeerId,
|
||||
reason: PendingMessageFailureReason?
|
||||
) {
|
||||
self.peerId = peerId
|
||||
self.reason = reason
|
||||
}
|
||||
}
|
||||
|
||||
public struct StandaloneSendEnqueueMessage {
|
||||
public struct Text {
|
||||
public var string: String
|
||||
public var entities: [MessageTextEntity]
|
||||
|
||||
public init(
|
||||
string: String,
|
||||
entities: [MessageTextEntity]
|
||||
) {
|
||||
self.string = string
|
||||
self.entities = entities
|
||||
}
|
||||
}
|
||||
|
||||
public struct Image {
|
||||
public var representation: TelegramMediaImageRepresentation
|
||||
|
||||
public init(
|
||||
representation: TelegramMediaImageRepresentation
|
||||
) {
|
||||
self.representation = representation
|
||||
}
|
||||
}
|
||||
|
||||
public struct Forward {
|
||||
public var sourceId: MessageId
|
||||
public var threadId: Int64?
|
||||
|
||||
public init(
|
||||
sourceId: MessageId,
|
||||
threadId: Int64?
|
||||
) {
|
||||
self.sourceId = sourceId
|
||||
self.threadId = threadId
|
||||
}
|
||||
}
|
||||
|
||||
public struct ForwardOptions {
|
||||
public var hideNames: Bool
|
||||
public var hideCaptions: Bool
|
||||
|
||||
public init(
|
||||
hideNames: Bool,
|
||||
hideCaptions: Bool
|
||||
) {
|
||||
self.hideNames = hideNames
|
||||
self.hideCaptions = hideCaptions
|
||||
}
|
||||
}
|
||||
|
||||
public enum Content {
|
||||
case text(text: Text)
|
||||
case image(image: Image, text: Text)
|
||||
case map(map: TelegramMediaMap)
|
||||
case arbitraryMedia(media: AnyMediaReference, text: Text)
|
||||
case forward(forward: Forward)
|
||||
}
|
||||
|
||||
public var content: Content
|
||||
public var replyToMessageId: MessageId?
|
||||
public var forwardOptions: ForwardOptions?
|
||||
public var isSilent: Bool = false
|
||||
|
||||
public init(
|
||||
content: Content,
|
||||
replyToMessageId: MessageId?
|
||||
) {
|
||||
self.content = content
|
||||
self.replyToMessageId = replyToMessageId
|
||||
}
|
||||
}
|
||||
|
||||
public func standaloneSendEnqueueMessages(account: Account, peerId: PeerId, messages: [StandaloneSendEnqueueMessage]) -> Signal<StandaloneSendMessageStatus, StandaloneSendMessagesError> {
|
||||
#if !DEBUG
|
||||
error
|
||||
#endif
|
||||
|
||||
let signals: [Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>] = messages.map { message in
|
||||
var attributes: [MessageAttribute] = []
|
||||
var text: String = ""
|
||||
var media: [Media] = []
|
||||
|
||||
switch message.content {
|
||||
case let .text(textValue):
|
||||
text = textValue.string
|
||||
if !textValue.entities.isEmpty {
|
||||
attributes.append(TextEntitiesMessageAttribute(entities: textValue.entities))
|
||||
}
|
||||
case let .image(image, textValue):
|
||||
media.append(TelegramMediaImage(
|
||||
imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: MediaId.Id.random(in: Int64.min ... Int64.max)),
|
||||
representations: [image.representation],
|
||||
immediateThumbnailData: nil,
|
||||
reference: nil,
|
||||
partialReference: nil,
|
||||
flags: []
|
||||
))
|
||||
|
||||
text = textValue.string
|
||||
if !textValue.entities.isEmpty {
|
||||
attributes.append(TextEntitiesMessageAttribute(entities: textValue.entities))
|
||||
}
|
||||
case let .map(mapValue):
|
||||
media.append(mapValue)
|
||||
case let .arbitraryMedia(mediaValue, textValue):
|
||||
media.append(mediaValue.media)
|
||||
|
||||
text = textValue.string
|
||||
if !textValue.entities.isEmpty {
|
||||
attributes.append(TextEntitiesMessageAttribute(entities: textValue.entities))
|
||||
}
|
||||
case let .forward(forwardValue):
|
||||
attributes.append(ForwardSourceInfoAttribute(messageId: forwardValue.sourceId))
|
||||
}
|
||||
|
||||
if let replyToMessageId = message.replyToMessageId {
|
||||
attributes.append(ReplyMessageAttribute(messageId: replyToMessageId, threadMessageId: nil))
|
||||
}
|
||||
if let forwardOptions = message.forwardOptions {
|
||||
attributes.append(ForwardOptionsMessageAttribute(hideNames: forwardOptions.hideNames, hideCaptions: forwardOptions.hideCaptions))
|
||||
}
|
||||
if message.isSilent {
|
||||
attributes.append(NotificationInfoMessageAttribute(flags: .muted))
|
||||
}
|
||||
|
||||
let content = messageContentToUpload(accountPeerId: account.peerId, network: account.network, postbox: account.postbox, auxiliaryMethods: account.auxiliaryMethods, transformOutgoingMessageMedia: account.transformOutgoingMessageMedia, messageMediaPreuploadManager: account.messageMediaPreuploadManager, revalidationContext: account.mediaReferenceRevalidationContext, forceReupload: false, isGrouped: false, passFetchProgress: true, forceNoBigParts: false, peerId: peerId, messageId: nil, attributes: attributes, text: text, media: media)
|
||||
let contentResult: Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>
|
||||
switch content {
|
||||
case let .signal(value, _):
|
||||
contentResult = value
|
||||
case let .immediate(value, _):
|
||||
contentResult = .single(value)
|
||||
}
|
||||
return contentResult
|
||||
}
|
||||
|
||||
return combineLatest(signals)
|
||||
|> mapError { _ -> StandaloneSendMessagesError in
|
||||
return StandaloneSendMessagesError(peerId: peerId, reason: nil)
|
||||
}
|
||||
|> mapToSignal { contentResults -> Signal<StandaloneSendMessageStatus, StandaloneSendMessagesError> in
|
||||
var progressSum: Float = 0.0
|
||||
var allResults: [PendingMessageUploadedContentAndReuploadInfo] = []
|
||||
var allDone = true
|
||||
for status in contentResults {
|
||||
switch status {
|
||||
case let .progress(value):
|
||||
allDone = false
|
||||
progressSum += value
|
||||
case let .content(content):
|
||||
allResults.append(content)
|
||||
}
|
||||
}
|
||||
if allDone {
|
||||
var sendSignals: [Signal<Never, StandaloneSendMessagesError>] = []
|
||||
sendSignals.removeAll()
|
||||
|
||||
for content in allResults {
|
||||
switch content.content {
|
||||
case let .text(text):
|
||||
let _ = text
|
||||
preconditionFailure()
|
||||
case let .media(inputMedia, text):
|
||||
let _ = inputMedia
|
||||
let _ = text
|
||||
preconditionFailure()
|
||||
case let .forward(source):
|
||||
let _ = source
|
||||
preconditionFailure()
|
||||
case let .chatContextResult(result):
|
||||
let _ = result
|
||||
preconditionFailure()
|
||||
case let .secretMedia(inputFile, size, key):
|
||||
let _ = inputFile
|
||||
let _ = size
|
||||
let _ = key
|
||||
preconditionFailure()
|
||||
case .messageScreenshot:
|
||||
return .single(.done)
|
||||
}
|
||||
}
|
||||
|
||||
return combineLatest(sendSignals)
|
||||
|> ignoreValues
|
||||
|> map { _ -> StandaloneSendMessageStatus in
|
||||
}
|
||||
|> then(.single(.done))
|
||||
} else {
|
||||
return .single(.progress(progressSum / max(1.0, Float(contentResults.count))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func sendUploadedMessageContent(postbox: Postbox, network: Network, stateManager: AccountStateManager, accountPeerId: PeerId, peerId: PeerId, content: PendingMessageUploadedContentAndReuploadInfo, text: String, attributes: [MessageAttribute], 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()
|
||||
} else if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||
var uniqueId: Int64 = 0
|
||||
var forwardSourceInfoAttribute: ForwardSourceInfoAttribute?
|
||||
var messageEntities: [Api.MessageEntity]?
|
||||
var replyMessageId: Int32?
|
||||
var replyToStoryId: StoryId?
|
||||
var scheduleTime: Int32?
|
||||
var sendAsPeerId: PeerId?
|
||||
var bubbleUpEmojiOrStickersets = false
|
||||
|
||||
var flags: Int32 = 0
|
||||
|
||||
for attribute in attributes {
|
||||
if let replyAttribute = attribute as? ReplyMessageAttribute {
|
||||
replyMessageId = replyAttribute.messageId.id
|
||||
} else if let attribute = attribute as? ReplyStoryAttribute {
|
||||
replyToStoryId = attribute.storyId
|
||||
} else if let outgoingInfo = attribute as? OutgoingMessageInfoAttribute {
|
||||
uniqueId = outgoingInfo.uniqueId
|
||||
bubbleUpEmojiOrStickersets = !outgoingInfo.bubbleUpEmojiOrStickersets.isEmpty
|
||||
} else if let attribute = attribute as? ForwardSourceInfoAttribute {
|
||||
forwardSourceInfoAttribute = attribute
|
||||
} else if let attribute = attribute as? TextEntitiesMessageAttribute {
|
||||
var associatedPeers = SimpleDictionary<PeerId, Peer>()
|
||||
for attributePeerId in attribute.associatedPeerIds {
|
||||
if let peer = transaction.getPeer(attributePeerId) {
|
||||
associatedPeers[peer.id] = peer
|
||||
}
|
||||
}
|
||||
messageEntities = apiTextAttributeEntities(attribute, associatedPeers: associatedPeers)
|
||||
} else if let attribute = attribute as? OutgoingContentInfoMessageAttribute {
|
||||
if attribute.flags.contains(.disableLinkPreviews) {
|
||||
flags |= Int32(1 << 1)
|
||||
}
|
||||
} else if let attribute = attribute as? NotificationInfoMessageAttribute {
|
||||
if attribute.flags.contains(.muted) {
|
||||
flags |= Int32(1 << 5)
|
||||
}
|
||||
} else if let attribute = attribute as? OutgoingScheduleInfoMessageAttribute {
|
||||
flags |= Int32(1 << 10)
|
||||
scheduleTime = attribute.scheduleTime
|
||||
} else if let attribute = attribute as? SendAsMessageAttribute {
|
||||
sendAsPeerId = attribute.peerId
|
||||
}
|
||||
}
|
||||
|
||||
if case .forward = content.content {
|
||||
} else {
|
||||
flags |= (1 << 7)
|
||||
|
||||
if let _ = replyMessageId {
|
||||
flags |= Int32(1 << 0)
|
||||
}
|
||||
if let _ = messageEntities {
|
||||
flags |= Int32(1 << 3)
|
||||
}
|
||||
}
|
||||
|
||||
var sendAsInputPeer: Api.InputPeer?
|
||||
if let sendAsPeerId = sendAsPeerId, let sendAsPeer = transaction.getPeer(sendAsPeerId), let inputPeer = apiInputPeerOrSelf(sendAsPeer, accountPeerId: accountPeerId) {
|
||||
sendAsInputPeer = inputPeer
|
||||
flags |= (1 << 13)
|
||||
}
|
||||
|
||||
let dependencyTag: PendingMessageRequestDependencyTag? = nil//(messageId: messageId)
|
||||
|
||||
let sendMessageRequest: Signal<NetworkRequestResult<Api.Updates>, MTRpcError>
|
||||
switch content.content {
|
||||
case .text:
|
||||
if bubbleUpEmojiOrStickersets {
|
||||
flags |= Int32(1 << 15)
|
||||
}
|
||||
|
||||
var replyTo: Api.InputReplyTo?
|
||||
if let replyMessageId = replyMessageId {
|
||||
flags |= 1 << 0
|
||||
|
||||
var replyFlags: Int32 = 0
|
||||
if threadId != nil {
|
||||
replyFlags |= 1 << 0
|
||||
}
|
||||
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: threadId.flatMap(Int32.init(clamping:)))
|
||||
} else if let replyToStoryId = replyToStoryId {
|
||||
if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) {
|
||||
flags |= 1 << 0
|
||||
replyTo = .inputReplyToStory(userId: inputUser, storyId: replyToStoryId.id)
|
||||
}
|
||||
}
|
||||
|
||||
sendMessageRequest = network.requestWithAdditionalInfo(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer), info: .acknowledgement, tag: dependencyTag)
|
||||
case let .media(inputMedia, text):
|
||||
if bubbleUpEmojiOrStickersets {
|
||||
flags |= Int32(1 << 15)
|
||||
}
|
||||
|
||||
var replyTo: Api.InputReplyTo?
|
||||
if let replyMessageId = replyMessageId {
|
||||
flags |= 1 << 0
|
||||
|
||||
var replyFlags: Int32 = 0
|
||||
if threadId != nil {
|
||||
replyFlags |= 1 << 0
|
||||
}
|
||||
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: threadId.flatMap(Int32.init(clamping:)))
|
||||
} else if let replyToStoryId = replyToStoryId {
|
||||
if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) {
|
||||
flags |= 1 << 0
|
||||
replyTo = .inputReplyToStory(userId: inputUser, storyId: replyToStoryId.id)
|
||||
}
|
||||
}
|
||||
|
||||
sendMessageRequest = network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyTo: replyTo, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer), tag: dependencyTag)
|
||||
|> map(NetworkRequestResult.result)
|
||||
case let .forward(sourceInfo):
|
||||
var topMsgId: Int32?
|
||||
if let threadId = threadId {
|
||||
flags |= Int32(1 << 9)
|
||||
topMsgId = Int32(clamping: threadId)
|
||||
}
|
||||
|
||||
if let forwardSourceInfoAttribute = forwardSourceInfoAttribute, let sourcePeer = transaction.getPeer(forwardSourceInfoAttribute.messageId.peerId), let sourceInputPeer = apiInputPeer(sourcePeer) {
|
||||
sendMessageRequest = network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: sourceInputPeer, id: [sourceInfo.messageId.id], randomId: [uniqueId], toPeer: inputPeer, topMsgId: topMsgId, scheduleDate: scheduleTime, sendAs: sendAsInputPeer), tag: dependencyTag)
|
||||
|> map(NetworkRequestResult.result)
|
||||
} else {
|
||||
sendMessageRequest = .fail(MTRpcError(errorCode: 400, errorDescription: "internal"))
|
||||
}
|
||||
case let .chatContextResult(chatContextResult):
|
||||
if chatContextResult.hideVia {
|
||||
flags |= Int32(1 << 11)
|
||||
}
|
||||
|
||||
var replyTo: Api.InputReplyTo?
|
||||
if let replyMessageId = replyMessageId {
|
||||
flags |= 1 << 0
|
||||
|
||||
var replyFlags: Int32 = 0
|
||||
if threadId != nil {
|
||||
replyFlags |= 1 << 0
|
||||
}
|
||||
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: threadId.flatMap(Int32.init(clamping:)))
|
||||
} else if let replyToStoryId = replyToStoryId {
|
||||
if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) {
|
||||
flags |= 1 << 0
|
||||
replyTo = .inputReplyToStory(userId: inputUser, storyId: replyToStoryId.id)
|
||||
}
|
||||
}
|
||||
|
||||
sendMessageRequest = network.request(Api.functions.messages.sendInlineBotResult(flags: flags, peer: inputPeer, replyTo: replyTo, randomId: uniqueId, queryId: chatContextResult.queryId, id: chatContextResult.id, scheduleDate: scheduleTime, sendAs: sendAsInputPeer))
|
||||
|> map(NetworkRequestResult.result)
|
||||
case .messageScreenshot:
|
||||
let replyTo: Api.InputReplyTo
|
||||
|
||||
if let replyMessageId = replyMessageId {
|
||||
let replyFlags: Int32 = 0
|
||||
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil)
|
||||
} else if let replyToStoryId = replyToStoryId {
|
||||
if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) {
|
||||
flags |= 1 << 0
|
||||
replyTo = .inputReplyToStory(userId: inputUser, storyId: replyToStoryId.id)
|
||||
} else {
|
||||
let replyFlags: Int32 = 0
|
||||
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil)
|
||||
}
|
||||
} else {
|
||||
let replyFlags: Int32 = 0
|
||||
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil)
|
||||
}
|
||||
|
||||
sendMessageRequest = network.request(Api.functions.messages.sendScreenshotNotification(peer: inputPeer, replyTo: replyTo, randomId: uniqueId))
|
||||
|> map(NetworkRequestResult.result)
|
||||
case .secretMedia:
|
||||
assertionFailure()
|
||||
sendMessageRequest = .fail(MTRpcError(errorCode: 400, errorDescription: "internal"))
|
||||
}
|
||||
|
||||
return sendMessageRequest
|
||||
|> mapToSignal { result -> Signal<Never, MTRpcError> in
|
||||
switch result {
|
||||
case .progress:
|
||||
return .complete()
|
||||
case .acknowledged:
|
||||
return .complete()
|
||||
case let .result(result):
|
||||
stateManager.addUpdates(result)
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|> mapError { error -> StandaloneSendMessagesError in
|
||||
if error.errorDescription.hasPrefix("FILEREF_INVALID") || error.errorDescription.hasPrefix("FILE_REFERENCE_") {
|
||||
return StandaloneSendMessagesError(peerId: peerId, reason: nil)
|
||||
} else if let failureReason = sendMessageReasonForError(error.errorDescription) {
|
||||
return StandaloneSendMessagesError(peerId: peerId, reason: failureReason)
|
||||
}
|
||||
return StandaloneSendMessagesError(peerId: peerId, reason: nil)
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|> castError(StandaloneSendMessagesError.self)
|
||||
|> switchToLatest
|
||||
}
|
||||
|
||||
public func standaloneSendMessage(account: Account, peerId: PeerId, text: String, attributes: [MessageAttribute], media: StandaloneMedia?, replyToMessageId: MessageId?) -> Signal<Float, StandaloneSendMessageError> {
|
||||
let content: Signal<StandaloneSendMessageEvent, StandaloneSendMessageError>
|
||||
if let media = media {
|
||||
|
@ -60,7 +60,7 @@ public enum PendingMessageFailureReason {
|
||||
case sendingTooFast
|
||||
}
|
||||
|
||||
private func reasonForError(_ error: String) -> PendingMessageFailureReason? {
|
||||
func sendMessageReasonForError(_ error: String) -> PendingMessageFailureReason? {
|
||||
if error.hasPrefix("PEER_FLOOD") {
|
||||
return .flood
|
||||
} else if error.hasPrefix("SENDING_TOO_FAST") {
|
||||
@ -125,7 +125,7 @@ private func failMessages(postbox: Postbox, ids: [MessageId]) -> Signal<Void, No
|
||||
return modify
|
||||
}
|
||||
|
||||
private final class PendingMessageRequestDependencyTag: NetworkRequestDependencyTag {
|
||||
final class PendingMessageRequestDependencyTag: NetworkRequestDependencyTag {
|
||||
let messageId: MessageId
|
||||
|
||||
init(messageId: MessageId) {
|
||||
@ -970,7 +970,7 @@ public final class PendingMessageManager {
|
||||
strongSelf.beginSendingMessages(messages.map({ $0.0.id }))
|
||||
return .complete()
|
||||
}
|
||||
} else if let failureReason = reasonForError(error.errorDescription), let message = messages.first?.0 {
|
||||
} else if let failureReason = sendMessageReasonForError(error.errorDescription), let message = messages.first?.0 {
|
||||
for (message, _) in messages {
|
||||
if let context = strongSelf.messageContexts[message.id] {
|
||||
context.error = failureReason
|
||||
@ -1292,7 +1292,7 @@ public final class PendingMessageManager {
|
||||
strongSelf.beginSendingMessages([messageId])
|
||||
return
|
||||
}
|
||||
} else if let failureReason = reasonForError(error.errorDescription) {
|
||||
} else if let failureReason = sendMessageReasonForError(error.errorDescription) {
|
||||
if let context = strongSelf.messageContexts[message.id] {
|
||||
context.error = failureReason
|
||||
for f in context.statusSubscribers.copyItems() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user