diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index 562fd75d7b..f3b203ad23 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -832,433 +832,24 @@ public final class ShareController: ViewController { return .complete() } - return self.currentContext.engine.data.get(EngineDataMap( - peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)) - )) - |> deliverOnMainQueue - |> castError(ShareControllerError.self) - |> mapToSignal { [weak self] peers -> Signal in - guard let strongSelf = self else { - return .complete() - } - - var shareSignals: [Signal<[MessageId?], NoError>] = [] - var subject = strongSelf.subject - if let segmentedValues = strongSelf.segmentedValues { - let selectedValue = segmentedValues[strongSelf.controllerNode.selectedSegmentedIndex] - subject = selectedValue.subject - } - - func transformMessages(_ messages: [EnqueueMessage], showNames: Bool, silently: Bool) -> [EnqueueMessage] { - return messages.map { message in - return message.withUpdatedAttributes({ attributes in - var attributes = attributes - if !showNames { - attributes.append(ForwardOptionsMessageAttribute(hideNames: true, hideCaptions: false)) - } - if silently { - attributes.append(NotificationInfoMessageAttribute(flags: .muted)) - } - return attributes - }) - } - } - - switch subject { - case let .url(url): - for peerId in peerIds { - guard let maybePeer = peers[peerId], let peer = maybePeer else { - continue - } - - var banSendText = false - if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil { - banSendText = true - } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) { - banSendText = true - } - - if banSendText { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - - return .fail(.generic) - } - - var replyToMessageId: MessageId? - if let topicId = topicIds[peerId] { - replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) - } - - var messages: [EnqueueMessage] = [] - if !text.isEmpty { - messages.append(.message(text: url + "\n\n" + text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - } else { - messages.append(.message(text: url, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - } - messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) - } - case let .text(string): - for peerId in peerIds { - guard let maybePeer = peers[peerId], let peer = maybePeer else { - continue - } - - var banSendText = false - if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil { - banSendText = true - } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) { - banSendText = true - } - - if banSendText { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - - return .fail(.generic) - } - - var replyToMessageId: MessageId? - if let topicId = topicIds[peerId] { - replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) - } - - var messages: [EnqueueMessage] = [] - if !text.isEmpty { - messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - } - messages.append(.message(text: string, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) - } - case let .quote(string, url): - for peerId in peerIds { - guard let maybePeer = peers[peerId], let peer = maybePeer else { - continue - } - - var banSendText = false - if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil { - banSendText = true - } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) { - banSendText = true - } - - if banSendText { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - - return .fail(.generic) - } - - var replyToMessageId: MessageId? - if let topicId = topicIds[peerId] { - replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) - } - - var messages: [EnqueueMessage] = [] - if !text.isEmpty { - messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - } - let attributedText = NSMutableAttributedString(string: string, attributes: [ChatTextInputAttributes.italic: true as NSNumber]) - attributedText.append(NSAttributedString(string: "\n\n\(url)")) - let entities = generateChatInputTextEntities(attributedText) - messages.append(.message(text: attributedText.string, attributes: [TextEntitiesMessageAttribute(entities: entities)], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) - } - case let .image(representations): - for peerId in peerIds { - guard let maybePeer = peers[peerId], let peer = maybePeer else { - continue - } - - var banSendPhotos = false - if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendPhotos) != nil { - banSendPhotos = true - } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendPhotos) { - banSendPhotos = true - } - - if banSendPhotos { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - - return .fail(.generic) - } - - var replyToMessageId: MessageId? - if let topicId = topicIds[peerId] { - replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) - } - - var messages: [EnqueueMessage] = [] - messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: Int64.random(in: Int64.min ... Int64.max)), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])), replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) - } - case let .media(mediaReference): - var sendTextAsCaption = false - if mediaReference.media is TelegramMediaImage || mediaReference.media is TelegramMediaFile { - sendTextAsCaption = true - } - - for peerId in peerIds { - guard let maybePeer = peers[peerId], let peer = maybePeer else { - continue - } - - var banSendType = false - if mediaReference.media is TelegramMediaImage { - if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendPhotos) != nil { - banSendType = true - } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendPhotos) { - banSendType = true - } - } else if let file = mediaReference.media as? TelegramMediaFile { - if file.isSticker || file.isAnimated { - if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendStickers) != nil { - banSendType = true - } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendStickers) { - banSendType = true - } - } else if file.isInstantVideo { - if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendInstantVideos) != nil { - banSendType = true - } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendInstantVideos) { - banSendType = true - } - } else if file.isVoice { - if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendVoice) != nil { - banSendType = true - } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendVoice) { - banSendType = true - } - } else if file.isMusic { - if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendMusic) != nil { - banSendType = true - } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendMusic) { - banSendType = true - } - } else if file.isVideo { - if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendVideos) != nil { - banSendType = true - } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendVideos) { - banSendType = true - } - } else { - if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendFiles) != nil { - banSendType = true - } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendFiles) { - banSendType = true - } - } - } - - if banSendType { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - - return .fail(.generic) - } - - var replyToMessageId: MessageId? - if let topicId = topicIds[peerId] { - replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) - } - - var messages: [EnqueueMessage] = [] - if !text.isEmpty && !sendTextAsCaption { - messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - } - messages.append(.message(text: sendTextAsCaption ? text : "", attributes: [], inlineStickers: [:], mediaReference: mediaReference, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) - } - case let .mapMedia(media): - for peerId in peerIds { - guard let maybePeer = peers[peerId], let peer = maybePeer else { - continue - } - - var banSendText = false - if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil { - banSendText = true - } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) { - banSendText = true - } - - if banSendText { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - - return .fail(.generic) - } - - var replyToMessageId: MessageId? - if let topicId = topicIds[peerId] { - replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) - } - - var messages: [EnqueueMessage] = [] - if !text.isEmpty { - messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - } - messages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) - } - case let .messages(messages): - for peerId in peerIds { - guard let maybePeer = peers[peerId], let peer = maybePeer else { - continue - } - - var replyToMessageId: MessageId? - var threadId: Int64? - if let topicId = topicIds[peerId] { - replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) - threadId = topicId - } - - var messagesToEnqueue: [EnqueueMessage] = [] - if !text.isEmpty { - var banSendText = false - if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil { - banSendText = true - } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) { - banSendText = true - } - - if banSendText { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - - return .fail(.generic) - } - - messagesToEnqueue.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - } - for message in messages { - for media in message.media { - var banSendType = false - if media is TelegramMediaImage { - if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendPhotos) != nil { - banSendType = true - } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendPhotos) { - banSendType = true - } - } else if let file = media as? TelegramMediaFile { - if file.isSticker || file.isAnimated { - if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendStickers) != nil { - banSendType = true - } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendStickers) { - banSendType = true - } - } else if file.isInstantVideo { - if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendInstantVideos) != nil { - banSendType = true - } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendInstantVideos) { - banSendType = true - } - } else if file.isVoice { - if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendVoice) != nil { - banSendType = true - } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendVoice) { - banSendType = true - } - } else if file.isMusic { - if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendMusic) != nil { - banSendType = true - } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendMusic) { - banSendType = true - } - } else if file.isVideo { - if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendVideos) != nil { - banSendType = true - } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendVideos) { - banSendType = true - } - } else { - if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendFiles) != nil { - banSendType = true - } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendFiles) { - banSendType = true - } - } - } - - if banSendType { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - - return .fail(.generic) - } - } - - messagesToEnqueue.append(.forward(source: message.id, threadId: threadId, grouping: .auto, attributes: [], correlationId: nil)) - } - messagesToEnqueue = transformMessages(messagesToEnqueue, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messagesToEnqueue)) - } - case let .fromExternal(f): - return f(peerIds, topicIds, text, strongSelf.currentAccount, silently) - |> map { state -> ShareState in - switch state { - case let .preparing(long): - return .preparing(long) - case let .progress(value): - return .progress(value) - case .done: - return .done - } - } - } - let account = strongSelf.currentAccount - let queue = Queue.mainQueue() - var displayedError = false - return combineLatest(queue: queue, shareSignals) - |> castError(ShareControllerError.self) - |> mapToSignal { messageIdSets -> Signal in - var statuses: [Signal<(MessageId, PendingMessageStatus?, PendingMessageFailureReason?), ShareControllerError>] = [] - for messageIds in messageIdSets { - for case let id? in messageIds { - statuses.append(account.pendingMessageManager.pendingMessageStatus(id) - |> castError(ShareControllerError.self) - |> map { status, error -> (MessageId, PendingMessageStatus?, PendingMessageFailureReason?) in - return (id, status, error) - }) - } - } - return combineLatest(queue: queue, statuses) - |> mapToSignal { statuses -> Signal in - var hasStatuses = false - for (id, status, error) in statuses { - if let error = error { - Queue.mainQueue().async { - let _ = TelegramEngine(account: account).messages.deleteMessagesInteractively(messageIds: [id], type: .forEveryone).start() - let _ = (TelegramEngine(account: account).data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: id.peerId)) - |> deliverOnMainQueue).start(next: { peer in - guard let strongSelf = self, let peer = peer else { - return - } - if !displayedError { - if case .slowmodeActive = error { - displayedError = true - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: strongSelf.presentationData.strings.Chat_SlowmodeSendError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - } else if case .mediaRestricted = error { - displayedError = true - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - } - } - }) - } - } - if status != nil { - hasStatuses = true - } - } - if !hasStatuses { - return .single(.done) - } - return .complete() - } - |> take(1) + var useLegacy = false + if self.sharedContext.applicationBindings.isMainApp { + useLegacy = true + } + if peerIds.contains(where: { $0.namespace == Namespaces.Peer.SecretChat }) { + useLegacy = true + } + if let data = self.currentContext.currentAppConfiguration.with({ $0 }).data { + if let _ = data["ios_disable_modern_sharing"] { + useLegacy = true } } + + if useLegacy { + return self.shareLegacy(text: text, peerIds: peerIds, topicIds: topicIds, showNames: showNames, silently: silently) + } else { + return self.shareModern(text: text, peerIds: peerIds, topicIds: topicIds, showNames: showNames, silently: silently) + } } self.controllerNode.shareExternal = { [weak self] _ in if let strongSelf = self { @@ -1442,6 +1033,937 @@ public final class ShareController: ViewController { super.loadView() } + private func shareModern(text: String, peerIds: [EnginePeer.Id], topicIds: [EnginePeer.Id: Int64], showNames: Bool, silently: Bool) -> Signal { + return self.currentContext.engine.data.get(EngineDataMap( + peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)) + )) + |> deliverOnMainQueue + |> castError(ShareControllerError.self) + |> mapToSignal { [weak self] peers -> Signal in + guard let strongSelf = self else { + return .complete() + } + + var shareSignals: [Signal] = [] + var subject = strongSelf.subject + if let segmentedValues = strongSelf.segmentedValues { + let selectedValue = segmentedValues[strongSelf.controllerNode.selectedSegmentedIndex] + subject = selectedValue.subject + } + + func transformMessages(_ messages: [StandaloneSendEnqueueMessage], showNames: Bool, silently: Bool) -> [StandaloneSendEnqueueMessage] { + return messages.map { message in + var message = message + if !showNames { + message.forwardOptions = StandaloneSendEnqueueMessage.ForwardOptions( + hideNames: true, + hideCaptions: false + ) + } + if silently { + message.isSilent = true + } + return message + } + } + + switch subject { + case let .url(url): + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var banSendText = false + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil { + banSendText = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) { + banSendText = true + } + + if banSendText { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .fail(.generic) + } + + var replyToMessageId: MessageId? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + } + + var messages: [StandaloneSendEnqueueMessage] = [] + if !text.isEmpty { + messages.append(StandaloneSendEnqueueMessage( + content: .text(text: StandaloneSendEnqueueMessage.Text( + string: url + "\n\n" + text, + entities: [] + )), + replyToMessageId: replyToMessageId + )) + } else { + messages.append(StandaloneSendEnqueueMessage( + content: .text(text: StandaloneSendEnqueueMessage.Text( + string: url, + entities: [] + )), + replyToMessageId: replyToMessageId + )) + } + messages = transformMessages(messages, showNames: showNames, silently: silently) + shareSignals.append(standaloneSendEnqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + } + case let .text(string): + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var banSendText = false + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil { + banSendText = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) { + banSendText = true + } + + if banSendText { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .fail(.generic) + } + + var replyToMessageId: MessageId? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + } + + var messages: [StandaloneSendEnqueueMessage] = [] + if !text.isEmpty { + messages.append(StandaloneSendEnqueueMessage( + content: .text(text: StandaloneSendEnqueueMessage.Text( + string: text, + entities: [] + )), + replyToMessageId: replyToMessageId + )) + } + messages.append(StandaloneSendEnqueueMessage( + content: .text(text: StandaloneSendEnqueueMessage.Text( + string: string, + entities: [] + )), + replyToMessageId: replyToMessageId + )) + messages = transformMessages(messages, showNames: showNames, silently: silently) + shareSignals.append(standaloneSendEnqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + } + case let .quote(string, url): + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var banSendText = false + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil { + banSendText = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) { + banSendText = true + } + + if banSendText { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .fail(.generic) + } + + var replyToMessageId: MessageId? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + } + + var messages: [StandaloneSendEnqueueMessage] = [] + if !text.isEmpty { + messages.append(StandaloneSendEnqueueMessage( + content: .text(text: StandaloneSendEnqueueMessage.Text( + string: text, + entities: [] + )), + replyToMessageId: replyToMessageId + )) + } + let attributedText = NSMutableAttributedString(string: string, attributes: [ChatTextInputAttributes.italic: true as NSNumber]) + attributedText.append(NSAttributedString(string: "\n\n\(url)")) + let entities = generateChatInputTextEntities(attributedText) + + messages.append(StandaloneSendEnqueueMessage( + content: .text(text: StandaloneSendEnqueueMessage.Text( + string: attributedText.string, + entities: entities + )), + replyToMessageId: replyToMessageId + )) + messages = transformMessages(messages, showNames: showNames, silently: silently) + shareSignals.append(standaloneSendEnqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + } + case let .image(representations): + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var banSendPhotos = false + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendPhotos) != nil { + banSendPhotos = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendPhotos) { + banSendPhotos = true + } + + if banSendPhotos { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .fail(.generic) + } + + var replyToMessageId: MessageId? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + } + + var messages: [StandaloneSendEnqueueMessage] = [] + + if let representation = representations.last { + messages.append(StandaloneSendEnqueueMessage( + content: .image( + image: StandaloneSendEnqueueMessage.Image( + representation: representation.representation + ), + text: StandaloneSendEnqueueMessage.Text( + string: text, + entities: [] + ) + ), + replyToMessageId: replyToMessageId + )) + } + messages = transformMessages(messages, showNames: showNames, silently: silently) + shareSignals.append(standaloneSendEnqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + } + case let .media(mediaReference): + var sendTextAsCaption = false + if mediaReference.media is TelegramMediaImage || mediaReference.media is TelegramMediaFile { + sendTextAsCaption = true + } + + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var banSendType = false + if mediaReference.media is TelegramMediaImage { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendPhotos) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendPhotos) { + banSendType = true + } + } else if let file = mediaReference.media as? TelegramMediaFile { + if file.isSticker || file.isAnimated { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendStickers) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendStickers) { + banSendType = true + } + } else if file.isInstantVideo { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendInstantVideos) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendInstantVideos) { + banSendType = true + } + } else if file.isVoice { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendVoice) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendVoice) { + banSendType = true + } + } else if file.isMusic { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendMusic) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendMusic) { + banSendType = true + } + } else if file.isVideo { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendVideos) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendVideos) { + banSendType = true + } + } else { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendFiles) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendFiles) { + banSendType = true + } + } + } + + if banSendType { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .fail(.generic) + } + + var replyToMessageId: MessageId? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + } + + var messages: [StandaloneSendEnqueueMessage] = [] + if !text.isEmpty && !sendTextAsCaption { + messages.append(StandaloneSendEnqueueMessage( + content: .text(text: StandaloneSendEnqueueMessage.Text( + string: text, + entities: [] + )), + replyToMessageId: replyToMessageId + )) + } + + messages.append(StandaloneSendEnqueueMessage( + content: .arbitraryMedia( + media: mediaReference, + text: StandaloneSendEnqueueMessage.Text( + string: sendTextAsCaption ? text : "", + entities: [] + ) + ), + replyToMessageId: replyToMessageId + )) + messages = transformMessages(messages, showNames: showNames, silently: silently) + shareSignals.append(standaloneSendEnqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + } + case let .mapMedia(media): + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var banSendText = false + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil { + banSendText = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) { + banSendText = true + } + + if banSendText { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .fail(.generic) + } + + var replyToMessageId: MessageId? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + } + + var messages: [StandaloneSendEnqueueMessage] = [] + if !text.isEmpty { + messages.append(StandaloneSendEnqueueMessage( + content: .text(text: StandaloneSendEnqueueMessage.Text( + string: text, + entities: [] + )), + replyToMessageId: replyToMessageId + )) + } + + messages.append(StandaloneSendEnqueueMessage( + content: .map(map: media), + replyToMessageId: replyToMessageId + )) + messages = transformMessages(messages, showNames: showNames, silently: silently) + shareSignals.append(standaloneSendEnqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + } + case let .messages(messages): + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var replyToMessageId: MessageId? + var threadId: Int64? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + threadId = topicId + } + + var messagesToEnqueue: [StandaloneSendEnqueueMessage] = [] + if !text.isEmpty { + var banSendText = false + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil { + banSendText = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) { + banSendText = true + } + + if banSendText { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .fail(.generic) + } + + messagesToEnqueue.append(StandaloneSendEnqueueMessage( + content: .text(text: StandaloneSendEnqueueMessage.Text( + string: text, + entities: [] + )), + replyToMessageId: replyToMessageId + )) + } + for message in messages { + for media in message.media { + var banSendType = false + if media is TelegramMediaImage { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendPhotos) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendPhotos) { + banSendType = true + } + } else if let file = media as? TelegramMediaFile { + if file.isSticker || file.isAnimated { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendStickers) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendStickers) { + banSendType = true + } + } else if file.isInstantVideo { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendInstantVideos) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendInstantVideos) { + banSendType = true + } + } else if file.isVoice { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendVoice) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendVoice) { + banSendType = true + } + } else if file.isMusic { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendMusic) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendMusic) { + banSendType = true + } + } else if file.isVideo { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendVideos) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendVideos) { + banSendType = true + } + } else { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendFiles) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendFiles) { + banSendType = true + } + } + } + + if banSendType { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .fail(.generic) + } + } + + messagesToEnqueue.append(StandaloneSendEnqueueMessage( + content: .forward(forward: StandaloneSendEnqueueMessage.Forward( + sourceId: message.id, + threadId: threadId + )), + replyToMessageId: replyToMessageId + )) + } + messagesToEnqueue = transformMessages(messagesToEnqueue, showNames: showNames, silently: silently) + shareSignals.append(standaloneSendEnqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messagesToEnqueue)) + } + case let .fromExternal(f): + return f(peerIds, topicIds, text, strongSelf.currentAccount, silently) + |> map { state -> ShareState in + switch state { + case let .preparing(long): + return .preparing(long) + case let .progress(value): + return .progress(value) + case .done: + return .done + } + } + } + let account = strongSelf.currentAccount + let queue = Queue.mainQueue() + //var displayedError = false + return combineLatest(queue: queue, shareSignals) + |> `catch` { error -> Signal<[StandaloneSendMessageStatus], ShareControllerError> in + Queue.mainQueue().async { + let _ = (TelegramEngine(account: account).data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: error.peerId)) + |> deliverOnMainQueue).start(next: { peer in + guard let strongSelf = self, let peer = peer else { + return + } + if case .slowmodeActive = error.reason { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: strongSelf.presentationData.strings.Chat_SlowmodeSendError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + } else if case .mediaRestricted = error.reason { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + } + }) + } + + return .single([]) + } + |> mapToSignal { progressSets -> Signal in + if progressSets.isEmpty { + return .single(.done) + } + for item in progressSets { + if case .progress = item { + return .complete() + } + } + return .single(.done) + } + } + } + + private func shareLegacy(text: String, peerIds: [EnginePeer.Id], topicIds: [EnginePeer.Id: Int64], showNames: Bool, silently: Bool) -> Signal { + return self.currentContext.engine.data.get(EngineDataMap( + peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)) + )) + |> deliverOnMainQueue + |> castError(ShareControllerError.self) + |> mapToSignal { [weak self] peers -> Signal in + guard let strongSelf = self else { + return .complete() + } + + var shareSignals: [Signal<[MessageId?], NoError>] = [] + var subject = strongSelf.subject + if let segmentedValues = strongSelf.segmentedValues { + let selectedValue = segmentedValues[strongSelf.controllerNode.selectedSegmentedIndex] + subject = selectedValue.subject + } + + func transformMessages(_ messages: [EnqueueMessage], showNames: Bool, silently: Bool) -> [EnqueueMessage] { + return messages.map { message in + return message.withUpdatedAttributes({ attributes in + var attributes = attributes + if !showNames { + attributes.append(ForwardOptionsMessageAttribute(hideNames: true, hideCaptions: false)) + } + if silently { + attributes.append(NotificationInfoMessageAttribute(flags: .muted)) + } + return attributes + }) + } + } + + switch subject { + case let .url(url): + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var banSendText = false + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil { + banSendText = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) { + banSendText = true + } + + if banSendText { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .fail(.generic) + } + + var replyToMessageId: MessageId? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + } + + var messages: [EnqueueMessage] = [] + if !text.isEmpty { + messages.append(.message(text: url + "\n\n" + text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + } else { + messages.append(.message(text: url, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + } + messages = transformMessages(messages, showNames: showNames, silently: silently) + shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + } + case let .text(string): + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var banSendText = false + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil { + banSendText = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) { + banSendText = true + } + + if banSendText { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .fail(.generic) + } + + var replyToMessageId: MessageId? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + } + + var messages: [EnqueueMessage] = [] + if !text.isEmpty { + messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + } + messages.append(.message(text: string, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + messages = transformMessages(messages, showNames: showNames, silently: silently) + shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + } + case let .quote(string, url): + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var banSendText = false + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil { + banSendText = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) { + banSendText = true + } + + if banSendText { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .fail(.generic) + } + + var replyToMessageId: MessageId? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + } + + var messages: [EnqueueMessage] = [] + if !text.isEmpty { + messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + } + let attributedText = NSMutableAttributedString(string: string, attributes: [ChatTextInputAttributes.italic: true as NSNumber]) + attributedText.append(NSAttributedString(string: "\n\n\(url)")) + let entities = generateChatInputTextEntities(attributedText) + messages.append(.message(text: attributedText.string, attributes: [TextEntitiesMessageAttribute(entities: entities)], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + messages = transformMessages(messages, showNames: showNames, silently: silently) + shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + } + case let .image(representations): + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var banSendPhotos = false + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendPhotos) != nil { + banSendPhotos = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendPhotos) { + banSendPhotos = true + } + + if banSendPhotos { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .fail(.generic) + } + + var replyToMessageId: MessageId? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + } + + var messages: [EnqueueMessage] = [] + messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: Int64.random(in: Int64.min ... Int64.max)), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])), replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + messages = transformMessages(messages, showNames: showNames, silently: silently) + shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + } + case let .media(mediaReference): + var sendTextAsCaption = false + if mediaReference.media is TelegramMediaImage || mediaReference.media is TelegramMediaFile { + sendTextAsCaption = true + } + + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var banSendType = false + if mediaReference.media is TelegramMediaImage { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendPhotos) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendPhotos) { + banSendType = true + } + } else if let file = mediaReference.media as? TelegramMediaFile { + if file.isSticker || file.isAnimated { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendStickers) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendStickers) { + banSendType = true + } + } else if file.isInstantVideo { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendInstantVideos) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendInstantVideos) { + banSendType = true + } + } else if file.isVoice { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendVoice) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendVoice) { + banSendType = true + } + } else if file.isMusic { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendMusic) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendMusic) { + banSendType = true + } + } else if file.isVideo { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendVideos) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendVideos) { + banSendType = true + } + } else { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendFiles) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendFiles) { + banSendType = true + } + } + } + + if banSendType { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .fail(.generic) + } + + var replyToMessageId: MessageId? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + } + + var messages: [EnqueueMessage] = [] + if !text.isEmpty && !sendTextAsCaption { + messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + } + messages.append(.message(text: sendTextAsCaption ? text : "", attributes: [], inlineStickers: [:], mediaReference: mediaReference, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + messages = transformMessages(messages, showNames: showNames, silently: silently) + shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + } + case let .mapMedia(media): + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var banSendText = false + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil { + banSendText = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) { + banSendText = true + } + + if banSendText { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .fail(.generic) + } + + var replyToMessageId: MessageId? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + } + + var messages: [EnqueueMessage] = [] + if !text.isEmpty { + messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + } + messages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + messages = transformMessages(messages, showNames: showNames, silently: silently) + shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + } + case let .messages(messages): + for peerId in peerIds { + guard let maybePeer = peers[peerId], let peer = maybePeer else { + continue + } + + var replyToMessageId: MessageId? + var threadId: Int64? + if let topicId = topicIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)) + threadId = topicId + } + + var messagesToEnqueue: [EnqueueMessage] = [] + if !text.isEmpty { + var banSendText = false + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil { + banSendText = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) { + banSendText = true + } + + if banSendText { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .fail(.generic) + } + + messagesToEnqueue.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + } + for message in messages { + for media in message.media { + var banSendType = false + if media is TelegramMediaImage { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendPhotos) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendPhotos) { + banSendType = true + } + } else if let file = media as? TelegramMediaFile { + if file.isSticker || file.isAnimated { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendStickers) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendStickers) { + banSendType = true + } + } else if file.isInstantVideo { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendInstantVideos) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendInstantVideos) { + banSendType = true + } + } else if file.isVoice { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendVoice) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendVoice) { + banSendType = true + } + } else if file.isMusic { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendMusic) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendMusic) { + banSendType = true + } + } else if file.isVideo { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendVideos) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendVideos) { + banSendType = true + } + } else { + if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendFiles) != nil { + banSendType = true + } else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendFiles) { + banSendType = true + } + } + } + + if banSendType { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + return .fail(.generic) + } + } + + messagesToEnqueue.append(.forward(source: message.id, threadId: threadId, grouping: .auto, attributes: [], correlationId: nil)) + } + messagesToEnqueue = transformMessages(messagesToEnqueue, showNames: showNames, silently: silently) + shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messagesToEnqueue)) + } + case let .fromExternal(f): + return f(peerIds, topicIds, text, strongSelf.currentAccount, silently) + |> map { state -> ShareState in + switch state { + case let .preparing(long): + return .preparing(long) + case let .progress(value): + return .progress(value) + case .done: + return .done + } + } + } + let account = strongSelf.currentAccount + let queue = Queue.mainQueue() + var displayedError = false + return combineLatest(queue: queue, shareSignals) + |> castError(ShareControllerError.self) + |> mapToSignal { messageIdSets -> Signal in + var statuses: [Signal<(MessageId, PendingMessageStatus?, PendingMessageFailureReason?), ShareControllerError>] = [] + for messageIds in messageIdSets { + for case let id? in messageIds { + statuses.append(account.pendingMessageManager.pendingMessageStatus(id) + |> castError(ShareControllerError.self) + |> map { status, error -> (MessageId, PendingMessageStatus?, PendingMessageFailureReason?) in + return (id, status, error) + }) + } + } + return combineLatest(queue: queue, statuses) + |> mapToSignal { statuses -> Signal in + var hasStatuses = false + for (id, status, error) in statuses { + if let error = error { + Queue.mainQueue().async { + let _ = TelegramEngine(account: account).messages.deleteMessagesInteractively(messageIds: [id], type: .forEveryone).start() + let _ = (TelegramEngine(account: account).data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: id.peerId)) + |> deliverOnMainQueue).start(next: { peer in + guard let strongSelf = self, let peer = peer else { + return + } + if !displayedError { + if case .slowmodeActive = error { + displayedError = true + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: strongSelf.presentationData.strings.Chat_SlowmodeSendError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + } else if case .mediaRestricted = error { + displayedError = true + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), text: restrictedSendingContentsText(peer: peer, presentationData: strongSelf.presentationData), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + } + } + }) + } + } + if status != nil { + hasStatuses = true + } + } + if !hasStatuses { + return .single(.done) + } + return .complete() + } + |> take(1) + } + } + } + let didAppearPromise = ValuePromise(false, ignoreRepeated: true) override public func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) diff --git a/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift index 13b835ee98..ef7baa9c43 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift @@ -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 { + #if !DEBUG + error + #endif + + let signals: [Signal] = 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 + 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 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] = [] + 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 { + return postbox.transaction { transaction -> Signal 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() + 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, 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 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 { let content: Signal if let media = media { diff --git a/submodules/TelegramCore/Sources/State/PendingMessageManager.swift b/submodules/TelegramCore/Sources/State/PendingMessageManager.swift index 05fac086d2..52cab84ac5 100644 --- a/submodules/TelegramCore/Sources/State/PendingMessageManager.swift +++ b/submodules/TelegramCore/Sources/State/PendingMessageManager.swift @@ -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