mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
2369 lines
139 KiB
Swift
2369 lines
139 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import Display
|
|
import AsyncDisplayKit
|
|
import Postbox
|
|
import TelegramCore
|
|
import SwiftSignalKit
|
|
import TelegramPresentationData
|
|
import TelegramUIPreferences
|
|
import TextFormat
|
|
import AccountContext
|
|
import ActionSheetPeerItem
|
|
import LocalizedPeerData
|
|
import UrlEscaping
|
|
import StickerResources
|
|
import SaveToCameraRoll
|
|
import TelegramStringFormatting
|
|
import WallpaperBackgroundNode
|
|
|
|
public struct ShareControllerAction {
|
|
let title: String
|
|
let action: () -> Void
|
|
|
|
public init(title: String, action: @escaping () -> Void) {
|
|
self.title = title
|
|
self.action = action
|
|
}
|
|
}
|
|
|
|
public enum ShareControllerPreferredAction {
|
|
case `default`
|
|
case saveToCameraRoll
|
|
case custom(action: ShareControllerAction)
|
|
}
|
|
|
|
public enum ShareControllerExternalStatus {
|
|
case preparing(Bool)
|
|
case progress(Float)
|
|
case done
|
|
}
|
|
|
|
public enum ShareControllerError {
|
|
case generic
|
|
case fileTooBig(Int64)
|
|
}
|
|
|
|
public struct ShareControllerSegmentedValue {
|
|
let title: String
|
|
let subject: ShareControllerSubject
|
|
let actionTitle: String
|
|
let formatSendTitle: (Int) -> String
|
|
|
|
public init(title: String, subject: ShareControllerSubject, actionTitle: String, formatSendTitle: @escaping (Int) -> String) {
|
|
self.title = title
|
|
self.subject = subject
|
|
self.actionTitle = actionTitle
|
|
self.formatSendTitle = formatSendTitle
|
|
}
|
|
}
|
|
|
|
public enum ShareControllerSubject {
|
|
case url(String)
|
|
case text(String)
|
|
case quote(text: String, url: String)
|
|
case messages([Message])
|
|
case image([ImageRepresentationWithReference])
|
|
case media(AnyMediaReference)
|
|
case mapMedia(TelegramMediaMap)
|
|
case fromExternal(([PeerId], [PeerId: Int64], String, Account, Bool) -> Signal<ShareControllerExternalStatus, ShareControllerError>)
|
|
}
|
|
|
|
private enum ExternalShareItem {
|
|
case text(String)
|
|
case url(URL)
|
|
case image(UIImage)
|
|
case file(URL, String, String)
|
|
}
|
|
|
|
private enum ExternalShareItemStatus {
|
|
case progress
|
|
case done(ExternalShareItem)
|
|
}
|
|
|
|
private enum ExternalShareResourceStatus {
|
|
case progress
|
|
case done(MediaResourceData)
|
|
}
|
|
|
|
private func collectExternalShareResource(postbox: Postbox, resourceReference: MediaResourceReference, statsCategory: MediaResourceStatsCategory) -> Signal<ExternalShareResourceStatus, NoError> {
|
|
return Signal { subscriber in
|
|
let fetched = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: .other, userContentType: .other, reference: resourceReference, statsCategory: statsCategory).start()
|
|
let data = postbox.mediaBox.resourceData(resourceReference.resource, option: .complete(waitUntilFetchStatus: false)).start(next: { value in
|
|
if value.complete {
|
|
subscriber.putNext(.done(value))
|
|
} else {
|
|
subscriber.putNext(.progress)
|
|
}
|
|
})
|
|
|
|
return ActionDisposable {
|
|
fetched.dispose()
|
|
data.dispose()
|
|
}
|
|
}
|
|
}
|
|
|
|
private enum ExternalShareItemsState {
|
|
case progress
|
|
case done([ExternalShareItem])
|
|
}
|
|
|
|
private struct CollectableExternalShareItem {
|
|
let url: String?
|
|
let text: String
|
|
let author: PeerId?
|
|
let timestamp: Int32?
|
|
let mediaReference: AnyMediaReference?
|
|
}
|
|
|
|
private func collectExternalShareItems(strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, engine: TelegramEngine, postbox: Postbox, collectableItems: [CollectableExternalShareItem], takeOne: Bool = true) -> Signal<ExternalShareItemsState, NoError> {
|
|
var signals: [Signal<ExternalShareItemStatus, NoError>] = []
|
|
let authorsPeerIds = collectableItems.compactMap { $0.author }
|
|
let authorsPromise = Promise<[PeerId: String]>()
|
|
|
|
let peerTitles = engine.data.get(EngineDataMap(
|
|
authorsPeerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init)
|
|
))
|
|
|> map { peerMap -> [EnginePeer.Id: String] in
|
|
return peerMap.compactMapValues { peer -> String? in
|
|
return peer?.displayTitle(strings: strings, displayOrder: nameOrder)
|
|
}
|
|
}
|
|
|
|
authorsPromise.set(peerTitles)
|
|
for item in collectableItems {
|
|
if let mediaReference = item.mediaReference, let file = mediaReference.media as? TelegramMediaFile {
|
|
signals.append(collectExternalShareResource(postbox: postbox, resourceReference: mediaReference.resourceReference(file.resource), statsCategory: statsCategoryForFileWithAttributes(file.attributes))
|
|
|> mapToSignal { next -> Signal<ExternalShareItemStatus, NoError> in
|
|
switch next {
|
|
case .progress:
|
|
return .single(.progress)
|
|
case let .done(data):
|
|
if file.isSticker, !file.isAnimatedSticker, let dimensions = file.dimensions {
|
|
return chatMessageSticker(postbox: postbox, userLocation: .other, file: file, small: false, fetched: true, onlyFullSize: true)
|
|
|> map { f -> ExternalShareItemStatus in
|
|
let context = f(TransformImageArguments(corners: ImageCorners(), imageSize: dimensions.cgSize, boundingSize: dimensions.cgSize, intrinsicInsets: UIEdgeInsets(), emptyColor: nil, scale: 1.0))
|
|
if let image = context?.generateImage() {
|
|
return .done(.image(image))
|
|
} else {
|
|
return .progress
|
|
}
|
|
}
|
|
} else {
|
|
let fileName: String
|
|
if let value = file.fileName {
|
|
fileName = value
|
|
} else if file.isVideo {
|
|
fileName = "telegram_video.mp4"
|
|
} else if file.isVoice {
|
|
fileName = "telegram_audio.ogg"
|
|
} else {
|
|
fileName = "file"
|
|
}
|
|
let randomDirectory = UUID()
|
|
let safeFileName = fileName.replacingOccurrences(of: "/", with: "_")
|
|
let fileDirectory = NSTemporaryDirectory() + "\(randomDirectory)"
|
|
let _ = try? FileManager.default.createDirectory(at: URL(fileURLWithPath: fileDirectory), withIntermediateDirectories: true, attributes: nil)
|
|
let filePath = fileDirectory + "/\(safeFileName)"
|
|
if let _ = try? FileManager.default.copyItem(at: URL(fileURLWithPath: data.path), to: URL(fileURLWithPath: filePath)) {
|
|
return .single(.done(.file(URL(fileURLWithPath: filePath), fileName, file.mimeType)))
|
|
} else {
|
|
return .single(.progress)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
} else if let mediaReference = item.mediaReference, let image = mediaReference.media as? TelegramMediaImage, let largest = largestImageRepresentation(image.representations) {
|
|
signals.append(collectExternalShareResource(postbox: postbox, resourceReference: mediaReference.resourceReference(largest.resource), statsCategory: .image)
|
|
|> map { next -> ExternalShareItemStatus in
|
|
switch next {
|
|
case .progress:
|
|
return .progress
|
|
case let .done(data):
|
|
guard let fileData = try? Data(contentsOf: URL(fileURLWithPath: data.path)) else {
|
|
return .progress
|
|
}
|
|
if let image = UIImage(data: fileData) {
|
|
return .done(.image(image))
|
|
} else {
|
|
#if DEBUG
|
|
if "".isEmpty {
|
|
return .done(.file(URL(fileURLWithPath: data.path), "image.bin", "application/octet-stream"))
|
|
}
|
|
#endif
|
|
return .progress
|
|
}
|
|
}
|
|
})
|
|
} else if let mediaReference = item.mediaReference, let poll = mediaReference.media as? TelegramMediaPoll {
|
|
var text = "📊 \(poll.text)"
|
|
text.append("\n\(strings.MessagePoll_LabelAnonymous)")
|
|
for option in poll.options {
|
|
text.append("\n— \(option.text)")
|
|
}
|
|
let totalVoters = poll.results.totalVoters ?? 0
|
|
switch poll.kind {
|
|
case .poll:
|
|
if totalVoters == 0 {
|
|
text.append("\n\(strings.MessagePoll_NoVotes)")
|
|
} else {
|
|
text.append("\n\(strings.MessagePoll_VotedCount(totalVoters))")
|
|
}
|
|
case .quiz:
|
|
if totalVoters == 0 {
|
|
text.append("\n\(strings.MessagePoll_QuizNoUsers)")
|
|
} else {
|
|
text.append("\n\(strings.MessagePoll_QuizCount(totalVoters))")
|
|
}
|
|
}
|
|
signals.append(.single(.done(.text(text))))
|
|
} else if let mediaReference = item.mediaReference, let contact = mediaReference.media as? TelegramMediaContact {
|
|
let contactData: DeviceContactExtendedData
|
|
if let vCard = contact.vCardData, let vCardData = vCard.data(using: .utf8), let parsed = DeviceContactExtendedData(vcard: vCardData) {
|
|
contactData = parsed
|
|
} else {
|
|
contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: contact.firstName, lastName: contact.lastName, phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!<Mobile>!$_", value: contact.phoneNumber)]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
|
|
}
|
|
|
|
if let vCard = contactData.serializedVCard() {
|
|
let fullName = [contact.firstName, contact.lastName].filter { !$0.isEmpty }.joined(separator: " ")
|
|
let fileName = "\(fullName).vcf"
|
|
let randomDirectory = UUID()
|
|
let safeFileName = fileName.replacingOccurrences(of: "/", with: "_")
|
|
let fileDirectory = NSTemporaryDirectory() + "\(randomDirectory)"
|
|
let _ = try? FileManager.default.createDirectory(at: URL(fileURLWithPath: fileDirectory), withIntermediateDirectories: true, attributes: nil)
|
|
let filePath = fileDirectory + "/\(safeFileName)"
|
|
let vCardData = vCard.data(using: .utf8)
|
|
if let _ = try? vCardData?.write(to: URL(fileURLWithPath: filePath)) {
|
|
signals.append(.single(.done(.file(URL(fileURLWithPath: filePath), fileName, "text/x-vcard"))))
|
|
}
|
|
}
|
|
}
|
|
if let url = item.url, let parsedUrl = URL(string: url) {
|
|
if signals.isEmpty || !takeOne {
|
|
signals.append(.single(.done(.url(parsedUrl))))
|
|
}
|
|
}
|
|
if !item.text.isEmpty {
|
|
if signals.isEmpty || !takeOne {
|
|
let author: Signal<String?, NoError>
|
|
if let peerId = item.author {
|
|
author = authorsPromise.get()
|
|
|> take(1)
|
|
|> map { authors in
|
|
return authors[peerId]
|
|
}
|
|
} else {
|
|
author = .single(nil)
|
|
}
|
|
signals.append(author
|
|
|> map { author in
|
|
var text: String = item.text
|
|
var metadata: [String] = []
|
|
if let author = author {
|
|
metadata.append(author)
|
|
}
|
|
if let timestamp = item.timestamp {
|
|
metadata.append("[\(stringForFullDate(timestamp: timestamp, strings: strings, dateTimeFormat: dateTimeFormat))]")
|
|
}
|
|
if !metadata.isEmpty {
|
|
text = metadata.joined(separator: ", ") + "\n" + text + "\n"
|
|
}
|
|
return .done(.text(text))
|
|
})
|
|
}
|
|
}
|
|
}
|
|
return combineLatest(signals)
|
|
|> map { statuses -> ExternalShareItemsState in
|
|
var items: [ExternalShareItem] = []
|
|
for status in statuses {
|
|
switch status {
|
|
case .progress:
|
|
return .progress
|
|
case let .done(item):
|
|
items.append(item)
|
|
}
|
|
}
|
|
return .done(items)
|
|
}
|
|
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
|
if case .progress = lhs, case .progress = rhs {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
})
|
|
}
|
|
|
|
public final class ShareController: ViewController {
|
|
private var controllerNode: ShareControllerNode {
|
|
return self.displayNode as! ShareControllerNode
|
|
}
|
|
|
|
private let _ready = Promise<Bool>()
|
|
override public var ready: Promise<Bool> {
|
|
return self._ready
|
|
}
|
|
|
|
private var animatedIn = false
|
|
|
|
private let sharedContext: SharedAccountContext
|
|
private let currentContext: AccountContext
|
|
private var currentAccount: Account
|
|
private var presentationData: PresentationData
|
|
private var presentationDataDisposable: Disposable?
|
|
private let forceTheme: PresentationTheme?
|
|
private let shareAsLink: Bool
|
|
|
|
private let externalShare: Bool
|
|
private let immediateExternalShare: Bool
|
|
private let subject: ShareControllerSubject
|
|
private let presetText: String?
|
|
private let switchableAccounts: [AccountWithInfo]
|
|
private let immediatePeerId: PeerId?
|
|
private let segmentedValues: [ShareControllerSegmentedValue]?
|
|
private let fromForeignApp: Bool
|
|
|
|
private let peers = Promise<([(EngineRenderedPeer, EnginePeer.Presence?)], EnginePeer)>()
|
|
private let peersDisposable = MetaDisposable()
|
|
private let readyDisposable = MetaDisposable()
|
|
private let accountActiveDisposable = MetaDisposable()
|
|
|
|
private var defaultAction: ShareControllerAction?
|
|
public private(set) var actionIsMediaSaving = false
|
|
|
|
public var actionCompleted: (() -> Void)?
|
|
public var dismissed: ((Bool) -> Void)?
|
|
public var completed: (([PeerId]) -> Void)? {
|
|
didSet {
|
|
if self.isNodeLoaded {
|
|
self.controllerNode.completed = completed
|
|
}
|
|
}
|
|
}
|
|
|
|
public var openShareAsImage: (([Message]) -> Void)?
|
|
|
|
public var debugAction: (() -> Void)?
|
|
|
|
public convenience init(context: AccountContext, subject: ShareControllerSubject, presetText: String? = nil, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, fromForeignApp: Bool = false, segmentedValues: [ShareControllerSegmentedValue]? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false, switchableAccounts: [AccountWithInfo] = [], immediatePeerId: PeerId? = nil, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, forceTheme: PresentationTheme? = nil, forcedActionTitle: String? = nil, shareAsLink: Bool = false) {
|
|
self.init(sharedContext: context.sharedContext, currentContext: context, subject: subject, presetText: presetText, preferredAction: preferredAction, showInChat: showInChat, fromForeignApp: fromForeignApp, segmentedValues: segmentedValues, externalShare: externalShare, immediateExternalShare: immediateExternalShare, switchableAccounts: switchableAccounts, immediatePeerId: immediatePeerId, updatedPresentationData: updatedPresentationData, forceTheme: forceTheme, forcedActionTitle: forcedActionTitle, shareAsLink: shareAsLink)
|
|
}
|
|
|
|
public init(sharedContext: SharedAccountContext, currentContext: AccountContext, subject: ShareControllerSubject, presetText: String? = nil, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, fromForeignApp: Bool = false, segmentedValues: [ShareControllerSegmentedValue]? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false, switchableAccounts: [AccountWithInfo] = [], immediatePeerId: PeerId? = nil, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, forceTheme: PresentationTheme? = nil, forcedActionTitle: String? = nil, shareAsLink: Bool = false) {
|
|
self.sharedContext = sharedContext
|
|
self.currentContext = currentContext
|
|
self.currentAccount = currentContext.account
|
|
self.subject = subject
|
|
self.presetText = presetText
|
|
self.externalShare = externalShare
|
|
self.immediateExternalShare = immediateExternalShare
|
|
self.switchableAccounts = switchableAccounts
|
|
self.immediatePeerId = immediatePeerId
|
|
self.fromForeignApp = fromForeignApp
|
|
self.segmentedValues = segmentedValues
|
|
self.forceTheme = forceTheme
|
|
self.shareAsLink = shareAsLink
|
|
|
|
self.presentationData = updatedPresentationData?.initial ?? sharedContext.currentPresentationData.with { $0 }
|
|
if let forceTheme = self.forceTheme {
|
|
self.presentationData = self.presentationData.withUpdated(theme: forceTheme)
|
|
}
|
|
|
|
super.init(navigationBarPresentationData: nil)
|
|
|
|
self.statusBar.statusBarStyle = .Ignore
|
|
|
|
switch subject {
|
|
case let .url(text):
|
|
self.defaultAction = ShareControllerAction(title: forcedActionTitle ?? self.presentationData.strings.ShareMenu_CopyShareLink, action: { [weak self] in
|
|
if let strongSelf = self, let segmentedValues = segmentedValues {
|
|
let selectedValue = segmentedValues[strongSelf.controllerNode.selectedSegmentedIndex]
|
|
if case let .url(text) = selectedValue.subject {
|
|
UIPasteboard.general.string = text
|
|
}
|
|
} else {
|
|
UIPasteboard.general.string = text
|
|
}
|
|
self?.controllerNode.cancel?()
|
|
|
|
self?.actionCompleted?()
|
|
})
|
|
case .text:
|
|
break
|
|
case let .mapMedia(media):
|
|
self.defaultAction = ShareControllerAction(title: self.presentationData.strings.ShareMenu_CopyShareLink, action: { [weak self] in
|
|
let latLong = "\(media.latitude),\(media.longitude)"
|
|
let url = "https://maps.apple.com/maps?ll=\(latLong)&q=\(latLong)&t=m"
|
|
UIPasteboard.general.string = url
|
|
self?.controllerNode.cancel?()
|
|
|
|
self?.actionCompleted?()
|
|
})
|
|
break
|
|
case .quote:
|
|
break
|
|
case let .image(representations):
|
|
if case .saveToCameraRoll = preferredAction {
|
|
self.actionIsMediaSaving = true
|
|
self.defaultAction = ShareControllerAction(title: self.presentationData.strings.Gallery_SaveImage, action: { [weak self] in
|
|
self?.saveToCameraRoll(representations: representations)
|
|
self?.actionCompleted?()
|
|
})
|
|
}
|
|
case let .media(mediaReference):
|
|
var canSave = false
|
|
var isVideo = false
|
|
if mediaReference.media is TelegramMediaImage {
|
|
canSave = true
|
|
} else if let file = mediaReference.media as? TelegramMediaFile {
|
|
canSave = true
|
|
isVideo = file.isVideo
|
|
}
|
|
if case .saveToCameraRoll = preferredAction, canSave {
|
|
self.actionIsMediaSaving = true
|
|
self.defaultAction = ShareControllerAction(title: isVideo ? self.presentationData.strings.Gallery_SaveVideo : self.presentationData.strings.Gallery_SaveImage, action: { [weak self] in
|
|
if let strongSelf = self {
|
|
if case let .message(message, media) = mediaReference, let messageId = message.id, let file = media as? TelegramMediaFile {
|
|
let _ = (messageMediaFileStatus(context: currentContext, messageId: messageId, file: file)
|
|
|> take(1)
|
|
|> deliverOnMainQueue).start(next: { [weak self] fetchStatus in
|
|
if let strongSelf = self {
|
|
if case .Local = fetchStatus {
|
|
strongSelf.saveToCameraRoll(mediaReference: mediaReference, completion: nil)
|
|
strongSelf.actionCompleted?()
|
|
} else {
|
|
strongSelf.saveToCameraRoll(mediaReference: mediaReference, completion: {
|
|
})
|
|
}
|
|
}
|
|
})
|
|
} else {
|
|
strongSelf.saveToCameraRoll(mediaReference: mediaReference, completion: nil)
|
|
strongSelf.actionCompleted?()
|
|
}
|
|
}
|
|
})
|
|
}
|
|
case let .messages(messages):
|
|
if case .saveToCameraRoll = preferredAction {
|
|
self.actionIsMediaSaving = true
|
|
self.defaultAction = ShareControllerAction(title: self.presentationData.strings.Preview_SaveToCameraRoll, action: { [weak self] in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
|
|
let actionCompleted = strongSelf.actionCompleted
|
|
strongSelf.saveToCameraRoll(messages: messages, completion: {
|
|
actionCompleted?()
|
|
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
strongSelf.controllerNode.animateOut(shared: false, completion: {
|
|
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
|
})
|
|
})
|
|
})
|
|
} else if let message = messages.first {
|
|
let groupingKey: Int64? = message.groupingKey
|
|
var sameGroupingKey = groupingKey != nil
|
|
if sameGroupingKey {
|
|
for message in messages {
|
|
if message.groupingKey != groupingKey {
|
|
sameGroupingKey = false
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if let showInChat = showInChat, messages.count == 1 {
|
|
self.defaultAction = ShareControllerAction(title: self.presentationData.strings.SharedMedia_ViewInChat, action: { [weak self] in
|
|
self?.controllerNode.cancel?()
|
|
showInChat(message)
|
|
self?.actionCompleted?()
|
|
})
|
|
} else if let chatPeer = message.peers[message.id.peerId] as? TelegramChannel, messages.count == 1 || sameGroupingKey {
|
|
if message.id.namespace == Namespaces.Message.Cloud {
|
|
self.defaultAction = ShareControllerAction(title: self.presentationData.strings.ShareMenu_CopyShareLink, action: { [weak self] in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
let _ = (TelegramEngine(account: strongSelf.currentAccount).messages.exportMessageLink(peerId: chatPeer.id, messageId: message.id)
|
|
|> map { result -> String? in
|
|
return result
|
|
}
|
|
|> deliverOnMainQueue).start(next: { link in
|
|
if let link = link {
|
|
UIPasteboard.general.string = link
|
|
}
|
|
})
|
|
strongSelf.controllerNode.cancel?()
|
|
strongSelf.actionCompleted?()
|
|
})
|
|
}
|
|
}
|
|
}
|
|
case .fromExternal:
|
|
break
|
|
}
|
|
|
|
if case let .custom(action) = preferredAction {
|
|
self.defaultAction = ShareControllerAction(title: action.title, action: { [weak self] in
|
|
self?.controllerNode.cancel?()
|
|
action.action()
|
|
self?.actionCompleted?()
|
|
})
|
|
}
|
|
|
|
self.presentationDataDisposable = ((updatedPresentationData?.signal ?? self.sharedContext.presentationData)
|
|
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
|
if let strongSelf = self, strongSelf.isNodeLoaded {
|
|
strongSelf.controllerNode.updatePresentationData(presentationData)
|
|
}
|
|
})
|
|
|
|
self.switchToAccount(account: currentAccount, animateIn: false)
|
|
|
|
if self.fromForeignApp {
|
|
if let application = UIApplication.value(forKeyPath: #keyPath(UIApplication.shared)) as? UIApplication {
|
|
application.isIdleTimerDisabled = true
|
|
}
|
|
}
|
|
}
|
|
|
|
required public init(coder aDecoder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
deinit {
|
|
self.peersDisposable.dispose()
|
|
self.readyDisposable.dispose()
|
|
self.accountActiveDisposable.dispose()
|
|
|
|
if self.fromForeignApp {
|
|
if let application = UIApplication.value(forKeyPath: #keyPath(UIApplication.shared)) as? UIApplication {
|
|
application.isIdleTimerDisabled = false
|
|
}
|
|
}
|
|
}
|
|
|
|
override public func loadDisplayNode() {
|
|
var fromPublicChannel = false
|
|
if case let .messages(messages) = self.subject, let message = messages.first, let peer = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = peer.info {
|
|
fromPublicChannel = true
|
|
}
|
|
|
|
self.displayNode = ShareControllerNode(sharedContext: self.sharedContext, presentationData: self.presentationData, presetText: self.presetText, defaultAction: self.defaultAction, requestLayout: { [weak self] transition in
|
|
self?.requestLayout(transition: transition)
|
|
}, presentError: { [weak self] title, text in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: title, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
|
}, externalShare: self.externalShare, immediateExternalShare: self.immediateExternalShare, immediatePeerId: self.immediatePeerId, fromForeignApp: self.fromForeignApp, forceTheme: self.forceTheme, fromPublicChannel: fromPublicChannel, segmentedValues: self.segmentedValues)
|
|
self.controllerNode.completed = self.completed
|
|
self.controllerNode.present = { [weak self] c in
|
|
self?.presentInGlobalOverlay(c)
|
|
}
|
|
self.controllerNode.dismiss = { [weak self] shared in
|
|
self?.dismissed?(shared)
|
|
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
|
}
|
|
self.controllerNode.cancel = { [weak self] in
|
|
self?.controllerNode.view.endEditing(true)
|
|
self?.controllerNode.animateOut(shared: false, completion: {
|
|
self?.dismissed?(false)
|
|
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
|
})
|
|
}
|
|
|
|
self.controllerNode.tryShare = { [weak self] text, peers in
|
|
guard let strongSelf = self else {
|
|
return false
|
|
}
|
|
|
|
var subject = strongSelf.subject
|
|
if let segmentedValues = strongSelf.segmentedValues {
|
|
let selectedValue = segmentedValues[strongSelf.controllerNode.selectedSegmentedIndex]
|
|
subject = selectedValue.subject
|
|
}
|
|
|
|
switch subject {
|
|
case .url:
|
|
for peer in peers {
|
|
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 false
|
|
}
|
|
}
|
|
case .text:
|
|
for peer in peers {
|
|
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 false
|
|
}
|
|
}
|
|
case .quote:
|
|
for peer in peers {
|
|
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 false
|
|
}
|
|
}
|
|
case .image:
|
|
for peer in peers {
|
|
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 false
|
|
}
|
|
}
|
|
case let .media(mediaReference):
|
|
var sendTextAsCaption = false
|
|
if mediaReference.media is TelegramMediaImage || mediaReference.media is TelegramMediaFile {
|
|
sendTextAsCaption = true
|
|
}
|
|
|
|
for peer in peers {
|
|
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 false
|
|
}
|
|
|
|
if !text.isEmpty && !sendTextAsCaption {
|
|
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 false
|
|
}
|
|
}
|
|
}
|
|
case .mapMedia:
|
|
for peer in peers {
|
|
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 false
|
|
}
|
|
}
|
|
case let .messages(messages):
|
|
for peer in peers {
|
|
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 false
|
|
}
|
|
}
|
|
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
|
|
}
|
|
}
|
|
} else if media is TelegramMediaContact || media is TelegramMediaMap {
|
|
if case let .channel(channel) = peer, channel.hasBannedPermission(.banSendText) != nil {
|
|
banSendType = true
|
|
} else if case let .legacyGroup(group) = peer, group.hasBannedPermission(.banSendText) {
|
|
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 false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case .fromExternal:
|
|
break
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
self.controllerNode.share = { [weak self] text, peerIds, topicIds, showNames, silently in
|
|
guard let self else {
|
|
return .complete()
|
|
}
|
|
|
|
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 {
|
|
var collectableItems: [CollectableExternalShareItem] = []
|
|
var subject = strongSelf.subject
|
|
if let segmentedValues = strongSelf.segmentedValues {
|
|
let selectedValue = segmentedValues[strongSelf.controllerNode.selectedSegmentedIndex]
|
|
subject = selectedValue.subject
|
|
}
|
|
var messageUrl: String?
|
|
// var messagesToShare: [Message]?
|
|
switch subject {
|
|
case let .url(text):
|
|
collectableItems.append(CollectableExternalShareItem(url: explicitUrl(text), text: "", author: nil, timestamp: nil, mediaReference: nil))
|
|
case let .text(string):
|
|
collectableItems.append(CollectableExternalShareItem(url: "", text: string, author: nil, timestamp: nil, mediaReference: nil))
|
|
case let .quote(text, url):
|
|
collectableItems.append(CollectableExternalShareItem(url: "", text: "\"\(text)\"\n\n\(url)", author: nil, timestamp: nil, mediaReference: nil))
|
|
case let .image(representations):
|
|
let 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: [])
|
|
collectableItems.append(CollectableExternalShareItem(url: "", text: "", author: nil, timestamp: nil, mediaReference: .standalone(media: media)))
|
|
case let .media(mediaReference):
|
|
collectableItems.append(CollectableExternalShareItem(url: "", text: "", author: nil, timestamp: nil, mediaReference: mediaReference))
|
|
case let .mapMedia(media):
|
|
let latLong = "\(media.latitude),\(media.longitude)"
|
|
collectableItems.append(CollectableExternalShareItem(url: "https://maps.apple.com/maps?ll=\(latLong)&q=\(latLong)&t=m", text: "", author: nil, timestamp: nil, mediaReference: nil))
|
|
case let .messages(messages):
|
|
// messagesToShare = messages
|
|
for message in messages {
|
|
var url: String?
|
|
var selectedMedia: Media?
|
|
loop: for media in message.media {
|
|
switch media {
|
|
case _ as TelegramMediaImage, _ as TelegramMediaFile:
|
|
selectedMedia = media
|
|
break loop
|
|
case let webpage as TelegramMediaWebpage:
|
|
if case let .Loaded(content) = webpage.content, ["photo", "document", "video", "gif"].contains(content.type) {
|
|
if let file = content.file {
|
|
selectedMedia = file
|
|
} else if let image = content.image {
|
|
selectedMedia = image
|
|
}
|
|
}
|
|
case _ as TelegramMediaPoll:
|
|
selectedMedia = media
|
|
break loop
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
if let chatPeer = message.peers[message.id.peerId] as? TelegramChannel {
|
|
if message.id.namespace == Namespaces.Message.Cloud, let addressName = chatPeer.addressName, !addressName.isEmpty {
|
|
url = "https://t.me/\(addressName)/\(message.id.id)"
|
|
if messageUrl == nil {
|
|
messageUrl = url
|
|
}
|
|
}
|
|
}
|
|
let accountPeerId = strongSelf.currentAccount.peerId
|
|
let authorPeerId: PeerId?
|
|
if let author = message.effectiveAuthor {
|
|
authorPeerId = author.id
|
|
} else if message.effectivelyIncoming(accountPeerId) {
|
|
authorPeerId = message.id.peerId
|
|
} else {
|
|
authorPeerId = accountPeerId
|
|
}
|
|
|
|
var restrictedText: String?
|
|
for attribute in message.attributes {
|
|
if let attribute = attribute as? RestrictedContentMessageAttribute {
|
|
restrictedText = attribute.platformText(platform: "ios", contentSettings: strongSelf.currentContext.currentContentSettings.with { $0 }) ?? ""
|
|
}
|
|
}
|
|
|
|
if let restrictedText = restrictedText {
|
|
collectableItems.append(CollectableExternalShareItem(url: url, text: restrictedText, author: authorPeerId, timestamp: message.timestamp, mediaReference: nil))
|
|
} else {
|
|
collectableItems.append(CollectableExternalShareItem(url: url, text: message.text, author: authorPeerId, timestamp: message.timestamp, mediaReference: selectedMedia.flatMap({ AnyMediaReference.message(message: MessageReference(message), media: $0) })))
|
|
}
|
|
}
|
|
case .fromExternal:
|
|
break
|
|
}
|
|
return (collectExternalShareItems(strings: strongSelf.presentationData.strings, dateTimeFormat: strongSelf.presentationData.dateTimeFormat, nameOrder: strongSelf.presentationData.nameDisplayOrder, engine: TelegramEngine(account: strongSelf.currentAccount), postbox: strongSelf.currentAccount.postbox, collectableItems: collectableItems, takeOne: !strongSelf.immediateExternalShare)
|
|
|> deliverOnMainQueue)
|
|
|> map { state in
|
|
switch state {
|
|
case .progress:
|
|
return .preparing
|
|
case let .done(items):
|
|
if let strongSelf = self, !items.isEmpty {
|
|
strongSelf._ready.set(.single(true))
|
|
var activityItems: [Any] = []
|
|
if strongSelf.shareAsLink, let messageUrl = messageUrl, let url = NSURL(string: messageUrl) {
|
|
activityItems.append(url)
|
|
} else {
|
|
for item in items {
|
|
switch item {
|
|
case let .url(url):
|
|
activityItems.append(url as NSURL)
|
|
case let .text(text):
|
|
activityItems.append(text as NSString)
|
|
case let .image(image):
|
|
activityItems.append(image)
|
|
case let .file(url, _, _):
|
|
activityItems.append(url)
|
|
}
|
|
}
|
|
}
|
|
|
|
let activities: [UIActivity]? = nil
|
|
let _ = (strongSelf.didAppearPromise.get()
|
|
|> filter { $0 }
|
|
|> take(1)
|
|
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
|
// if asImage, let messages = messagesToShare {
|
|
// self?.openShareAsImage?(messages)
|
|
// } else {
|
|
let activityController = UIActivityViewController(activityItems: activityItems, applicationActivities: activities)
|
|
if let strongSelf = self, let window = strongSelf.view.window, let rootViewController = window.rootViewController {
|
|
activityController.popoverPresentationController?.sourceView = window
|
|
activityController.popoverPresentationController?.sourceRect = CGRect(origin: CGPoint(x: window.bounds.width / 2.0, y: window.bounds.size.height - 1.0), size: CGSize(width: 1.0, height: 1.0))
|
|
rootViewController.present(activityController, animated: true, completion: nil)
|
|
}
|
|
// }
|
|
})
|
|
}
|
|
return .done
|
|
}
|
|
}
|
|
} else {
|
|
return .single(.done)
|
|
}
|
|
}
|
|
self.controllerNode.switchToAnotherAccount = { [weak self] in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
strongSelf.controllerNode.animateOut(shared: false, completion: {})
|
|
|
|
let presentationData = strongSelf.sharedContext.currentPresentationData.with { $0 }
|
|
let controller = ActionSheetController(presentationData: presentationData)
|
|
controller.dismissed = { [weak self] cancelled in
|
|
if cancelled {
|
|
self?.controllerNode.animateIn()
|
|
}
|
|
}
|
|
let dismissAction: () -> Void = { [weak controller] in
|
|
controller?.dismissAnimated()
|
|
}
|
|
var items: [ActionSheetItem] = []
|
|
for info in strongSelf.switchableAccounts {
|
|
items.append(ActionSheetPeerItem(context: strongSelf.sharedContext.makeTempAccountContext(account: info.account), peer: EnginePeer(info.peer), title: EnginePeer(info.peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), isSelected: info.account.id == strongSelf.currentAccount.id, strings: presentationData.strings, theme: presentationData.theme, action: { [weak self] in
|
|
dismissAction()
|
|
self?.switchToAccount(account: info.account, animateIn: true)
|
|
}))
|
|
}
|
|
controller.setItemGroups([
|
|
ActionSheetItemGroup(items: items)
|
|
])
|
|
strongSelf.view.endEditing(true)
|
|
strongSelf.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
}
|
|
self.controllerNode.debugAction = { [weak self] in
|
|
self?.debugAction?()
|
|
}
|
|
self.displayNodeDidLoad()
|
|
|
|
self.peersDisposable.set((self.peers.get()
|
|
|> deliverOnMainQueue).start(next: { [weak self] next in
|
|
if let strongSelf = self {
|
|
strongSelf.controllerNode.updatePeers(context: strongSelf.sharedContext.makeTempAccountContext(account: strongSelf.currentAccount), switchableAccounts: strongSelf.switchableAccounts, peers: next.0, accountPeer: next.1, defaultAction: strongSelf.defaultAction)
|
|
}
|
|
}))
|
|
self._ready.set(self.controllerNode.ready.get())
|
|
}
|
|
|
|
override public func loadView() {
|
|
super.loadView()
|
|
}
|
|
|
|
private func shareModern(text: String, peerIds: [EnginePeer.Id], topicIds: [EnginePeer.Id: Int64], showNames: Bool, silently: Bool) -> Signal<ShareState, ShareControllerError> {
|
|
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<ShareState, ShareControllerError> in
|
|
guard let strongSelf = self else {
|
|
return .complete()
|
|
}
|
|
|
|
var shareSignals: [Signal<StandaloneSendMessageStatus, StandaloneSendMessagesError>] = []
|
|
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<ShareState, ShareControllerError> 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<ShareState, ShareControllerError> {
|
|
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<ShareState, ShareControllerError> 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<ShareState, ShareControllerError> 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<ShareState, ShareControllerError> 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<Bool>(false, ignoreRepeated: true)
|
|
override public func viewDidAppear(_ animated: Bool) {
|
|
super.viewDidAppear(animated)
|
|
|
|
if !self.animatedIn {
|
|
self.animatedIn = true
|
|
self.didAppearPromise.set(true)
|
|
if !self.immediateExternalShare {
|
|
self.controllerNode.animateIn()
|
|
}
|
|
}
|
|
}
|
|
|
|
override public func dismiss(completion: (() -> Void)? = nil) {
|
|
self.controllerNode.view.endEditing(true)
|
|
self.controllerNode.animateOut(shared: false, completion: { [weak self] in
|
|
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
|
completion?()
|
|
})
|
|
}
|
|
|
|
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
|
super.containerLayoutUpdated(layout, transition: transition)
|
|
|
|
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
|
}
|
|
|
|
private func saveToCameraRoll(messages: [Message], completion: @escaping () -> Void) {
|
|
let postbox = self.currentAccount.postbox
|
|
let signals: [Signal<Float, NoError>] = messages.compactMap { message -> Signal<Float, NoError>? in
|
|
if let media = message.media.first {
|
|
let context: AccountContext
|
|
if self.currentContext.account.id == self.currentAccount.id {
|
|
context = self.currentContext
|
|
} else {
|
|
context = self.sharedContext.makeTempAccountContext(account: self.currentAccount)
|
|
}
|
|
return SaveToCameraRoll.saveToCameraRoll(context: context, postbox: postbox, userLocation: .peer(message.id.peerId), mediaReference: .message(message: MessageReference(message), media: media))
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
if !signals.isEmpty {
|
|
let total = combineLatest(signals)
|
|
|> map { values -> Float? in
|
|
var total: Float = 0.0
|
|
for value in values {
|
|
total += value
|
|
}
|
|
total /= Float(values.count)
|
|
return total
|
|
}
|
|
self.controllerNode.transitionToProgressWithValue(signal: total, completion: completion)
|
|
}
|
|
}
|
|
|
|
private func saveToCameraRoll(representations: [ImageRepresentationWithReference]) {
|
|
let media = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
|
|
let context: AccountContext
|
|
if self.currentContext.account.id == self.currentAccount.id {
|
|
context = self.currentContext
|
|
} else {
|
|
context = self.sharedContext.makeTempAccountContext(account: self.currentAccount)
|
|
}
|
|
self.controllerNode.transitionToProgressWithValue(signal: SaveToCameraRoll.saveToCameraRoll(context: context, postbox: context.account.postbox, userLocation: .other, mediaReference: .standalone(media: media)) |> map(Optional.init), dismissImmediately: true, completion: {})
|
|
}
|
|
|
|
private func saveToCameraRoll(mediaReference: AnyMediaReference, completion: (() -> Void)?) {
|
|
let context: AccountContext
|
|
if self.currentContext.account.id == self.currentAccount.id {
|
|
context = self.currentContext
|
|
} else {
|
|
context = self.sharedContext.makeTempAccountContext(account: self.currentAccount)
|
|
}
|
|
self.controllerNode.transitionToProgressWithValue(signal: SaveToCameraRoll.saveToCameraRoll(context: context, postbox: context.account.postbox, userLocation: .other, mediaReference: mediaReference) |> map(Optional.init), dismissImmediately: completion == nil, completion: completion ?? {})
|
|
}
|
|
|
|
private func switchToAccount(account: Account, animateIn: Bool) {
|
|
self.currentAccount = account
|
|
self.accountActiveDisposable.set(self.sharedContext.setAccountUserInterfaceInUse(account.id))
|
|
|
|
self.peers.set(combineLatest(
|
|
TelegramEngine(account: self.currentAccount).data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.currentAccount.peerId)),
|
|
self.currentAccount.viewTracker.tailChatListView(groupId: .root, count: 150)
|
|
|> take(1)
|
|
)
|
|
|> mapToSignal { maybeAccountPeer, view -> Signal<([(EngineRenderedPeer, EnginePeer.Presence?)], EnginePeer), NoError> in
|
|
let accountPeer = maybeAccountPeer!
|
|
|
|
var peers: [EngineRenderedPeer] = []
|
|
for entry in view.0.entries.reversed() {
|
|
switch entry {
|
|
case let .MessageEntry(entryData):
|
|
if let peer = entryData.renderedPeer.peers[entryData.renderedPeer.peerId], peer.id != accountPeer.id, canSendMessagesToPeer(peer) {
|
|
peers.append(EngineRenderedPeer(entryData.renderedPeer))
|
|
}
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
|
|
return TelegramEngine(account: account).data.subscribe(EngineDataMap(
|
|
peers.map { TelegramEngine.EngineData.Item.Peer.Presence(id: $0.peerId) }
|
|
))
|
|
|> map { presenceMap -> ([(EngineRenderedPeer, EnginePeer.Presence?)], EnginePeer) in
|
|
var resultPeers: [(EngineRenderedPeer, EnginePeer.Presence?)] = []
|
|
for peer in peers {
|
|
resultPeers.append((peer, presenceMap[peer.peerId].flatMap { $0 }))
|
|
}
|
|
return (resultPeers, accountPeer)
|
|
}
|
|
})
|
|
var animatedIn = false
|
|
self.peersDisposable.set((self.peers.get()
|
|
|> deliverOnMainQueue).start(next: { [weak self] next in
|
|
if let strongSelf = self {
|
|
strongSelf.controllerNode.updatePeers(context: strongSelf.sharedContext.makeTempAccountContext(account: strongSelf.currentAccount), switchableAccounts: strongSelf.switchableAccounts, peers: next.0, accountPeer: next.1, defaultAction: strongSelf.defaultAction)
|
|
|
|
if animateIn && !animatedIn {
|
|
animatedIn = true
|
|
strongSelf.readyDisposable.set((strongSelf.controllerNode.ready.get()
|
|
|> filter({ $0 })
|
|
|> take(1)
|
|
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
strongSelf.controllerNode.animateIn()
|
|
}))
|
|
}
|
|
}
|
|
}))
|
|
}
|
|
}
|
|
|
|
|
|
final class MessageStoryRenderer {
|
|
private let context: AccountContext
|
|
private let presentationData: PresentationData
|
|
private let messages: [Message]
|
|
|
|
let containerNode: ASDisplayNode
|
|
private let instantChatBackgroundNode: WallpaperBackgroundNode
|
|
private let messagesContainerNode: ASDisplayNode
|
|
private var dateHeaderNode: ListViewItemHeaderNode?
|
|
private var messageNodes: [ListViewItemNode]?
|
|
private let addressNode: ImmediateTextNode
|
|
|
|
init(context: AccountContext, messages: [Message]) {
|
|
self.context = context
|
|
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
self.messages = messages
|
|
|
|
self.containerNode = ASDisplayNode()
|
|
|
|
self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
|
self.instantChatBackgroundNode.displaysAsynchronously = false
|
|
|
|
self.messagesContainerNode = ASDisplayNode()
|
|
self.messagesContainerNode.clipsToBounds = true
|
|
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
|
|
|
|
let message = messages.first!
|
|
let addressName = message.peers[message.id.peerId]?.addressName ?? ""
|
|
|
|
self.addressNode = ImmediateTextNode()
|
|
self.addressNode.displaysAsynchronously = false
|
|
self.addressNode.attributedText = NSAttributedString(string: "t.me/\(addressName)/\(message.id.id)", font: Font.medium(14.0), textColor: UIColor(rgb: 0xffffff))
|
|
self.addressNode.textShadowColor = UIColor(rgb: 0x929292, alpha: 0.8)
|
|
|
|
self.containerNode.addSubnode(self.instantChatBackgroundNode)
|
|
self.containerNode.addSubnode(self.messagesContainerNode)
|
|
self.containerNode.addSubnode(self.addressNode)
|
|
}
|
|
|
|
func update(layout: ContainerViewLayout, completion: @escaping (UIImage?) -> Void) {
|
|
self.updateMessagesLayout(layout: layout)
|
|
|
|
Queue.mainQueue().after(0.01) {
|
|
UIGraphicsBeginImageContextWithOptions(layout.size, false, 3.0)
|
|
self.containerNode.view.drawHierarchy(in: CGRect(origin: CGPoint(), size: layout.size), afterScreenUpdates: true)
|
|
let img = UIGraphicsGetImageFromCurrentImageContext()
|
|
UIGraphicsEndImageContext()
|
|
completion(img)
|
|
}
|
|
}
|
|
|
|
private func updateMessagesLayout(layout: ContainerViewLayout) {
|
|
let size = layout.size
|
|
self.containerNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
|
self.instantChatBackgroundNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
|
self.instantChatBackgroundNode.updateLayout(size: size, displayMode: .aspectFill, transition: .immediate)
|
|
self.messagesContainerNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
|
|
|
let addressLayout = self.addressNode.updateLayout(size)
|
|
|
|
let theme = self.presentationData.theme.withUpdated(preview: true)
|
|
let headerItem = self.context.sharedContext.makeChatMessageDateHeaderItem(context: self.context, timestamp: self.messages.first?.timestamp ?? 0, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder)
|
|
|
|
let items: [ListViewItem] = [self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: self.messages, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.theme.chat.defaultWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: nil, availableReactions: nil, isCentered: false)]
|
|
|
|
let inset: CGFloat = 16.0
|
|
let width = layout.size.width - inset * 2.0
|
|
let params = ListViewItemLayoutParams(width: width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
|
|
if let messageNodes = self.messageNodes {
|
|
for i in 0 ..< items.count {
|
|
let itemNode = messageNodes[i]
|
|
items[i].updateNode(async: { $0() }, node: {
|
|
return itemNode
|
|
}, params: params, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], animation: .None, completion: { (layout, apply) in
|
|
let nodeFrame = CGRect(origin: CGPoint(x: 0.0, y: floor((size.height - layout.size.height) / 2.0)), size: CGSize(width: width, height: layout.size.height))
|
|
|
|
itemNode.contentSize = layout.contentSize
|
|
itemNode.insets = layout.insets
|
|
itemNode.frame = nodeFrame
|
|
itemNode.isUserInteractionEnabled = false
|
|
|
|
apply(ListViewItemApply(isOnScreen: true))
|
|
})
|
|
}
|
|
} else {
|
|
var messageNodes: [ListViewItemNode] = []
|
|
for i in 0 ..< items.count {
|
|
var itemNode: ListViewItemNode?
|
|
items[i].nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: true, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], completion: { node, apply in
|
|
itemNode = node
|
|
apply().1(ListViewItemApply(isOnScreen: true))
|
|
})
|
|
itemNode!.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
|
|
itemNode!.isUserInteractionEnabled = false
|
|
messageNodes.append(itemNode!)
|
|
self.messagesContainerNode.addSubnode(itemNode!)
|
|
}
|
|
self.messageNodes = messageNodes
|
|
}
|
|
|
|
var bottomOffset: CGFloat = 0.0
|
|
if let messageNodes = self.messageNodes {
|
|
for itemNode in messageNodes {
|
|
itemNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((size.height - itemNode.frame.height) / 2.0)), size: itemNode.frame.size)
|
|
bottomOffset += itemNode.frame.maxY
|
|
itemNode.updateFrame(itemNode.frame, within: layout.size)
|
|
}
|
|
}
|
|
|
|
self.addressNode.frame = CGRect(origin: CGPoint(x: inset + 16.0, y: bottomOffset + 3.0), size: CGSize(width: addressLayout.width, height: addressLayout.height + 3.0))
|
|
|
|
let dateHeaderNode: ListViewItemHeaderNode
|
|
if let currentDateHeaderNode = self.dateHeaderNode {
|
|
dateHeaderNode = currentDateHeaderNode
|
|
headerItem.updateNode(dateHeaderNode, previous: nil, next: headerItem)
|
|
} else {
|
|
dateHeaderNode = headerItem.node(synchronousLoad: true)
|
|
dateHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
|
|
self.messagesContainerNode.addSubnode(dateHeaderNode)
|
|
self.dateHeaderNode = dateHeaderNode
|
|
}
|
|
|
|
dateHeaderNode.frame = CGRect(origin: CGPoint(x: 0.0, y: bottomOffset), size: CGSize(width: layout.size.width, height: headerItem.height))
|
|
dateHeaderNode.updateLayout(size: self.containerNode.frame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
|
|
}
|
|
}
|
|
|
|
public class ShareToInstagramActivity: UIActivity {
|
|
private let context: AccountContext
|
|
private var activityItems = [Any]()
|
|
|
|
public init(context: AccountContext) {
|
|
self.context = context
|
|
|
|
super.init()
|
|
}
|
|
|
|
public override var activityTitle: String? {
|
|
return self.context.sharedContext.currentPresentationData.with { $0 }.strings.Share_ShareToInstagramStories
|
|
}
|
|
|
|
public override var activityImage: UIImage? {
|
|
return UIImage(bundleImageName: "Share/Instagram")
|
|
}
|
|
|
|
public override var activityType: UIActivity.ActivityType? {
|
|
return UIActivity.ActivityType(rawValue: "org.telegram.Telegram.ShareToInstagram")
|
|
}
|
|
|
|
public override class var activityCategory: UIActivity.Category {
|
|
return .action
|
|
}
|
|
|
|
public override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
|
|
return self.context.sharedContext.applicationBindings.canOpenUrl("instagram-stories://")
|
|
}
|
|
|
|
public override func prepare(withActivityItems activityItems: [Any]) {
|
|
self.activityItems = activityItems
|
|
}
|
|
|
|
public override func perform() {
|
|
if let url = self.activityItems.first as? URL, let data = try? Data(contentsOf: url, options: .mappedIfSafe) {
|
|
let pasteboardItems: [[String: Any]]
|
|
if url.path.hasSuffix(".mp4") {
|
|
pasteboardItems = [["com.instagram.sharedSticker.backgroundVideo": data]]
|
|
} else {
|
|
pasteboardItems = [["com.instagram.sharedSticker.backgroundImage": data]]
|
|
}
|
|
if #available(iOS 10.0, *) {
|
|
UIPasteboard.general.setItems(pasteboardItems, options: [.expirationDate: Date().addingTimeInterval(5 * 60)])
|
|
} else {
|
|
UIPasteboard.general.items = pasteboardItems
|
|
}
|
|
context.sharedContext.applicationBindings.openUrl("instagram-stories://share")
|
|
}
|
|
activityDidFinish(true)
|
|
}
|
|
}
|
|
|
|
|
|
public func presentExternalShare(context: AccountContext, text: String, parentController: ViewController) {
|
|
let activityController = UIActivityViewController(activityItems: [text], applicationActivities: nil)
|
|
if let window = parentController.view.window {
|
|
activityController.popoverPresentationController?.sourceView = window
|
|
activityController.popoverPresentationController?.sourceRect = CGRect(origin: CGPoint(x: window.bounds.width / 2.0, y: window.bounds.size.height - 1.0), size: CGSize(width: 1.0, height: 1.0))
|
|
}
|
|
context.sharedContext.applicationBindings.presentNativeController(activityController)
|
|
}
|
|
|
|
private func restrictedSendingContentsText(peer: EnginePeer, presentationData: PresentationData) -> String {
|
|
var itemList: [String] = []
|
|
|
|
let order: [TelegramChatBannedRightsFlags] = [
|
|
.banSendText,
|
|
.banSendPhotos,
|
|
.banSendVideos,
|
|
.banSendVoice,
|
|
.banSendInstantVideos,
|
|
.banSendFiles,
|
|
.banSendMusic,
|
|
.banSendStickers
|
|
]
|
|
|
|
for right in order {
|
|
if case let .channel(channel) = peer {
|
|
if channel.hasBannedPermission(right) != nil {
|
|
continue
|
|
}
|
|
} else if case let .legacyGroup(group) = peer {
|
|
if group.hasBannedPermission(right) {
|
|
continue
|
|
}
|
|
}
|
|
|
|
var title: String?
|
|
switch right {
|
|
case .banSendText:
|
|
title = presentationData.strings.Chat_SendAllowedContentTypeText
|
|
case .banSendPhotos:
|
|
title = presentationData.strings.Chat_SendAllowedContentTypePhoto
|
|
case .banSendVideos:
|
|
title = presentationData.strings.Chat_SendAllowedContentTypeVideo
|
|
case .banSendVoice:
|
|
title = presentationData.strings.Chat_SendAllowedContentTypeVoiceMessage
|
|
case .banSendInstantVideos:
|
|
title = presentationData.strings.Chat_SendAllowedContentTypeVideoMessage
|
|
case .banSendFiles:
|
|
title = presentationData.strings.Chat_SendAllowedContentTypeFile
|
|
case .banSendMusic:
|
|
title = presentationData.strings.Chat_SendAllowedContentTypeMusic
|
|
case .banSendStickers:
|
|
title = presentationData.strings.Chat_SendAllowedContentTypeSticker
|
|
default:
|
|
break
|
|
}
|
|
if let title {
|
|
itemList.append(title)
|
|
}
|
|
}
|
|
|
|
if itemList.isEmpty {
|
|
return presentationData.strings.Chat_SendNotAllowedPeerText(peer.compactDisplayTitle).string
|
|
}
|
|
|
|
var itemListString = ""
|
|
|
|
if #available(iOS 13.0, *) {
|
|
let listFormatter = ListFormatter()
|
|
listFormatter.locale = localeWithStrings(presentationData.strings)
|
|
if let value = listFormatter.string(from: itemList) {
|
|
itemListString = value
|
|
}
|
|
}
|
|
|
|
if itemListString.isEmpty {
|
|
for i in 0 ..< itemList.count {
|
|
if i != 0 {
|
|
itemListString.append(", ")
|
|
}
|
|
itemListString.append(itemList[i])
|
|
}
|
|
}
|
|
|
|
return presentationData.strings.Chat_SendAllowedContentPeerText(peer.compactDisplayTitle, itemListString).string
|
|
}
|