mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Message preview improvements
This commit is contained in:
parent
3aed18be08
commit
378b7e8ed5
@ -969,6 +969,7 @@ public protocol SharedAccountContext: AnyObject {
|
||||
func makeAttachmentFileController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, bannedSendMedia: (Int32, Bool)?, presentGallery: @escaping () -> Void, presentFiles: @escaping () -> Void, send: @escaping (AnyMediaReference) -> Void) -> AttachmentFileController
|
||||
func makeGalleryCaptionPanelView(context: AccountContext, chatLocation: ChatLocation, isScheduledMessages: Bool, isFile: Bool, customEmojiAvailable: Bool, present: @escaping (ViewController) -> Void, presentInGlobalOverlay: @escaping (ViewController) -> Void) -> NSObject?
|
||||
func makeHashtagSearchController(context: AccountContext, peer: EnginePeer?, query: String, all: Bool) -> ViewController
|
||||
func makeStorySearchController(context: AccountContext, query: String) -> ViewController
|
||||
func makeMyStoriesController(context: AccountContext, isArchive: Bool) -> ViewController
|
||||
func makeArchiveSettingsController(context: AccountContext) -> ViewController
|
||||
func makeFilterSettingsController(context: AccountContext, modal: Bool, scrollToTags: Bool, dismissed: (() -> Void)?) -> ViewController
|
||||
@ -1069,11 +1070,24 @@ public protocol AccountGroupCallContext: AnyObject {
|
||||
public protocol AccountGroupCallContextCache: AnyObject {
|
||||
}
|
||||
|
||||
public final class ChatSendMessageActionSheetControllerMessageEffect {
|
||||
public let id: Int64
|
||||
public struct ChatSendMessageActionSheetControllerSendParameters {
|
||||
public struct Effect {
|
||||
public let id: Int64
|
||||
|
||||
public init(id: Int64) {
|
||||
self.id = id
|
||||
}
|
||||
}
|
||||
|
||||
public init(id: Int64) {
|
||||
self.id = id
|
||||
public var effect: Effect?
|
||||
public var textIsAboveMedia: Bool
|
||||
|
||||
public init(
|
||||
effect: Effect?,
|
||||
textIsAboveMedia: Bool
|
||||
) {
|
||||
self.effect = effect
|
||||
self.textIsAboveMedia = textIsAboveMedia
|
||||
}
|
||||
}
|
||||
|
||||
@ -1089,7 +1103,7 @@ public protocol ChatSendMessageActionSheetControllerSourceSendButtonNode: ASDisp
|
||||
|
||||
public protocol ChatSendMessageActionSheetController: ViewController {
|
||||
typealias SendMode = ChatSendMessageActionSheetControllerSendMode
|
||||
typealias MessageEffect = ChatSendMessageActionSheetControllerMessageEffect
|
||||
typealias SendParameters = ChatSendMessageActionSheetControllerSendParameters
|
||||
}
|
||||
|
||||
public protocol AccountContext: AnyObject {
|
||||
|
@ -3,7 +3,7 @@ import Display
|
||||
import SwiftSignalKit
|
||||
|
||||
public protocol ContactSelectionController: ViewController {
|
||||
var result: Signal<([ContactListPeer], ContactListAction, Bool, Int32?, NSAttributedString?)?, NoError> { get }
|
||||
var result: Signal<([ContactListPeer], ContactListAction, Bool, Int32?, NSAttributedString?, ChatSendMessageActionSheetController.SendParameters?)?, NoError> { get }
|
||||
var displayProgress: Bool { get set }
|
||||
var dismissed: (() -> Void)? { get set }
|
||||
var presentScheduleTimePicker: (@escaping (Int32) -> Void) -> Void { get set }
|
||||
|
@ -147,7 +147,7 @@ public enum PeerSelectionControllerContext {
|
||||
|
||||
public protocol PeerSelectionController: ViewController {
|
||||
var peerSelected: ((EnginePeer, Int64?) -> Void)? { get set }
|
||||
var multiplePeersSelected: (([EnginePeer], [EnginePeer.Id: EnginePeer], NSAttributedString, AttachmentTextInputPanelSendMode, ChatInterfaceForwardOptionsState?, ChatSendMessageActionSheetController.MessageEffect?) -> Void)? { get set }
|
||||
var multiplePeersSelected: (([EnginePeer], [EnginePeer.Id: EnginePeer], NSAttributedString, AttachmentTextInputPanelSendMode, ChatInterfaceForwardOptionsState?, ChatSendMessageActionSheetController.SendParameters?) -> Void)? { get set }
|
||||
var inProgress: Bool { get set }
|
||||
var customDismiss: (() -> Void)? { get set }
|
||||
}
|
||||
|
@ -269,7 +269,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
|
||||
private var validLayout: (CGFloat, CGFloat, CGFloat, UIEdgeInsets, CGFloat, LayoutMetrics, Bool)?
|
||||
|
||||
public var sendMessage: (AttachmentTextInputPanelSendMode, ChatSendMessageActionSheetController.MessageEffect?) -> Void = { _, _ in }
|
||||
public var sendMessage: (AttachmentTextInputPanelSendMode, ChatSendMessageActionSheetController.SendParameters?) -> Void = { _, _ in }
|
||||
public var updateHeight: (Bool) -> Void = { _ in }
|
||||
|
||||
private var updatingInputState = false
|
||||
|
@ -154,14 +154,18 @@ public protocol AttachmentMediaPickerContext {
|
||||
var selectionCount: Signal<Int, NoError> { get }
|
||||
var caption: Signal<NSAttributedString?, NoError> { get }
|
||||
|
||||
var hasCaption: Bool { get }
|
||||
var captionIsAboveMedia: Signal<Bool, NoError> { get }
|
||||
func setCaptionIsAboveMedia(_ captionIsAboveMedia: Bool) -> Void
|
||||
|
||||
var loadingProgress: Signal<CGFloat?, NoError> { get }
|
||||
var mainButtonState: Signal<AttachmentMainButtonState?, NoError> { get }
|
||||
|
||||
func mainButtonAction()
|
||||
|
||||
func setCaption(_ caption: NSAttributedString)
|
||||
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, messageEffect: ChatSendMessageActionSheetController.MessageEffect?)
|
||||
func schedule(messageEffect: ChatSendMessageActionSheetController.MessageEffect?)
|
||||
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, parameters: ChatSendMessageActionSheetController.SendParameters?)
|
||||
func schedule(parameters: ChatSendMessageActionSheetController.SendParameters?)
|
||||
}
|
||||
|
||||
private func generateShadowImage() -> UIImage? {
|
||||
@ -249,7 +253,7 @@ public class AttachmentController: ViewController {
|
||||
|
||||
private var selectionCount: Int = 0
|
||||
|
||||
fileprivate var mediaPickerContext: AttachmentMediaPickerContext? {
|
||||
var mediaPickerContext: AttachmentMediaPickerContext? {
|
||||
didSet {
|
||||
if let mediaPickerContext = self.mediaPickerContext {
|
||||
self.captionDisposable.set((mediaPickerContext.caption
|
||||
@ -317,7 +321,7 @@ public class AttachmentController: ViewController {
|
||||
|
||||
self.container = AttachmentContainer()
|
||||
self.container.canHaveKeyboardFocus = true
|
||||
self.panel = AttachmentPanel(context: controller.context, chatLocation: controller.chatLocation, isScheduledMessages: controller.isScheduledMessages, updatedPresentationData: controller.updatedPresentationData, makeEntityInputView: makeEntityInputView)
|
||||
self.panel = AttachmentPanel(controller: controller, context: controller.context, chatLocation: controller.chatLocation, isScheduledMessages: controller.isScheduledMessages, updatedPresentationData: controller.updatedPresentationData, makeEntityInputView: makeEntityInputView)
|
||||
self.panel.fromMenu = controller.fromMenu
|
||||
self.panel.isStandalone = controller.isStandalone
|
||||
|
||||
@ -423,17 +427,17 @@ public class AttachmentController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
self.panel.sendMessagePressed = { [weak self] mode, messageEffect in
|
||||
self.panel.sendMessagePressed = { [weak self] mode, parameters in
|
||||
if let strongSelf = self {
|
||||
switch mode {
|
||||
case .generic:
|
||||
strongSelf.mediaPickerContext?.send(mode: .generic, attachmentMode: .media, messageEffect: messageEffect)
|
||||
strongSelf.mediaPickerContext?.send(mode: .generic, attachmentMode: .media, parameters: parameters)
|
||||
case .silent:
|
||||
strongSelf.mediaPickerContext?.send(mode: .silently, attachmentMode: .media, messageEffect: messageEffect)
|
||||
strongSelf.mediaPickerContext?.send(mode: .silently, attachmentMode: .media, parameters: parameters)
|
||||
case .schedule:
|
||||
strongSelf.mediaPickerContext?.schedule(messageEffect: messageEffect)
|
||||
strongSelf.mediaPickerContext?.schedule(parameters: parameters)
|
||||
case .whenOnline:
|
||||
strongSelf.mediaPickerContext?.send(mode: .whenOnline, attachmentMode: .media, messageEffect: messageEffect)
|
||||
strongSelf.mediaPickerContext?.send(mode: .whenOnline, attachmentMode: .media, parameters: parameters)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -683,6 +683,7 @@ private final class MainButtonNode: HighlightTrackingButtonNode {
|
||||
}
|
||||
|
||||
final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
private weak var controller: AttachmentController?
|
||||
private let context: AccountContext
|
||||
private let isScheduledMessages: Bool
|
||||
private var presentationData: PresentationData
|
||||
@ -729,7 +730,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
|
||||
var beganTextEditing: () -> Void = {}
|
||||
var textUpdated: (NSAttributedString) -> Void = { _ in }
|
||||
var sendMessagePressed: (AttachmentTextInputPanelSendMode, ChatSendMessageActionSheetController.MessageEffect?) -> Void = { _, _ in }
|
||||
var sendMessagePressed: (AttachmentTextInputPanelSendMode, ChatSendMessageActionSheetController.SendParameters?) -> Void = { _, _ in }
|
||||
var requestLayout: () -> Void = {}
|
||||
var present: (ViewController) -> Void = { _ in }
|
||||
var presentInGlobalOverlay: (ViewController) -> Void = { _ in }
|
||||
@ -738,7 +739,8 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
|
||||
var mainButtonPressed: () -> Void = { }
|
||||
|
||||
init(context: AccountContext, chatLocation: ChatLocation?, isScheduledMessages: Bool, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView?) {
|
||||
init(controller: AttachmentController, context: AccountContext, chatLocation: ChatLocation?, isScheduledMessages: Bool, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView?) {
|
||||
self.controller = controller
|
||||
self.context = context
|
||||
self.updatedPresentationData = updatedPresentationData
|
||||
self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||
@ -982,8 +984,16 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
isReady = .single(true)
|
||||
}
|
||||
|
||||
let _ = (isReady
|
||||
|> deliverOnMainQueue).start(next: { [weak strongSelf] _ in
|
||||
var captionIsAboveMedia: Signal<Bool, NoError> = .single(false)
|
||||
if let controller = strongSelf.controller, let mediaPickerContext = controller.mediaPickerContext {
|
||||
captionIsAboveMedia = mediaPickerContext.captionIsAboveMedia
|
||||
}
|
||||
|
||||
let _ = (combineLatest(
|
||||
isReady,
|
||||
captionIsAboveMedia |> take(1)
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak strongSelf] _, captionIsAboveMedia in
|
||||
guard let strongSelf else {
|
||||
return
|
||||
}
|
||||
@ -998,7 +1008,11 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
sourceSendButton: node,
|
||||
textInputView: textInputNode.textView,
|
||||
mediaPreview: mediaPreview,
|
||||
mediaCaptionIsAbove: (false, { _ in
|
||||
mediaCaptionIsAbove: (captionIsAboveMedia, { [weak strongSelf] value in
|
||||
guard let strongSelf, let controller = strongSelf.controller, let mediaPickerContext = controller.mediaPickerContext else {
|
||||
return
|
||||
}
|
||||
mediaPickerContext.setCaptionIsAboveMedia(value)
|
||||
}),
|
||||
emojiViewProvider: textInputPanelNode.emojiViewProvider,
|
||||
attachment: true,
|
||||
|
@ -389,7 +389,7 @@ public final class CallListController: TelegramBaseController {
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak controller, weak self] result in
|
||||
controller?.dismissSearch()
|
||||
if let strongSelf = self, let (contactPeers, action, _, _, _) = result, let contactPeer = contactPeers.first, case let .peer(peer, _, _) = contactPeer {
|
||||
if let strongSelf = self, let (contactPeers, action, _, _, _, _) = result, let contactPeer = contactPeers.first, case let .peer(peer, _, _) = contactPeer {
|
||||
strongSelf.call(peer.id, isVideo: action == .videoCall, began: {
|
||||
if let strongSelf = self {
|
||||
let _ = (strongSelf.context.sharedContext.hasOngoingCall.get()
|
||||
|
@ -29,8 +29,8 @@ private final class ChatSendMessageActionSheetControllerImpl: ViewController, Ch
|
||||
private let attachment: Bool
|
||||
private let canSendWhenOnline: Bool
|
||||
private let completion: () -> Void
|
||||
private let sendMessage: (SendMode, MessageEffect?) -> Void
|
||||
private let schedule: (MessageEffect?) -> Void
|
||||
private let sendMessage: (SendMode, SendParameters?) -> Void
|
||||
private let schedule: (SendParameters?) -> Void
|
||||
private let reactionItems: [ReactionItem]?
|
||||
|
||||
private var presentationData: PresentationData
|
||||
@ -44,7 +44,7 @@ private final class ChatSendMessageActionSheetControllerImpl: ViewController, Ch
|
||||
|
||||
private let emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?
|
||||
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: EnginePeer.Id?, isScheduledMessages: Bool = false, forwardMessageIds: [EngineMessage.Id]?, hasEntityKeyboard: Bool, gesture: ContextGesture, sourceSendButton: ASDisplayNode, textInputView: UITextView, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, attachment: Bool = false, canSendWhenOnline: Bool, completion: @escaping () -> Void, sendMessage: @escaping (SendMode, MessageEffect?) -> Void, schedule: @escaping (MessageEffect?) -> Void, reactionItems: [ReactionItem]? = nil) {
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: EnginePeer.Id?, isScheduledMessages: Bool = false, forwardMessageIds: [EngineMessage.Id]?, hasEntityKeyboard: Bool, gesture: ContextGesture, sourceSendButton: ASDisplayNode, textInputView: UITextView, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, attachment: Bool = false, canSendWhenOnline: Bool, completion: @escaping () -> Void, sendMessage: @escaping (SendMode, SendParameters?) -> Void, schedule: @escaping (SendParameters?) -> Void, reactionItems: [ReactionItem]? = nil) {
|
||||
self.context = context
|
||||
self.peerId = peerId
|
||||
self.isScheduledMessages = isScheduledMessages
|
||||
@ -108,32 +108,16 @@ private final class ChatSendMessageActionSheetControllerImpl: ViewController, Ch
|
||||
}
|
||||
|
||||
self.displayNode = ChatSendMessageActionSheetControllerNode(context: self.context, presentationData: self.presentationData, reminders: reminders, gesture: gesture, sourceSendButton: self.sourceSendButton, textInputView: self.textInputView, attachment: self.attachment, canSendWhenOnline: self.canSendWhenOnline, forwardedCount: forwardedCount, hasEntityKeyboard: self.hasEntityKeyboard, emojiViewProvider: self.emojiViewProvider, send: { [weak self] in
|
||||
var messageEffect: MessageEffect?
|
||||
if let selectedEffect = self?.controllerNode.selectedMessageEffect {
|
||||
messageEffect = MessageEffect(id: selectedEffect.id)
|
||||
}
|
||||
self?.sendMessage(.generic, messageEffect)
|
||||
self?.sendMessage(.generic, nil)
|
||||
self?.dismiss(cancel: false)
|
||||
}, sendSilently: { [weak self] in
|
||||
var messageEffect: MessageEffect?
|
||||
if let selectedEffect = self?.controllerNode.selectedMessageEffect {
|
||||
messageEffect = MessageEffect(id: selectedEffect.id)
|
||||
}
|
||||
self?.sendMessage(.silently, messageEffect)
|
||||
self?.sendMessage(.silently, nil)
|
||||
self?.dismiss(cancel: false)
|
||||
}, sendWhenOnline: { [weak self] in
|
||||
var messageEffect: MessageEffect?
|
||||
if let selectedEffect = self?.controllerNode.selectedMessageEffect {
|
||||
messageEffect = MessageEffect(id: selectedEffect.id)
|
||||
}
|
||||
self?.sendMessage(.whenOnline, messageEffect)
|
||||
self?.sendMessage(.whenOnline, nil)
|
||||
self?.dismiss(cancel: false)
|
||||
}, schedule: !canSchedule ? nil : { [weak self] in
|
||||
var messageEffect: MessageEffect?
|
||||
if let selectedEffect = self?.controllerNode.selectedMessageEffect {
|
||||
messageEffect = MessageEffect(id: selectedEffect.id)
|
||||
}
|
||||
self?.schedule(messageEffect)
|
||||
self?.schedule(nil)
|
||||
self?.dismiss(cancel: false)
|
||||
}, cancel: { [weak self] in
|
||||
self?.dismiss(cancel: true)
|
||||
@ -191,8 +175,8 @@ public func makeChatSendMessageActionSheetController(
|
||||
attachment: Bool = false,
|
||||
canSendWhenOnline: Bool,
|
||||
completion: @escaping () -> Void,
|
||||
sendMessage: @escaping (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.MessageEffect?) -> Void,
|
||||
schedule: @escaping (ChatSendMessageActionSheetController.MessageEffect?) -> Void,
|
||||
sendMessage: @escaping (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.SendParameters?) -> Void,
|
||||
schedule: @escaping (ChatSendMessageActionSheetController.SendParameters?) -> Void,
|
||||
reactionItems: [ReactionItem]? = nil,
|
||||
availableMessageEffects: AvailableMessageEffects? = nil,
|
||||
isPremium: Bool = false
|
||||
|
@ -69,8 +69,8 @@ final class ChatSendMessageContextScreenComponent: Component {
|
||||
let attachment: Bool
|
||||
let canSendWhenOnline: Bool
|
||||
let completion: () -> Void
|
||||
let sendMessage: (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.MessageEffect?) -> Void
|
||||
let schedule: (ChatSendMessageActionSheetController.MessageEffect?) -> Void
|
||||
let sendMessage: (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.SendParameters?) -> Void
|
||||
let schedule: (ChatSendMessageActionSheetController.SendParameters?) -> Void
|
||||
let reactionItems: [ReactionItem]?
|
||||
let availableMessageEffects: AvailableMessageEffects?
|
||||
let isPremium: Bool
|
||||
@ -92,8 +92,8 @@ final class ChatSendMessageContextScreenComponent: Component {
|
||||
attachment: Bool,
|
||||
canSendWhenOnline: Bool,
|
||||
completion: @escaping () -> Void,
|
||||
sendMessage: @escaping (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.MessageEffect?) -> Void,
|
||||
schedule: @escaping (ChatSendMessageActionSheetController.MessageEffect?) -> Void,
|
||||
sendMessage: @escaping (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.SendParameters?) -> Void,
|
||||
schedule: @escaping (ChatSendMessageActionSheetController.SendParameters?) -> Void,
|
||||
reactionItems: [ReactionItem]?,
|
||||
availableMessageEffects: AvailableMessageEffects?,
|
||||
isPremium: Bool
|
||||
@ -222,7 +222,13 @@ final class ChatSendMessageContextScreenComponent: Component {
|
||||
return
|
||||
}
|
||||
self.animateOutToEmpty = true
|
||||
component.sendMessage(.generic, self.selectedMessageEffect.flatMap({ ChatSendMessageActionSheetController.MessageEffect(id: $0.id) }))
|
||||
|
||||
let sendParameters = ChatSendMessageActionSheetController.SendParameters(
|
||||
effect: self.selectedMessageEffect.flatMap({ ChatSendMessageActionSheetController.SendParameters.Effect(id: $0.id) }),
|
||||
textIsAboveMedia: self.mediaCaptionIsAbove
|
||||
)
|
||||
|
||||
component.sendMessage(.generic, sendParameters)
|
||||
self.environment?.controller()?.dismiss()
|
||||
}
|
||||
|
||||
@ -327,6 +333,13 @@ final class ChatSendMessageContextScreenComponent: Component {
|
||||
)
|
||||
}
|
||||
|
||||
let textString: NSAttributedString
|
||||
if let attributedText = component.textInputView.attributedText {
|
||||
textString = attributedText
|
||||
} else {
|
||||
textString = NSAttributedString(string: " ", font: Font.regular(17.0), textColor: .black)
|
||||
}
|
||||
|
||||
let sendButton: SendButton
|
||||
if let current = self.sendButton {
|
||||
sendButton = current
|
||||
@ -365,7 +378,7 @@ final class ChatSendMessageContextScreenComponent: Component {
|
||||
}
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
if component.mediaCaptionIsAbove != nil {
|
||||
if component.mediaCaptionIsAbove != nil, textString.length != 0, case .media = component.mediaPreview?.layoutType {
|
||||
//TODO:localize
|
||||
let mediaCaptionIsAbove = self.mediaCaptionIsAbove
|
||||
items.append(.action(ContextMenuActionItem(
|
||||
@ -398,7 +411,13 @@ final class ChatSendMessageContextScreenComponent: Component {
|
||||
return
|
||||
}
|
||||
self.animateOutToEmpty = true
|
||||
component.sendMessage(.silently, self.selectedMessageEffect.flatMap({ ChatSendMessageActionSheetController.MessageEffect(id: $0.id) }))
|
||||
|
||||
let sendParameters = ChatSendMessageActionSheetController.SendParameters(
|
||||
effect: self.selectedMessageEffect.flatMap({ ChatSendMessageActionSheetController.SendParameters.Effect(id: $0.id) }),
|
||||
textIsAboveMedia: self.mediaCaptionIsAbove
|
||||
)
|
||||
|
||||
component.sendMessage(.silently, sendParameters)
|
||||
self.environment?.controller()?.dismiss()
|
||||
}
|
||||
)))
|
||||
@ -414,7 +433,13 @@ final class ChatSendMessageContextScreenComponent: Component {
|
||||
return
|
||||
}
|
||||
self.animateOutToEmpty = true
|
||||
component.sendMessage(.whenOnline, self.selectedMessageEffect.flatMap({ ChatSendMessageActionSheetController.MessageEffect(id: $0.id) }))
|
||||
|
||||
let sendParameters = ChatSendMessageActionSheetController.SendParameters(
|
||||
effect: self.selectedMessageEffect.flatMap({ ChatSendMessageActionSheetController.SendParameters.Effect(id: $0.id) }),
|
||||
textIsAboveMedia: self.mediaCaptionIsAbove
|
||||
)
|
||||
|
||||
component.sendMessage(.whenOnline, sendParameters)
|
||||
self.environment?.controller()?.dismiss()
|
||||
}
|
||||
)))
|
||||
@ -431,7 +456,13 @@ final class ChatSendMessageContextScreenComponent: Component {
|
||||
return
|
||||
}
|
||||
self.animateOutToEmpty = true
|
||||
component.schedule(self.selectedMessageEffect.flatMap({ ChatSendMessageActionSheetController.MessageEffect(id: $0.id) }))
|
||||
|
||||
let sendParameters = ChatSendMessageActionSheetController.SendParameters(
|
||||
effect: self.selectedMessageEffect.flatMap({ ChatSendMessageActionSheetController.SendParameters.Effect(id: $0.id) }),
|
||||
textIsAboveMedia: self.mediaCaptionIsAbove
|
||||
)
|
||||
|
||||
component.schedule(sendParameters)
|
||||
self.environment?.controller()?.dismiss()
|
||||
}
|
||||
)))
|
||||
@ -499,13 +530,6 @@ final class ChatSendMessageContextScreenComponent: Component {
|
||||
self.addSubview(messageItemView)
|
||||
}
|
||||
|
||||
let textString: NSAttributedString
|
||||
if let attributedText = component.textInputView.attributedText {
|
||||
textString = attributedText
|
||||
} else {
|
||||
textString = NSAttributedString(string: " ", font: Font.regular(17.0), textColor: .black)
|
||||
}
|
||||
|
||||
let localSourceTextInputViewFrame = convertFrame(component.textInputView.bounds, from: component.textInputView, to: self)
|
||||
|
||||
let sourceMessageTextInsets = UIEdgeInsets(top: 7.0, left: 12.0, bottom: 6.0, right: 20.0)
|
||||
@ -952,12 +976,19 @@ final class ChatSendMessageContextScreenComponent: Component {
|
||||
Transition.immediate.setScale(view: actionsStackNode.view, scale: 1.0)
|
||||
actionsStackNode.layer.animateSpring(from: 0.001 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.42, damping: 104.0)
|
||||
|
||||
messageItemView.animateIn(transition: transition)
|
||||
messageItemView.animateIn(
|
||||
sourceTextInputView: component.textInputView as? ChatInputTextView,
|
||||
transition: transition
|
||||
)
|
||||
case .animatedOut:
|
||||
transition.setAlpha(view: actionsStackNode.view, alpha: 0.0)
|
||||
transition.setScale(view: actionsStackNode.view, scale: 0.001)
|
||||
|
||||
messageItemView.animateOut(toEmpty: self.animateOutToEmpty, transition: transition)
|
||||
messageItemView.animateOut(
|
||||
sourceTextInputView: component.textInputView as? ChatInputTextView,
|
||||
toEmpty: self.animateOutToEmpty,
|
||||
transition: transition
|
||||
)
|
||||
}
|
||||
} else {
|
||||
switch self.presentationAnimationState {
|
||||
@ -1141,8 +1172,8 @@ public class ChatSendMessageContextScreen: ViewControllerComponentContainer, Cha
|
||||
attachment: Bool,
|
||||
canSendWhenOnline: Bool,
|
||||
completion: @escaping () -> Void,
|
||||
sendMessage: @escaping (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.MessageEffect?) -> Void,
|
||||
schedule: @escaping (ChatSendMessageActionSheetController.MessageEffect?) -> Void,
|
||||
sendMessage: @escaping (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.SendParameters?) -> Void,
|
||||
schedule: @escaping (ChatSendMessageActionSheetController.SendParameters?) -> Void,
|
||||
reactionItems: [ReactionItem]?,
|
||||
availableMessageEffects: AvailableMessageEffects?,
|
||||
isPremium: Bool
|
||||
|
@ -155,6 +155,7 @@ final class MessageItemView: UIView {
|
||||
|
||||
private var chatTheme: ChatPresentationThemeData?
|
||||
private var currentSize: CGSize?
|
||||
private var currentMediaCaptionIsAbove: Bool = false
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.backgroundWallpaperNode = ChatMessageBubbleBackdrop()
|
||||
@ -162,6 +163,7 @@ final class MessageItemView: UIView {
|
||||
self.backgroundNode.backdropNode = self.backgroundWallpaperNode
|
||||
|
||||
self.textClippingContainer = UIView()
|
||||
self.textClippingContainer.layer.anchorPoint = CGPoint()
|
||||
self.textClippingContainer.clipsToBounds = true
|
||||
|
||||
super.init(frame: frame)
|
||||
@ -178,13 +180,20 @@ final class MessageItemView: UIView {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
func animateIn(transition: Transition) {
|
||||
func animateIn(
|
||||
sourceTextInputView: ChatInputTextView?,
|
||||
transition: Transition
|
||||
) {
|
||||
if let mediaPreview = self.mediaPreview {
|
||||
mediaPreview.animateIn(transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
func animateOut(toEmpty: Bool, transition: Transition) {
|
||||
func animateOut(
|
||||
sourceTextInputView: ChatInputTextView?,
|
||||
toEmpty: Bool,
|
||||
transition: Transition
|
||||
) {
|
||||
if let mediaPreview = self.mediaPreview {
|
||||
if toEmpty {
|
||||
mediaPreview.animateOutOnSend(transition: transition)
|
||||
@ -266,6 +275,8 @@ final class MessageItemView: UIView {
|
||||
backgroundNode: backgroundNode
|
||||
)
|
||||
|
||||
let alphaTransition: Transition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.25)
|
||||
|
||||
if let sourceMediaPreview {
|
||||
let mediaPreviewClippingView: UIView
|
||||
if let current = self.mediaPreviewClippingView {
|
||||
@ -304,7 +315,11 @@ final class MessageItemView: UIView {
|
||||
let backgroundAlpha: CGFloat
|
||||
switch sourceMediaPreview.layoutType {
|
||||
case .media:
|
||||
backgroundAlpha = explicitBackgroundSize != nil ? 0.0 : 1.0
|
||||
if textString.length != 0 {
|
||||
backgroundAlpha = explicitBackgroundSize != nil ? 0.0 : 1.0
|
||||
} else {
|
||||
backgroundAlpha = 0.0
|
||||
}
|
||||
case .message, .videoMessage:
|
||||
backgroundAlpha = 0.0
|
||||
}
|
||||
@ -312,7 +327,7 @@ final class MessageItemView: UIView {
|
||||
var backgroundFrame = mediaPreviewFrame.insetBy(dx: -2.0, dy: -2.0)
|
||||
backgroundFrame.size.width += 6.0
|
||||
|
||||
if textString.length != 0 {
|
||||
if textString.length != 0, case .media = sourceMediaPreview.layoutType {
|
||||
let textNode: ChatInputTextNode
|
||||
if let current = self.textNode {
|
||||
textNode = current
|
||||
@ -414,9 +429,10 @@ final class MessageItemView: UIView {
|
||||
textClippingContainerBounds.origin.y = max(0.0, textClippingContainerBounds.origin.y)
|
||||
}
|
||||
|
||||
transition.setPosition(view: self.textClippingContainer, position: textClippingContainerFrame.center)
|
||||
transition.setPosition(view: self.textClippingContainer, position: textClippingContainerFrame.origin)
|
||||
transition.setBounds(view: self.textClippingContainer, bounds: textClippingContainerBounds)
|
||||
|
||||
alphaTransition.setAlpha(view: textNode.view, alpha: backgroundAlpha)
|
||||
transition.setFrame(view: textNode.view, frame: CGRect(origin: CGPoint(x: textFrame.minX + textPositioningInsets.left - textClippingContainerFrame.minX, y: textFrame.minY + textPositioningInsets.top - textClippingContainerFrame.minY), size: CGSize(width: maxTextWidth, height: textHeight)))
|
||||
self.updateTextContents()
|
||||
}
|
||||
@ -424,10 +440,10 @@ final class MessageItemView: UIView {
|
||||
transition.setFrame(view: sourceMediaPreview.view, frame: mediaPreviewFrame)
|
||||
|
||||
transition.setFrame(view: self.backgroundWallpaperNode.view, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
|
||||
transition.setAlpha(view: self.backgroundWallpaperNode.view, alpha: backgroundAlpha)
|
||||
alphaTransition.setAlpha(view: self.backgroundWallpaperNode.view, alpha: backgroundAlpha)
|
||||
self.backgroundWallpaperNode.updateFrame(backgroundFrame, transition: transition.containedViewLayoutTransition)
|
||||
transition.setFrame(view: self.backgroundNode.view, frame: backgroundFrame)
|
||||
transition.setAlpha(view: self.backgroundNode.view, alpha: backgroundAlpha)
|
||||
alphaTransition.setAlpha(view: self.backgroundNode.view, alpha: backgroundAlpha)
|
||||
self.backgroundNode.updateLayout(size: backgroundFrame.size, transition: transition.containedViewLayoutTransition)
|
||||
|
||||
if let effectIcon = self.effectIcon, let effectIconSize {
|
||||
@ -598,7 +614,7 @@ final class MessageItemView: UIView {
|
||||
textClippingContainerBounds.origin.y = max(0.0, textClippingContainerBounds.origin.y)
|
||||
}
|
||||
|
||||
transition.setPosition(view: self.textClippingContainer, position: textClippingContainerFrame.center)
|
||||
transition.setPosition(view: self.textClippingContainer, position: textClippingContainerFrame.origin)
|
||||
transition.setBounds(view: self.textClippingContainer, bounds: textClippingContainerBounds)
|
||||
|
||||
textNode.view.frame = CGRect(origin: CGPoint(x: textFrame.minX + textPositioningInsets.left - textClippingContainerFrame.minX, y: textFrame.minY + textPositioningInsets.top - textClippingContainerFrame.minY), size: CGSize(width: maxTextWidth, height: textHeight))
|
||||
|
@ -538,6 +538,17 @@ private final class CreatePollContext: AttachmentMediaPickerContext {
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
var captionIsAboveMedia: Signal<Bool, NoError> {
|
||||
return .single(false)
|
||||
}
|
||||
|
||||
var hasCaption: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func setCaptionIsAboveMedia(_ captionIsAboveMedia: Bool) -> Void {
|
||||
}
|
||||
|
||||
public var loadingProgress: Signal<CGFloat?, NoError> {
|
||||
return .single(nil)
|
||||
}
|
||||
@ -549,10 +560,10 @@ private final class CreatePollContext: AttachmentMediaPickerContext {
|
||||
func setCaption(_ caption: NSAttributedString) {
|
||||
}
|
||||
|
||||
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, messageEffect: ChatSendMessageActionSheetController.MessageEffect?) {
|
||||
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, parameters: ChatSendMessageActionSheetController.SendParameters?) {
|
||||
}
|
||||
|
||||
func schedule(messageEffect: ChatSendMessageActionSheetController.MessageEffect?) {
|
||||
func schedule(parameters: ChatSendMessageActionSheetController.SendParameters?) {
|
||||
}
|
||||
|
||||
func mainButtonAction() {
|
||||
|
@ -393,6 +393,17 @@ private final class LocationPickerContext: AttachmentMediaPickerContext {
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
var hasCaption: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
var captionIsAboveMedia: Signal<Bool, NoError> {
|
||||
return .single(false)
|
||||
}
|
||||
|
||||
func setCaptionIsAboveMedia(_ captionIsAboveMedia: Bool) -> Void {
|
||||
}
|
||||
|
||||
public var loadingProgress: Signal<CGFloat?, NoError> {
|
||||
return .single(nil)
|
||||
}
|
||||
@ -404,10 +415,10 @@ private final class LocationPickerContext: AttachmentMediaPickerContext {
|
||||
func setCaption(_ caption: NSAttributedString) {
|
||||
}
|
||||
|
||||
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, messageEffect: ChatSendMessageActionSheetController.MessageEffect?) {
|
||||
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, parameters: ChatSendMessageActionSheetController.SendParameters?) {
|
||||
}
|
||||
|
||||
func schedule(messageEffect: ChatSendMessageActionSheetController.MessageEffect?) {
|
||||
func schedule(parameters: ChatSendMessageActionSheetController.SendParameters?) {
|
||||
}
|
||||
|
||||
func mainButtonAction() {
|
||||
|
@ -33,14 +33,23 @@ final class MediaPickerInteraction {
|
||||
let openSelectedMedia: (TGMediaSelectableItem, UIImage?) -> Void
|
||||
let openDraft: (MediaEditorDraft, UIImage?) -> Void
|
||||
let toggleSelection: (TGMediaSelectableItem, Bool, Bool) -> Bool
|
||||
let sendSelected: (TGMediaSelectableItem?, Bool, Int32?, Bool, ChatSendMessageActionSheetController.MessageEffect?, @escaping () -> Void) -> Void
|
||||
let schedule: (ChatSendMessageActionSheetController.MessageEffect?) -> Void
|
||||
let sendSelected: (TGMediaSelectableItem?, Bool, Int32?, Bool, ChatSendMessageActionSheetController.SendParameters?, @escaping () -> Void) -> Void
|
||||
let schedule: (ChatSendMessageActionSheetController.SendParameters?) -> Void
|
||||
let dismissInput: () -> Void
|
||||
let selectionState: TGMediaSelectionContext?
|
||||
let editingState: TGMediaEditingContext
|
||||
var hiddenMediaId: String?
|
||||
|
||||
init(downloadManager: AssetDownloadManager, openMedia: @escaping (PHFetchResult<PHAsset>, Int, UIImage?) -> Void, openSelectedMedia: @escaping (TGMediaSelectableItem, UIImage?) -> Void, openDraft: @escaping (MediaEditorDraft, UIImage?) -> Void, toggleSelection: @escaping (TGMediaSelectableItem, Bool, Bool) -> Bool, sendSelected: @escaping (TGMediaSelectableItem?, Bool, Int32?, Bool, ChatSendMessageActionSheetController.MessageEffect?, @escaping () -> Void) -> Void, schedule: @escaping (ChatSendMessageActionSheetController.MessageEffect?) -> Void, dismissInput: @escaping () -> Void, selectionState: TGMediaSelectionContext?, editingState: TGMediaEditingContext) {
|
||||
var captionIsAboveMedia: Bool = false {
|
||||
didSet {
|
||||
if self.captionIsAboveMedia != oldValue {
|
||||
self.captionIsAboveMediaValue.set(self.captionIsAboveMedia)
|
||||
}
|
||||
}
|
||||
}
|
||||
let captionIsAboveMediaValue = ValuePromise<Bool>(false)
|
||||
|
||||
init(downloadManager: AssetDownloadManager, openMedia: @escaping (PHFetchResult<PHAsset>, Int, UIImage?) -> Void, openSelectedMedia: @escaping (TGMediaSelectableItem, UIImage?) -> Void, openDraft: @escaping (MediaEditorDraft, UIImage?) -> Void, toggleSelection: @escaping (TGMediaSelectableItem, Bool, Bool) -> Bool, sendSelected: @escaping (TGMediaSelectableItem?, Bool, Int32?, Bool, ChatSendMessageActionSheetController.SendParameters?, @escaping () -> Void) -> Void, schedule: @escaping (ChatSendMessageActionSheetController.SendParameters?) -> Void, dismissInput: @escaping () -> Void, selectionState: TGMediaSelectionContext?, editingState: TGMediaEditingContext) {
|
||||
self.downloadManager = downloadManager
|
||||
self.openMedia = openMedia
|
||||
self.openSelectedMedia = openSelectedMedia
|
||||
@ -200,7 +209,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
public var presentFilePicker: () -> Void = {}
|
||||
|
||||
private var completed = false
|
||||
public var legacyCompletion: (_ signals: [Any], _ silently: Bool, _ scheduleTime: Int32?, ChatSendMessageActionSheetController.MessageEffect?, @escaping (String) -> UIView?, @escaping () -> Void) -> Void = { _, _, _, _, _, _ in }
|
||||
public var legacyCompletion: (_ signals: [Any], _ silently: Bool, _ scheduleTime: Int32?, ChatSendMessageActionSheetController.SendParameters?, @escaping (String) -> UIView?, @escaping () -> Void) -> Void = { _, _, _, _, _, _ in }
|
||||
|
||||
public var requestAttachmentMenuExpansion: () -> Void = { }
|
||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||
@ -1218,12 +1227,24 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func send(asFile: Bool = false, silently: Bool, scheduleTime: Int32?, animated: Bool, messageEffect: ChatSendMessageActionSheetController.MessageEffect?, completion: @escaping () -> Void) {
|
||||
fileprivate func send(asFile: Bool = false, silently: Bool, scheduleTime: Int32?, animated: Bool, parameters: ChatSendMessageActionSheetController.SendParameters?, completion: @escaping () -> Void) {
|
||||
guard let controller = self.controller, !controller.completed else {
|
||||
return
|
||||
}
|
||||
controller.dismissAllTooltips()
|
||||
|
||||
var parameters = parameters
|
||||
if parameters == nil {
|
||||
var textIsAboveMedia = false
|
||||
if let interaction = controller.interaction {
|
||||
textIsAboveMedia = interaction.captionIsAboveMedia
|
||||
}
|
||||
parameters = ChatSendMessageActionSheetController.SendParameters(
|
||||
effect: nil,
|
||||
textIsAboveMedia: textIsAboveMedia
|
||||
)
|
||||
}
|
||||
|
||||
var hasHeic = false
|
||||
let allItems = controller.interaction?.selectionState?.selectedItems() ?? []
|
||||
for item in allItems {
|
||||
@ -1246,7 +1267,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
return
|
||||
}
|
||||
controller.completed = true
|
||||
controller.legacyCompletion(signals, silently, scheduleTime, messageEffect, { [weak self] identifier in
|
||||
controller.legacyCompletion(signals, silently, scheduleTime, parameters, { [weak self] identifier in
|
||||
return !asFile ? self?.getItemSnapshot(identifier) : nil
|
||||
}, { [weak self] in
|
||||
completion()
|
||||
@ -1959,18 +1980,18 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}, sendSelected: { [weak self] currentItem, silently, scheduleTime, animated, messageEffect, completion in
|
||||
}, sendSelected: { [weak self] currentItem, silently, scheduleTime, animated, parameters, completion in
|
||||
if let strongSelf = self, let selectionState = strongSelf.interaction?.selectionState, !strongSelf.isDismissing {
|
||||
strongSelf.isDismissing = true
|
||||
if let currentItem = currentItem {
|
||||
selectionState.setItem(currentItem, selected: true)
|
||||
}
|
||||
strongSelf.controllerNode.send(silently: silently, scheduleTime: scheduleTime, animated: animated, messageEffect: messageEffect, completion: completion)
|
||||
strongSelf.controllerNode.send(silently: silently, scheduleTime: scheduleTime, animated: animated, parameters: parameters, completion: completion)
|
||||
}
|
||||
}, schedule: { [weak self] messageEffect in
|
||||
}, schedule: { [weak self] parameters in
|
||||
if let strongSelf = self {
|
||||
strongSelf.presentSchedulePicker(false, { [weak self] time in
|
||||
self?.interaction?.sendSelected(nil, false, time, true, messageEffect, {})
|
||||
self?.interaction?.sendSelected(nil, false, time, true, parameters, {})
|
||||
})
|
||||
}
|
||||
}, dismissInput: { [weak self] in
|
||||
@ -2419,10 +2440,18 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var isCaptionAboveMediaAvailable: Signal<Bool, NoError> = .single(false)
|
||||
if let mediaPickerContext = self.mediaPickerContext {
|
||||
isCaptionAboveMediaAvailable = .single(mediaPickerContext.hasCaption)
|
||||
}
|
||||
|
||||
let items: Signal<ContextController.Items, NoError> = self.groupedPromise.get()
|
||||
let items: Signal<ContextController.Items, NoError> = combineLatest(
|
||||
self.groupedPromise.get(),
|
||||
isCaptionAboveMediaAvailable
|
||||
)
|
||||
|> deliverOnMainQueue
|
||||
|> map { [weak self] grouped -> ContextController.Items in
|
||||
|> map { [weak self] grouped, isCaptionAboveMediaAvailable -> ContextController.Items in
|
||||
var items: [ContextMenuItem] = []
|
||||
if !hasSpoilers {
|
||||
items.append(.action(ContextMenuActionItem(text: selectionCount > 1 ? strings.Attachment_SendAsFiles : strings.Attachment_SendAsFile, icon: { theme in
|
||||
@ -2430,7 +2459,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
}, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
|
||||
self?.controllerNode.send(asFile: true, silently: false, scheduleTime: nil, animated: true, messageEffect: nil, completion: {})
|
||||
self?.controllerNode.send(asFile: true, silently: false, scheduleTime: nil, animated: true, parameters: nil, completion: {})
|
||||
})))
|
||||
}
|
||||
if selectionCount > 1 {
|
||||
@ -2458,25 +2487,48 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
self?.groupedValue = false
|
||||
})))
|
||||
}
|
||||
if isSpoilerAvailable {
|
||||
if isSpoilerAvailable || (selectionCount > 0 && isCaptionAboveMediaAvailable) {
|
||||
if !items.isEmpty {
|
||||
items.append(.separator)
|
||||
}
|
||||
items.append(.action(ContextMenuActionItem(text: hasGeneric ? strings.Attachment_EnableSpoiler : strings.Attachment_DisableSpoiler, icon: { _ in return nil }, iconAnimation: ContextMenuActionItem.IconAnimation(
|
||||
name: "anim_spoiler",
|
||||
loop: true
|
||||
), action: { [weak self] _, f in
|
||||
f(.default)
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
||||
if isCaptionAboveMediaAvailable {
|
||||
var mediaCaptionIsAbove = false
|
||||
if let interaction = self?.interaction {
|
||||
mediaCaptionIsAbove = interaction.captionIsAboveMedia
|
||||
}
|
||||
|
||||
if let selectionContext = strongSelf.interaction?.selectionState, let editingContext = strongSelf.interaction?.editingState {
|
||||
for case let item as TGMediaEditableItem in selectionContext.selectedItems() {
|
||||
editingContext.setSpoiler(hasGeneric, for: item)
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: mediaCaptionIsAbove ? "Move Caption Down" : "Move Caption Up", icon: { _ in return nil }, iconAnimation: ContextMenuActionItem.IconAnimation(
|
||||
name: !mediaCaptionIsAbove ? "message_preview_sort_above" : "message_preview_sort_below"
|
||||
), action: { [weak self] _, f in
|
||||
f(.default)
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
}
|
||||
})))
|
||||
|
||||
if let interaction = strongSelf.interaction {
|
||||
interaction.captionIsAboveMedia = !interaction.captionIsAboveMedia
|
||||
}
|
||||
})))
|
||||
}
|
||||
if isSpoilerAvailable {
|
||||
items.append(.action(ContextMenuActionItem(text: hasGeneric ? strings.Attachment_EnableSpoiler : strings.Attachment_DisableSpoiler, icon: { _ in return nil }, iconAnimation: ContextMenuActionItem.IconAnimation(
|
||||
name: "anim_spoiler",
|
||||
loop: true
|
||||
), action: { [weak self] _, f in
|
||||
f(.default)
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
if let selectionContext = strongSelf.interaction?.selectionState, let editingContext = strongSelf.interaction?.editingState {
|
||||
for case let item as TGMediaEditableItem in selectionContext.selectedItems() {
|
||||
editingContext.setSpoiler(hasGeneric, for: item)
|
||||
}
|
||||
}
|
||||
})))
|
||||
}
|
||||
}
|
||||
return ContextController.Items(content: .list(items))
|
||||
}
|
||||
@ -2534,7 +2586,18 @@ final class MediaPickerContext: AttachmentMediaPickerContext {
|
||||
|
||||
var caption: Signal<NSAttributedString?, NoError> {
|
||||
return Signal { [weak self] subscriber in
|
||||
let disposable = self?.controller?.interaction?.editingState.forcedCaption().start(next: { caption in
|
||||
guard let self else {
|
||||
subscriber.putNext(nil)
|
||||
subscriber.putCompletion()
|
||||
return EmptyDisposable
|
||||
}
|
||||
guard let caption = self.controller?.interaction?.editingState.forcedCaption() else {
|
||||
subscriber.putNext(nil)
|
||||
subscriber.putCompletion()
|
||||
return EmptyDisposable
|
||||
}
|
||||
|
||||
let disposable = caption.start(next: { caption in
|
||||
if let caption = caption as? NSAttributedString {
|
||||
subscriber.putNext(caption)
|
||||
} else {
|
||||
@ -2546,6 +2609,34 @@ final class MediaPickerContext: AttachmentMediaPickerContext {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var hasCaption: Bool {
|
||||
guard let isForcedCaption = self.controller?.interaction?.editingState.isForcedCaption() else {
|
||||
return false
|
||||
}
|
||||
return isForcedCaption
|
||||
}
|
||||
|
||||
var captionIsAboveMedia: Signal<Bool, NoError> {
|
||||
return Signal { [weak self] subscriber in
|
||||
guard let interaction = self?.controller?.interaction else {
|
||||
subscriber.putNext(false)
|
||||
subscriber.putCompletion()
|
||||
|
||||
return EmptyDisposable
|
||||
}
|
||||
let disposable = interaction.captionIsAboveMediaValue.get().start(next: { value in
|
||||
subscriber.putNext(value)
|
||||
}, error: { _ in }, completed: { })
|
||||
return ActionDisposable {
|
||||
disposable.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setCaptionIsAboveMedia(_ captionIsAboveMedia: Bool) -> Void {
|
||||
self.controller?.interaction?.captionIsAboveMedia = captionIsAboveMedia
|
||||
}
|
||||
|
||||
public var loadingProgress: Signal<CGFloat?, NoError> {
|
||||
return .single(nil)
|
||||
@ -2563,12 +2654,12 @@ final class MediaPickerContext: AttachmentMediaPickerContext {
|
||||
self.controller?.interaction?.editingState.setForcedCaption(caption, skipUpdate: true)
|
||||
}
|
||||
|
||||
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, messageEffect: ChatSendMessageActionSheetController.MessageEffect?) {
|
||||
self.controller?.interaction?.sendSelected(nil, mode == .silently, mode == .whenOnline ? scheduleWhenOnlineTimestamp : nil, true, messageEffect, {})
|
||||
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, parameters: ChatSendMessageActionSheetController.SendParameters?) {
|
||||
self.controller?.interaction?.sendSelected(nil, mode == .silently, mode == .whenOnline ? scheduleWhenOnlineTimestamp : nil, true, parameters, {})
|
||||
}
|
||||
|
||||
func schedule(messageEffect: ChatSendMessageActionSheetController.MessageEffect?) {
|
||||
self.controller?.interaction?.schedule(messageEffect)
|
||||
func schedule(parameters: ChatSendMessageActionSheetController.SendParameters?) {
|
||||
self.controller?.interaction?.schedule(parameters)
|
||||
}
|
||||
|
||||
func mainButtonAction() {
|
||||
|
@ -976,7 +976,14 @@ final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, AS
|
||||
let graphics = PresentationResourcesChat.principalGraphics(theme: theme, wallpaper: wallpaper, bubbleCorners: bubbleCorners)
|
||||
|
||||
var groupIndex = 0
|
||||
var isFirstGroup = true
|
||||
for (items, groupSize) in groupLayouts {
|
||||
if isFirstGroup {
|
||||
isFirstGroup = false
|
||||
} else {
|
||||
contentHeight += spacing
|
||||
}
|
||||
|
||||
var groupRect = CGRect(origin: CGPoint(x: 0.0, y: insets.top + contentHeight), size: groupSize)
|
||||
if !self.isExternalPreview {
|
||||
groupRect.origin.x = insets.left + floorToScreenPixels((size.width - insets.left - insets.right - groupSize.width) / 2.0)
|
||||
@ -1005,7 +1012,6 @@ final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, AS
|
||||
groupBackgroundNode.update(size: groupBackgroundNode.frame.size, theme: theme, wallpaper: wallpaper, graphics: graphics, wallpaperBackgroundNode: self.wallpaperBackgroundNode, transition: itemTransition)
|
||||
}
|
||||
|
||||
var isFirstGroup = true
|
||||
for (item, itemRect, itemPosition) in items {
|
||||
if let identifier = item.uniqueIdentifier, let itemNode = self.itemNodes[identifier] {
|
||||
var corners: CACornerMask = []
|
||||
@ -1039,11 +1045,6 @@ final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, AS
|
||||
}
|
||||
}
|
||||
|
||||
if isFirstGroup {
|
||||
isFirstGroup = false
|
||||
} else {
|
||||
contentHeight += spacing
|
||||
}
|
||||
contentHeight += groupSize.height
|
||||
contentWidth = max(contentWidth, groupSize.width)
|
||||
groupIndex += 1
|
||||
|
@ -1415,7 +1415,7 @@ private func addContactToExisting(context: AccountContext, parentController: Vie
|
||||
(parentController.navigationController as? NavigationController)?.pushViewController(contactsController)
|
||||
let _ = (contactsController.result
|
||||
|> deliverOnMainQueue).start(next: { result in
|
||||
if let (peers, _, _, _, _) = result, let peer = peers.first {
|
||||
if let (peers, _, _, _, _, _) = result, let peer = peers.first {
|
||||
let dataSignal: Signal<(EnginePeer?, DeviceContactStableId?), NoError>
|
||||
switch peer {
|
||||
case let .peer(contact, _, _):
|
||||
|
@ -1175,7 +1175,7 @@ public final class PeerStoryListContext {
|
||||
public var hasCache: Bool
|
||||
public var allEntityFiles: [MediaId: TelegramMediaFile]
|
||||
|
||||
init(
|
||||
public init(
|
||||
peerReference: PeerReference?,
|
||||
items: [EngineStoryItem],
|
||||
pinnedIds: Set<Int32>,
|
||||
|
@ -12,8 +12,8 @@ public func chatMessageBubbleImageContentCorners(relativeContentPosition positio
|
||||
case let .linear(top, _):
|
||||
switch top {
|
||||
case .Neighbour:
|
||||
topLeftCorner = .Corner(mergedWithAnotherContentRadius)
|
||||
topRightCorner = .Corner(mergedWithAnotherContentRadius)
|
||||
topLeftCorner = .Corner(normalRadius)
|
||||
topRightCorner = .Corner(normalRadius)
|
||||
case .BubbleNeighbour:
|
||||
topLeftCorner = .Corner(mergedRadius)
|
||||
topRightCorner = .Corner(mergedRadius)
|
||||
|
@ -248,7 +248,11 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
|
||||
messageWithCaptionToAdd = (message, itemAttributes)
|
||||
skipText = true
|
||||
} else {
|
||||
result.append((message, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: isFile ? .condensed : .default)))
|
||||
if let _ = message.attributes.first(where: { $0 is InvertMediaMessageAttribute }) {
|
||||
result.insert((message, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: isFile ? .condensed : .default)), at: 0)
|
||||
} else {
|
||||
result.append((message, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: isFile ? .condensed : .default)))
|
||||
}
|
||||
needReactions = false
|
||||
}
|
||||
} else {
|
||||
@ -293,8 +297,15 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
|
||||
}
|
||||
|
||||
if let (messageWithCaptionToAdd, itemAttributes) = messageWithCaptionToAdd {
|
||||
result.append((messageWithCaptionToAdd, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
||||
needReactions = false
|
||||
if let _ = messageWithCaptionToAdd.attributes.first(where: { $0 is InvertMediaMessageAttribute }) {
|
||||
if result.isEmpty {
|
||||
needReactions = false
|
||||
}
|
||||
result.insert((messageWithCaptionToAdd, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)), at: 0)
|
||||
} else {
|
||||
result.append((messageWithCaptionToAdd, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
||||
needReactions = false
|
||||
}
|
||||
}
|
||||
|
||||
if let additionalContent = item.additionalContent {
|
||||
@ -2760,7 +2771,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
let contentNodeFrame = framesAndPositions[mosaicIndex].0.offsetBy(dx: 0.0, dy: contentNodesHeight)
|
||||
contentNodeFramesPropertiesAndApply.append((contentNodeFrame, properties, true, apply))
|
||||
|
||||
if mosaicIndex == mosaicRange.upperBound - 1 {
|
||||
if i == mosaicRange.upperBound - 1 {
|
||||
contentNodesHeight += size.height
|
||||
totalContentNodesHeight += size.height
|
||||
|
||||
|
@ -254,6 +254,8 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
let statusType: ChatMessageDateAndStatusType?
|
||||
if case .customChatContents = item.associatedData.subject {
|
||||
statusType = nil
|
||||
} else if item.message.timestamp == 0 {
|
||||
statusType = nil
|
||||
} else {
|
||||
switch position {
|
||||
case .linear(_, .None), .linear(_, .Neighbour(true, _, _)):
|
||||
@ -274,6 +276,7 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
|
||||
var statusSuggestedWidthAndContinue: (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation) -> Void))?
|
||||
let messageEffect = item.message.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects)
|
||||
if let statusType = statusType {
|
||||
var isReplyThread = false
|
||||
if case .replyThread = item.chatLocation {
|
||||
@ -295,7 +298,7 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
reactionPeers: dateReactionsAndPeers.peers,
|
||||
displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser,
|
||||
areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId),
|
||||
messageEffect: item.message.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects),
|
||||
messageEffect: messageEffect,
|
||||
replyCount: dateReplies,
|
||||
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && isReplyThread,
|
||||
hasAutoremove: item.message.isSelfExpiring,
|
||||
@ -447,11 +450,18 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
|
||||
if let forwardInfo = item.message.forwardInfo, forwardInfo.flags.contains(.isImported) {
|
||||
strongSelf.dateAndStatusNode.pressed = {
|
||||
guard let strongSelf = self else {
|
||||
guard let strongSelf = self, let item = strongSelf.item else {
|
||||
return
|
||||
}
|
||||
item.controllerInteraction.displayImportedMessageTooltip(strongSelf.dateAndStatusNode)
|
||||
}
|
||||
} else if messageEffect != nil {
|
||||
strongSelf.dateAndStatusNode.pressed = {
|
||||
guard let strongSelf = self, let item = strongSelf.item else {
|
||||
return
|
||||
}
|
||||
item.controllerInteraction.playMessageEffect(item.message)
|
||||
}
|
||||
} else {
|
||||
strongSelf.dateAndStatusNode.pressed = nil
|
||||
}
|
||||
|
@ -14,6 +14,180 @@ import WallpaperBackgroundNode
|
||||
import AudioWaveform
|
||||
import ChatMessageItemView
|
||||
|
||||
public final class ChatSendContactMessageContextPreview: UIView, ChatSendMessageContextScreenMediaPreview {
|
||||
private let context: AccountContext
|
||||
private let presentationData: PresentationData
|
||||
private let wallpaperBackgroundNode: WallpaperBackgroundNode?
|
||||
private let contactPeers: [ContactListPeer]
|
||||
|
||||
private var messageNodes: [ListViewItemNode]?
|
||||
private let messagesContainer: UIView
|
||||
|
||||
public var isReady: Signal<Bool, NoError> {
|
||||
return .single(true)
|
||||
}
|
||||
|
||||
public var view: UIView {
|
||||
return self
|
||||
}
|
||||
|
||||
public var globalClippingRect: CGRect? {
|
||||
return nil
|
||||
}
|
||||
|
||||
public var layoutType: ChatSendMessageContextScreenMediaPreviewLayoutType {
|
||||
return .message
|
||||
}
|
||||
|
||||
public init(context: AccountContext, presentationData: PresentationData, wallpaperBackgroundNode: WallpaperBackgroundNode?, contactPeers: [ContactListPeer]) {
|
||||
self.context = context
|
||||
self.presentationData = presentationData
|
||||
self.wallpaperBackgroundNode = wallpaperBackgroundNode
|
||||
self.contactPeers = contactPeers
|
||||
|
||||
self.messagesContainer = UIView()
|
||||
self.messagesContainer.layer.sublayerTransform = CATransform3DMakeScale(-1.0, -1.0, 1.0)
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.addSubview(self.messagesContainer)
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
}
|
||||
|
||||
public func animateIn(transition: Transition) {
|
||||
transition.animateAlpha(view: self.messagesContainer, from: 0.0, to: 1.0)
|
||||
transition.animateScale(view: self.messagesContainer, from: 0.001, to: 1.0)
|
||||
}
|
||||
|
||||
public func animateOut(transition: Transition) {
|
||||
transition.setAlpha(view: self.messagesContainer, alpha: 0.0)
|
||||
transition.setScale(view: self.messagesContainer, scale: 0.001)
|
||||
}
|
||||
|
||||
public func animateOutOnSend(transition: Transition) {
|
||||
transition.setAlpha(view: self.messagesContainer, alpha: 0.0)
|
||||
}
|
||||
|
||||
public func update(containerSize: CGSize, transition: Transition) -> CGSize {
|
||||
var contactsMedia: [TelegramMediaContact] = []
|
||||
for peer in self.contactPeers {
|
||||
switch peer {
|
||||
case let .peer(contact, _, _):
|
||||
guard let contact = contact as? TelegramUser, let phoneNumber = contact.phone else {
|
||||
continue
|
||||
}
|
||||
let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: contact.firstName ?? "", lastName: contact.lastName ?? "", phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!<Mobile>!$_", value: phoneNumber)]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
|
||||
|
||||
let phone = contactData.basicData.phoneNumbers[0].value
|
||||
contactsMedia.append(TelegramMediaContact(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumber: phone, peerId: contact.id, vCardData: nil))
|
||||
case let .deviceContact(_, basicData):
|
||||
guard !basicData.phoneNumbers.isEmpty else {
|
||||
continue
|
||||
}
|
||||
let contactData = DeviceContactExtendedData(basicData: basicData, middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
|
||||
|
||||
let phone = contactData.basicData.phoneNumbers[0].value
|
||||
contactsMedia.append(TelegramMediaContact(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumber: phone, peerId: nil, vCardData: nil))
|
||||
}
|
||||
}
|
||||
|
||||
var items: [ListViewItem] = []
|
||||
for contactMedia in contactsMedia {
|
||||
let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: self.context.account.peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: [contactMedia], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
|
||||
|
||||
let item = self.context.sharedContext.makeChatMessagePreviewItem(
|
||||
context: self.context,
|
||||
messages: [message],
|
||||
theme: presentationData.theme,
|
||||
strings: presentationData.strings,
|
||||
wallpaper: presentationData.chatWallpaper,
|
||||
fontSize: presentationData.chatFontSize,
|
||||
chatBubbleCorners: presentationData.chatBubbleCorners,
|
||||
dateTimeFormat: presentationData.dateTimeFormat,
|
||||
nameOrder: presentationData.nameDisplayOrder,
|
||||
forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .fetchStatus(.Local), fetchStatus: .Local),
|
||||
tapMessage: nil,
|
||||
clickThroughMessage: nil,
|
||||
backgroundNode: self.wallpaperBackgroundNode,
|
||||
availableReactions: nil,
|
||||
accountPeer: nil,
|
||||
isCentered: false,
|
||||
isPreview: true,
|
||||
isStandalone: true
|
||||
)
|
||||
items.append(item)
|
||||
}
|
||||
|
||||
let params = ListViewItemLayoutParams(width: containerSize.width, leftInset: 0.0, rightInset: 0.0, availableHeight: containerSize.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: itemNode.frame.minX, y: itemNode.frame.minY), size: CGSize(width: containerSize.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: false, 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!.isUserInteractionEnabled = false
|
||||
messageNodes.append(itemNode!)
|
||||
self.messagesContainer.addSubview(itemNode!.view)
|
||||
}
|
||||
self.messageNodes = messageNodes
|
||||
}
|
||||
|
||||
var contentSize = CGSize()
|
||||
for messageNode in self.messageNodes ?? [] {
|
||||
guard let messageNode = messageNode as? ChatMessageItemView else {
|
||||
continue
|
||||
}
|
||||
if !contentSize.height.isZero {
|
||||
contentSize.height += 2.0
|
||||
}
|
||||
let contentFrame = messageNode.contentFrame()
|
||||
contentSize.height += contentFrame.height
|
||||
contentSize.width = max(contentSize.width, contentFrame.width)
|
||||
}
|
||||
|
||||
var contentOffsetY: CGFloat = 0.0
|
||||
for messageNode in self.messageNodes ?? [] {
|
||||
guard let messageNode = messageNode as? ChatMessageItemView else {
|
||||
continue
|
||||
}
|
||||
if !contentOffsetY.isZero {
|
||||
contentOffsetY += 2.0
|
||||
}
|
||||
let contentFrame = messageNode.contentFrame()
|
||||
messageNode.frame = CGRect(origin: CGPoint(x: contentFrame.minX + contentSize.width - contentFrame.width + 6.0, y: 3.0 + contentOffsetY), size: CGSize(width: contentFrame.width, height: contentFrame.height))
|
||||
contentOffsetY += contentFrame.height
|
||||
}
|
||||
|
||||
self.messagesContainer.frame = CGRect(origin: CGPoint(x: 6.0, y: 3.0), size: CGSize(width: contentSize.width, height: contentSize.height))
|
||||
|
||||
return CGSize(width: contentSize.width - 4.0, height: contentSize.height + 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
public final class ChatSendAudioMessageContextPreview: UIView, ChatSendMessageContextScreenMediaPreview {
|
||||
private let context: AccountContext
|
||||
private let presentationData: PresentationData
|
||||
|
@ -419,7 +419,7 @@ private final class PeerInfoPendingPane {
|
||||
}
|
||||
}
|
||||
|
||||
let visualPaneNode = PeerInfoStoryPaneNode(context: context, peerId: peerId, chatLocation: chatLocation, contentType: .photoOrVideo, captureProtected: captureProtected, isSaved: false, isArchive: key == .storyArchive, isProfileEmbedded: true, canManageStories: canManage, navigationController: chatControllerInteraction.navigationController, listContext: key == .storyArchive ? data.storyArchiveListContext : data.storyListContext)
|
||||
let visualPaneNode = PeerInfoStoryPaneNode(context: context, peerId: peerId, contentType: .photoOrVideo, captureProtected: captureProtected, isSaved: false, isArchive: key == .storyArchive, isProfileEmbedded: true, canManageStories: canManage, navigationController: chatControllerInteraction.navigationController, listContext: key == .storyArchive ? data.storyArchiveListContext : data.storyListContext)
|
||||
paneNode = visualPaneNode
|
||||
visualPaneNode.openCurrentDate = {
|
||||
openMediaCalendar()
|
||||
|
@ -456,7 +456,6 @@ final class PeerInfoStoryGridScreenComponent: Component {
|
||||
paneNode = PeerInfoStoryPaneNode(
|
||||
context: component.context,
|
||||
peerId: component.peerId,
|
||||
chatLocation: .peer(id: component.peerId),
|
||||
contentType: .photoOrVideo,
|
||||
captureProtected: false,
|
||||
isSaved: true,
|
||||
|
@ -0,0 +1,261 @@
|
||||
import Foundation
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
import ComponentFlow
|
||||
import TelegramCore
|
||||
import PeerInfoVisualMediaPaneNode
|
||||
import ViewControllerComponent
|
||||
import ChatListHeaderComponent
|
||||
import ContextUI
|
||||
import ChatTitleView
|
||||
import BottomButtonPanelComponent
|
||||
import UndoUI
|
||||
import MoreHeaderButton
|
||||
import MediaEditorScreen
|
||||
import SaveToCameraRoll
|
||||
|
||||
final class StorySearchGridScreenComponent: Component {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let searchQuery: String
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
searchQuery: String
|
||||
) {
|
||||
self.context = context
|
||||
self.searchQuery = searchQuery
|
||||
}
|
||||
|
||||
static func ==(lhs: StorySearchGridScreenComponent, rhs: StorySearchGridScreenComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.searchQuery != rhs.searchQuery {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private var component: StorySearchGridScreenComponent?
|
||||
private(set) weak var state: EmptyComponentState?
|
||||
private var environment: EnvironmentType?
|
||||
|
||||
private(set) var paneNode: PeerInfoStoryPaneNode?
|
||||
private var paneStatusDisposable: Disposable?
|
||||
private(set) var paneStatusText: String?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.paneStatusDisposable?.dispose()
|
||||
}
|
||||
|
||||
func scrollToTop() {
|
||||
guard let paneNode = self.paneNode else {
|
||||
return
|
||||
}
|
||||
let _ = paneNode.scrollToTop()
|
||||
}
|
||||
|
||||
private var isUpdating = false
|
||||
func update(component: StorySearchGridScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
|
||||
self.isUpdating = true
|
||||
defer {
|
||||
self.isUpdating = false
|
||||
}
|
||||
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let sideInset: CGFloat = 14.0
|
||||
let _ = sideInset
|
||||
|
||||
let environment = environment[EnvironmentType.self].value
|
||||
|
||||
let themeUpdated = self.environment?.theme !== environment.theme
|
||||
|
||||
self.environment = environment
|
||||
|
||||
if themeUpdated {
|
||||
self.backgroundColor = environment.theme.list.plainBackgroundColor
|
||||
}
|
||||
|
||||
let bottomInset: CGFloat = environment.safeInsets.bottom
|
||||
|
||||
let paneNode: PeerInfoStoryPaneNode
|
||||
if let current = self.paneNode {
|
||||
paneNode = current
|
||||
} else {
|
||||
paneNode = PeerInfoStoryPaneNode(
|
||||
context: component.context,
|
||||
peerId: nil,
|
||||
searchQuery: component.searchQuery,
|
||||
contentType: .photoOrVideo,
|
||||
captureProtected: false,
|
||||
isSaved: false,
|
||||
isArchive: false,
|
||||
isProfileEmbedded: false,
|
||||
canManageStories: false,
|
||||
navigationController: { [weak self] in
|
||||
guard let self else {
|
||||
return nil
|
||||
}
|
||||
return self.environment?.controller()?.navigationController as? NavigationController
|
||||
},
|
||||
listContext: nil
|
||||
)
|
||||
paneNode.isEmptyUpdated = { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if !self.isUpdating {
|
||||
self.state?.updated(transition: .immediate)
|
||||
}
|
||||
}
|
||||
self.paneNode = paneNode
|
||||
self.addSubview(paneNode.view)
|
||||
|
||||
self.paneStatusDisposable = (paneNode.status
|
||||
|> deliverOnMainQueue).start(next: { [weak self] status in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if self.paneStatusText != status?.text {
|
||||
self.paneStatusText = status?.text
|
||||
(self.environment?.controller() as? StorySearchGridScreen)?.updateTitle()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
paneNode.update(
|
||||
size: availableSize,
|
||||
topInset: environment.navigationHeight,
|
||||
sideInset: environment.safeInsets.left,
|
||||
bottomInset: bottomInset,
|
||||
deviceMetrics: environment.deviceMetrics,
|
||||
visibleHeight: availableSize.height,
|
||||
isScrollingLockedAtTop: false,
|
||||
expandProgress: 1.0,
|
||||
navigationHeight: 0.0,
|
||||
presentationData: component.context.sharedContext.currentPresentationData.with({ $0 }),
|
||||
synchronous: false,
|
||||
transition: transition.containedViewLayoutTransition
|
||||
)
|
||||
transition.setFrame(view: paneNode.view, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||
|
||||
return availableSize
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View()
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
public class StorySearchGridScreen: ViewControllerComponentContainer {
|
||||
private let context: AccountContext
|
||||
private let searchQuery: String
|
||||
private var isDismissed: Bool = false
|
||||
|
||||
private var titleView: ChatTitleView?
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
searchQuery: String
|
||||
) {
|
||||
self.context = context
|
||||
self.searchQuery = searchQuery
|
||||
|
||||
super.init(context: context, component: StorySearchGridScreenComponent(
|
||||
context: context,
|
||||
searchQuery: searchQuery
|
||||
), navigationBarAppearance: .default, theme: .default)
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with({ $0 })
|
||||
|
||||
self.titleView = ChatTitleView(
|
||||
context: context, theme:
|
||||
presentationData.theme,
|
||||
strings: presentationData.strings,
|
||||
dateTimeFormat: presentationData.dateTimeFormat,
|
||||
nameDisplayOrder: presentationData.nameDisplayOrder,
|
||||
animationCache: context.animationCache,
|
||||
animationRenderer: context.animationRenderer
|
||||
)
|
||||
self.titleView?.disableAnimations = true
|
||||
|
||||
self.navigationItem.titleView = self.titleView
|
||||
|
||||
self.updateTitle()
|
||||
|
||||
self.scrollToTop = { [weak self] in
|
||||
guard let self, let componentView = self.node.hostView.componentView as? StorySearchGridScreenComponent.View else {
|
||||
return
|
||||
}
|
||||
componentView.scrollToTop()
|
||||
}
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
}
|
||||
|
||||
func updateTitle() {
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let _ = presentationData
|
||||
|
||||
guard let componentView = self.node.hostView.componentView as? StorySearchGridScreenComponent.View, let paneNode = componentView.paneNode else {
|
||||
return
|
||||
}
|
||||
let _ = paneNode
|
||||
|
||||
let title: String?
|
||||
if let paneStatusText = componentView.paneStatusText, !paneStatusText.isEmpty {
|
||||
title = paneStatusText
|
||||
} else {
|
||||
title = nil
|
||||
}
|
||||
//TODO:localize
|
||||
self.titleView?.titleContent = .custom("\(self.searchQuery)", title, false)
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
self.titleView?.layout = layout
|
||||
}
|
||||
}
|
||||
|
||||
private final class PeerInfoContextReferenceContentSource: ContextReferenceContentSource {
|
||||
private let controller: ViewController
|
||||
private let sourceNode: ContextReferenceContentNode
|
||||
|
||||
init(controller: ViewController, sourceNode: ContextReferenceContentNode) {
|
||||
self.controller = controller
|
||||
self.sourceNode = sourceNode
|
||||
}
|
||||
|
||||
func transitionInfo() -> ContextControllerReferenceViewInfo? {
|
||||
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -19,7 +19,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
||||
private var customTitle: String?
|
||||
|
||||
public var peerSelected: ((EnginePeer, Int64?) -> Void)?
|
||||
public var multiplePeersSelected: (([EnginePeer], [EnginePeer.Id: EnginePeer], NSAttributedString, AttachmentTextInputPanelSendMode, ChatInterfaceForwardOptionsState?, ChatSendMessageActionSheetController.MessageEffect?) -> Void)?
|
||||
public var multiplePeersSelected: (([EnginePeer], [EnginePeer.Id: EnginePeer], NSAttributedString, AttachmentTextInputPanelSendMode, ChatInterfaceForwardOptionsState?, ChatSendMessageActionSheetController.SendParameters?) -> Void)?
|
||||
private let filter: ChatListNodePeersFilter
|
||||
private let forumPeerId: EnginePeer.Id?
|
||||
private let selectForumThreads: Bool
|
||||
|
@ -83,7 +83,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
var requestOpenDisabledPeer: ((EnginePeer, Int64?, ChatListDisabledPeerReason) -> Void)?
|
||||
var requestOpenPeerFromSearch: ((EnginePeer, Int64?) -> Void)?
|
||||
var requestOpenMessageFromSearch: ((EnginePeer, Int64?, EngineMessage.Id) -> Void)?
|
||||
var requestSend: (([EnginePeer], [EnginePeer.Id: EnginePeer], NSAttributedString, AttachmentTextInputPanelSendMode, ChatInterfaceForwardOptionsState?, ChatSendMessageActionSheetController.MessageEffect?) -> Void)?
|
||||
var requestSend: (([EnginePeer], [EnginePeer.Id: EnginePeer], NSAttributedString, AttachmentTextInputPanelSendMode, ChatInterfaceForwardOptionsState?, ChatSendMessageActionSheetController.SendParameters?) -> Void)?
|
||||
|
||||
private var presentationData: PresentationData {
|
||||
didSet {
|
||||
|
@ -34,6 +34,17 @@ private final class PremiumGiftContext: AttachmentMediaPickerContext {
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
var hasCaption: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
var captionIsAboveMedia: Signal<Bool, NoError> {
|
||||
return .single(false)
|
||||
}
|
||||
|
||||
func setCaptionIsAboveMedia(_ captionIsAboveMedia: Bool) -> Void {
|
||||
}
|
||||
|
||||
public var loadingProgress: Signal<CGFloat?, NoError> {
|
||||
return .single(nil)
|
||||
}
|
||||
@ -49,10 +60,10 @@ private final class PremiumGiftContext: AttachmentMediaPickerContext {
|
||||
func setCaption(_ caption: NSAttributedString) {
|
||||
}
|
||||
|
||||
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, messageEffect: ChatSendMessageActionSheetController.MessageEffect?) {
|
||||
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, parameters: ChatSendMessageActionSheetController.SendParameters?) {
|
||||
}
|
||||
|
||||
func schedule(messageEffect: ChatSendMessageActionSheetController.MessageEffect?) {
|
||||
func schedule(parameters: ChatSendMessageActionSheetController.SendParameters?) {
|
||||
}
|
||||
|
||||
func mainButtonAction() {
|
||||
|
@ -362,6 +362,17 @@ private final class ThemeColorsGridContext: AttachmentMediaPickerContext {
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
var hasCaption: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
var captionIsAboveMedia: Signal<Bool, NoError> {
|
||||
return .single(false)
|
||||
}
|
||||
|
||||
func setCaptionIsAboveMedia(_ captionIsAboveMedia: Bool) -> Void {
|
||||
}
|
||||
|
||||
public var loadingProgress: Signal<CGFloat?, NoError> {
|
||||
return .single(nil)
|
||||
}
|
||||
@ -377,10 +388,10 @@ private final class ThemeColorsGridContext: AttachmentMediaPickerContext {
|
||||
func setCaption(_ caption: NSAttributedString) {
|
||||
}
|
||||
|
||||
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, messageEffect: ChatSendMessageActionSheetController.MessageEffect?) {
|
||||
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, parameters: ChatSendMessageActionSheetController.SendParameters?) {
|
||||
}
|
||||
|
||||
func schedule(messageEffect: ChatSendMessageActionSheetController.MessageEffect?) {
|
||||
func schedule(parameters: ChatSendMessageActionSheetController.SendParameters?) {
|
||||
}
|
||||
|
||||
func mainButtonAction() {
|
||||
|
@ -1556,14 +1556,14 @@ final class StoryItemSetContainerSendMessage {
|
||||
completion(controller, mediaPickerContext)
|
||||
}, updateMediaPickerContext: { [weak attachmentController] mediaPickerContext in
|
||||
attachmentController?.mediaPickerContext = mediaPickerContext
|
||||
}, completion: { [weak self, weak view] signals, silentPosting, scheduleTime, messageEffect, getAnimatedTransitionSource, completion in
|
||||
}, completion: { [weak self, weak view] signals, silentPosting, scheduleTime, parameters, getAnimatedTransitionSource, completion in
|
||||
guard let self, let view else {
|
||||
return
|
||||
}
|
||||
if !inputText.string.isEmpty {
|
||||
self.clearInputText(view: view)
|
||||
}
|
||||
self.enqueueMediaMessages(view: view, peer: peer, replyToMessageId: nil, replyToStoryId: focusedStoryId, signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, messageEffect: messageEffect, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion)
|
||||
self.enqueueMediaMessages(view: view, peer: peer, replyToMessageId: nil, replyToStoryId: focusedStoryId, signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, parameters: parameters, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion)
|
||||
}
|
||||
)
|
||||
case .file:
|
||||
@ -1658,7 +1658,7 @@ final class StoryItemSetContainerSendMessage {
|
||||
}
|
||||
self.controllerNavigationDisposable.set((contactsController.result
|
||||
|> deliverOnMainQueue).start(next: { [weak self, weak view] peers in
|
||||
guard let self, let view, let (peers, _, silent, scheduleTime, text) = peers else {
|
||||
guard let self, let view, let (peers, _, silent, scheduleTime, text, _) = peers else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -1875,7 +1875,7 @@ final class StoryItemSetContainerSendMessage {
|
||||
bannedSendVideos: (Int32, Bool)?,
|
||||
present: @escaping (MediaPickerScreen, AttachmentMediaPickerContext?) -> Void,
|
||||
updateMediaPickerContext: @escaping (AttachmentMediaPickerContext?) -> Void,
|
||||
completion: @escaping ([Any], Bool, Int32?, ChatSendMessageActionSheetController.MessageEffect?, @escaping (String) -> UIView?, @escaping () -> Void) -> Void
|
||||
completion: @escaping ([Any], Bool, Int32?, ChatSendMessageActionSheetController.SendParameters?, @escaping (String) -> UIView?, @escaping () -> Void) -> Void
|
||||
) {
|
||||
guard let component = view.component else {
|
||||
return
|
||||
@ -2240,14 +2240,14 @@ final class StoryItemSetContainerSendMessage {
|
||||
present(controller, mediaPickerContext)
|
||||
},
|
||||
updateMediaPickerContext: { _ in },
|
||||
completion: { [weak self, weak view] signals, silentPosting, scheduleTime, messageEffect, getAnimatedTransitionSource, completion in
|
||||
completion: { [weak self, weak view] signals, silentPosting, scheduleTime, parameters, getAnimatedTransitionSource, completion in
|
||||
guard let self, let view else {
|
||||
return
|
||||
}
|
||||
if !inputText.string.isEmpty {
|
||||
self.clearInputText(view: view)
|
||||
}
|
||||
self.enqueueMediaMessages(view: view, peer: peer, replyToMessageId: nil, replyToStoryId: focusedStoryId, signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, messageEffect: messageEffect, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion)
|
||||
self.enqueueMediaMessages(view: view, peer: peer, replyToMessageId: nil, replyToStoryId: focusedStoryId, signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, parameters: parameters, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion)
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -2569,7 +2569,7 @@ final class StoryItemSetContainerSendMessage {
|
||||
}
|
||||
}
|
||||
|
||||
private func enqueueMediaMessages(view: StoryItemSetContainerComponent.View, peer: EnginePeer, replyToMessageId: EngineMessage.Id?, replyToStoryId: StoryId?, signals: [Any]?, silentPosting: Bool, scheduleTime: Int32? = nil, messageEffect: ChatSendMessageActionSheetController.MessageEffect? = nil, getAnimatedTransitionSource: ((String) -> UIView?)? = nil, completion: @escaping () -> Void = {}) {
|
||||
private func enqueueMediaMessages(view: StoryItemSetContainerComponent.View, peer: EnginePeer, replyToMessageId: EngineMessage.Id?, replyToStoryId: StoryId?, signals: [Any]?, silentPosting: Bool, scheduleTime: Int32? = nil, parameters: ChatSendMessageActionSheetController.SendParameters? = nil, getAnimatedTransitionSource: ((String) -> UIView?)? = nil, completion: @escaping () -> Void = {}) {
|
||||
guard let component = view.component else {
|
||||
return
|
||||
}
|
||||
@ -2620,11 +2620,20 @@ final class StoryItemSetContainerSendMessage {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let messageEffect {
|
||||
message = message.withUpdatedAttributes { attributes in
|
||||
var attributes = attributes
|
||||
attributes.append(EffectMessageAttribute(id: messageEffect.id))
|
||||
return attributes
|
||||
if let parameters {
|
||||
if let effect = parameters.effect {
|
||||
message = message.withUpdatedAttributes { attributes in
|
||||
var attributes = attributes
|
||||
attributes.append(EffectMessageAttribute(id: effect.id))
|
||||
return attributes
|
||||
}
|
||||
}
|
||||
if parameters.textIsAboveMedia {
|
||||
message = message.withUpdatedAttributes { attributes in
|
||||
var attributes = attributes
|
||||
attributes.append(InvertMediaMessageAttribute())
|
||||
return attributes
|
||||
}
|
||||
}
|
||||
}
|
||||
mappedMessages.append(message)
|
||||
@ -2909,8 +2918,13 @@ final class StoryItemSetContainerSendMessage {
|
||||
return
|
||||
}
|
||||
if !hashtag.isEmpty {
|
||||
let searchController = component.context.sharedContext.makeHashtagSearchController(context: component.context, peer: peer.flatMap(EnginePeer.init), query: hashtag, all: true)
|
||||
navigationController.pushViewController(searchController)
|
||||
if "".isEmpty {
|
||||
let searchController = component.context.sharedContext.makeStorySearchController(context: component.context, query: hashtag)
|
||||
navigationController.pushViewController(searchController)
|
||||
} else {
|
||||
let searchController = component.context.sharedContext.makeHashtagSearchController(context: component.context, peer: peer.flatMap(EnginePeer.init), query: hashtag, all: true)
|
||||
navigationController.pushViewController(searchController)
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
@ -172,6 +172,17 @@ private final class AttachmentFileContext: AttachmentMediaPickerContext {
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
var hasCaption: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
var captionIsAboveMedia: Signal<Bool, NoError> {
|
||||
return .single(false)
|
||||
}
|
||||
|
||||
func setCaptionIsAboveMedia(_ captionIsAboveMedia: Bool) -> Void {
|
||||
}
|
||||
|
||||
public var loadingProgress: Signal<CGFloat?, NoError> {
|
||||
return .single(nil)
|
||||
}
|
||||
@ -183,10 +194,10 @@ private final class AttachmentFileContext: AttachmentMediaPickerContext {
|
||||
func setCaption(_ caption: NSAttributedString) {
|
||||
}
|
||||
|
||||
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, messageEffect: ChatSendMessageActionSheetController.MessageEffect?) {
|
||||
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, parameters: ChatSendMessageActionSheetController.SendParameters?) {
|
||||
}
|
||||
|
||||
func schedule(messageEffect: ChatSendMessageActionSheetController.MessageEffect?) {
|
||||
func schedule(parameters: ChatSendMessageActionSheetController.SendParameters?) {
|
||||
}
|
||||
|
||||
func mainButtonAction() {
|
||||
|
@ -27,8 +27,8 @@ extension ChatControllerImpl {
|
||||
subjects: subjects,
|
||||
presentMediaPicker: { [weak self] subject, saveEditedPhotos, bannedSendPhotos, bannedSendVideos, present in
|
||||
if let strongSelf = self {
|
||||
strongSelf.presentMediaPicker(subject: subject, saveEditedPhotos: saveEditedPhotos, bannedSendPhotos: bannedSendPhotos, bannedSendVideos: bannedSendVideos, present: present, updateMediaPickerContext: { _ in }, completion: { [weak self] signals, silentPosting, scheduleTime, messageEffect, getAnimatedTransitionSource, completion in
|
||||
self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, messageEffect: messageEffect, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion)
|
||||
strongSelf.presentMediaPicker(subject: subject, saveEditedPhotos: saveEditedPhotos, bannedSendPhotos: bannedSendPhotos, bannedSendVideos: bannedSendVideos, present: present, updateMediaPickerContext: { _ in }, completion: { [weak self] signals, silentPosting, scheduleTime, parameters, getAnimatedTransitionSource, completion in
|
||||
self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, parameters: parameters, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
@ -15,7 +15,7 @@ import ChatControllerInteraction
|
||||
import ChatSendAudioMessageContextPreview
|
||||
|
||||
extension ChatSendMessageEffect {
|
||||
convenience init(_ effect: ChatSendMessageActionSheetController.MessageEffect) {
|
||||
convenience init(_ effect: ChatSendMessageActionSheetController.SendParameters.Effect) {
|
||||
self.init(id: effect.id)
|
||||
}
|
||||
}
|
||||
@ -114,17 +114,17 @@ func chatMessageDisplaySendMessageOptions(selfController: ChatControllerImpl, no
|
||||
}
|
||||
selfController.supportedOrientations = previousSupportedOrientations
|
||||
},
|
||||
sendMessage: { [weak selfController] mode, messageEffect in
|
||||
sendMessage: { [weak selfController] mode, parameters in
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
switch mode {
|
||||
case .generic:
|
||||
selfController.controllerInteraction?.sendCurrentMessage(false, messageEffect.flatMap(ChatSendMessageEffect.init))
|
||||
selfController.controllerInteraction?.sendCurrentMessage(false, parameters?.effect.flatMap(ChatSendMessageEffect.init))
|
||||
case .silently:
|
||||
selfController.controllerInteraction?.sendCurrentMessage(true, messageEffect.flatMap(ChatSendMessageEffect.init))
|
||||
selfController.controllerInteraction?.sendCurrentMessage(true, parameters?.effect.flatMap(ChatSendMessageEffect.init))
|
||||
case .whenOnline:
|
||||
selfController.chatDisplayNode.sendCurrentMessage(scheduleTime: scheduleWhenOnlineTimestamp, messageEffect: messageEffect.flatMap(ChatSendMessageEffect.init)) { [weak selfController] in
|
||||
selfController.chatDisplayNode.sendCurrentMessage(scheduleTime: scheduleWhenOnlineTimestamp, messageEffect: parameters?.effect.flatMap(ChatSendMessageEffect.init)) { [weak selfController] in
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
@ -135,7 +135,7 @@ func chatMessageDisplaySendMessageOptions(selfController: ChatControllerImpl, no
|
||||
}
|
||||
}
|
||||
},
|
||||
schedule: { [weak selfController] messageEffect in
|
||||
schedule: { [weak selfController] effect in
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
|
@ -9096,7 +9096,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
func enqueueMediaMessages(signals: [Any]?, silentPosting: Bool, scheduleTime: Int32? = nil, messageEffect: ChatSendMessageActionSheetController.MessageEffect? = nil, getAnimatedTransitionSource: ((String) -> UIView?)? = nil, completion: @escaping () -> Void = {}) {
|
||||
func enqueueMediaMessages(signals: [Any]?, silentPosting: Bool, scheduleTime: Int32? = nil, parameters: ChatSendMessageActionSheetController.SendParameters? = nil, getAnimatedTransitionSource: ((String) -> UIView?)? = nil, completion: @escaping () -> Void = {}) {
|
||||
self.enqueueMediaMessageDisposable.set((legacyAssetPickerEnqueueMessages(context: self.context, account: self.context.account, signals: signals!)
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] items in
|
||||
if let strongSelf = self {
|
||||
@ -9151,11 +9151,20 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
completionImpl = nil
|
||||
}
|
||||
|
||||
if let messageEffect {
|
||||
message = message.withUpdatedAttributes { attributes in
|
||||
var attributes = attributes
|
||||
attributes.append(EffectMessageAttribute(id: messageEffect.id))
|
||||
return attributes
|
||||
if let parameters {
|
||||
if let effect = parameters.effect {
|
||||
message = message.withUpdatedAttributes { attributes in
|
||||
var attributes = attributes
|
||||
attributes.append(EffectMessageAttribute(id: effect.id))
|
||||
return attributes
|
||||
}
|
||||
}
|
||||
if parameters.textIsAboveMedia {
|
||||
message = message.withUpdatedAttributes { attributes in
|
||||
var attributes = attributes
|
||||
attributes.append(InvertMediaMessageAttribute())
|
||||
return attributes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -309,11 +309,11 @@ extension ChatControllerImpl {
|
||||
completion(controller, mediaPickerContext)
|
||||
}, updateMediaPickerContext: { [weak attachmentController] mediaPickerContext in
|
||||
attachmentController?.mediaPickerContext = mediaPickerContext
|
||||
}, completion: { [weak self] signals, silentPosting, scheduleTime, messageEffect, getAnimatedTransitionSource, completion in
|
||||
}, completion: { [weak self] signals, silentPosting, scheduleTime, parameters, getAnimatedTransitionSource, completion in
|
||||
if !inputText.string.isEmpty {
|
||||
self?.clearInputText()
|
||||
}
|
||||
self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, messageEffect: messageEffect, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion)
|
||||
self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, parameters: parameters, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion)
|
||||
})
|
||||
case .file:
|
||||
strongSelf.controllerNavigationDisposable.set(nil)
|
||||
@ -405,7 +405,7 @@ extension ChatControllerImpl {
|
||||
completion(contactsController, contactsController.mediaPickerContext)
|
||||
strongSelf.controllerNavigationDisposable.set((contactsController.result
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] peers in
|
||||
if let strongSelf = self, let (peers, _, silent, scheduleTime, text) = peers {
|
||||
if let strongSelf = self, let (peers, _, silent, scheduleTime, text, parameters) = peers {
|
||||
var textEnqueueMessage: EnqueueMessage?
|
||||
if let text = text, text.length > 0 {
|
||||
var attributes: [MessageAttribute] = []
|
||||
@ -456,6 +456,17 @@ extension ChatControllerImpl {
|
||||
enqueueMessages.append(message)
|
||||
}
|
||||
}
|
||||
if !enqueueMessages.isEmpty {
|
||||
enqueueMessages[enqueueMessages.count - 1] = enqueueMessages[enqueueMessages.count - 1].withUpdatedAttributes { attributes in
|
||||
var attributes = attributes
|
||||
if let parameters {
|
||||
if let effect = parameters.effect {
|
||||
attributes.append(EffectMessageAttribute(id: effect.id))
|
||||
}
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
}
|
||||
strongSelf.sendMessages(strongSelf.transformEnqueueMessages(enqueueMessages, silentPosting: silent, scheduleTime: scheduleTime))
|
||||
} else if let peer = peers.first {
|
||||
let dataSignal: Signal<(Peer?, DeviceContactExtendedData?), NoError>
|
||||
@ -471,14 +482,14 @@ extension ChatControllerImpl {
|
||||
|> mapToSignal { basicData -> Signal<(Peer?, DeviceContactExtendedData?), NoError> in
|
||||
var stableId: String?
|
||||
let queryPhoneNumber = formatPhoneNumber(context: context, number: phoneNumber)
|
||||
outer: for (id, data) in basicData {
|
||||
for phoneNumber in data.phoneNumbers {
|
||||
if formatPhoneNumber(context: context, number: phoneNumber.value) == queryPhoneNumber {
|
||||
stableId = id
|
||||
break outer
|
||||
outer: for (id, data) in basicData {
|
||||
for phoneNumber in data.phoneNumbers {
|
||||
if formatPhoneNumber(context: context, number: phoneNumber.value) == queryPhoneNumber {
|
||||
stableId = id
|
||||
break outer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let stableId = stableId {
|
||||
return (context.sharedContext.contactDataManager?.extendedData(stableId: stableId) ?? .single(nil))
|
||||
@ -498,7 +509,7 @@ extension ChatControllerImpl {
|
||||
}
|
||||
}
|
||||
strongSelf.controllerNavigationDisposable.set((dataSignal
|
||||
|> deliverOnMainQueue).startStrict(next: { peerAndContactData in
|
||||
|> deliverOnMainQueue).startStrict(next: { peerAndContactData in
|
||||
if let strongSelf = self, let contactData = peerAndContactData.1, contactData.basicData.phoneNumbers.count != 0 {
|
||||
if contactData.isPrimitive {
|
||||
let phone = contactData.basicData.phoneNumbers[0].value
|
||||
@ -518,7 +529,13 @@ extension ChatControllerImpl {
|
||||
if let textEnqueueMessage = textEnqueueMessage {
|
||||
enqueueMessages.append(textEnqueueMessage)
|
||||
}
|
||||
enqueueMessages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: strongSelf.chatLocation.threadId, replyToMessageId: replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []))
|
||||
var attributes: [MessageAttribute] = []
|
||||
if let parameters {
|
||||
if let effect = parameters.effect {
|
||||
attributes.append(EffectMessageAttribute(id: effect.id))
|
||||
}
|
||||
}
|
||||
enqueueMessages.append(.message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), threadId: strongSelf.chatLocation.threadId, replyToMessageId: replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []))
|
||||
strongSelf.sendMessages(strongSelf.transformEnqueueMessages(enqueueMessages, silentPosting: silent, scheduleTime: scheduleTime))
|
||||
} else {
|
||||
let contactController = strongSelf.context.sharedContext.makeDeviceContactInfoController(context: ShareControllerAppAccountContext(context: strongSelf.context), environment: ShareControllerAppEnvironment(sharedContext: strongSelf.context.sharedContext), subject: .filter(peer: peerAndContactData.0, contactId: nil, contactData: contactData, completion: { peer, contactData in
|
||||
@ -1137,7 +1154,7 @@ extension ChatControllerImpl {
|
||||
self.present(actionSheet, in: .window(.root))
|
||||
}
|
||||
|
||||
func presentMediaPicker(subject: MediaPickerScreen.Subject = .assets(nil, .default), saveEditedPhotos: Bool, bannedSendPhotos: (Int32, Bool)?, bannedSendVideos: (Int32, Bool)?, present: @escaping (MediaPickerScreen, AttachmentMediaPickerContext?) -> Void, updateMediaPickerContext: @escaping (AttachmentMediaPickerContext?) -> Void, completion: @escaping ([Any], Bool, Int32?, ChatSendMessageActionSheetController.MessageEffect?, @escaping (String) -> UIView?, @escaping () -> Void) -> Void) {
|
||||
func presentMediaPicker(subject: MediaPickerScreen.Subject = .assets(nil, .default), saveEditedPhotos: Bool, bannedSendPhotos: (Int32, Bool)?, bannedSendVideos: (Int32, Bool)?, present: @escaping (MediaPickerScreen, AttachmentMediaPickerContext?) -> Void, updateMediaPickerContext: @escaping (AttachmentMediaPickerContext?) -> Void, completion: @escaping ([Any], Bool, Int32?, ChatSendMessageActionSheetController.SendParameters?, @escaping (String) -> UIView?, @escaping () -> Void) -> Void) {
|
||||
var isScheduledMessages = false
|
||||
if case .scheduledMessages = self.presentationInterfaceState.subject {
|
||||
isScheduledMessages = true
|
||||
@ -1211,8 +1228,8 @@ extension ChatControllerImpl {
|
||||
controller.getCaptionPanelView = { [weak self] in
|
||||
return self?.getCaptionPanelView(isFile: false)
|
||||
}
|
||||
controller.legacyCompletion = { signals, silently, scheduleTime, messageEffect, getAnimatedTransitionSource, sendCompletion in
|
||||
completion(signals, silently, scheduleTime, messageEffect, getAnimatedTransitionSource, sendCompletion)
|
||||
controller.legacyCompletion = { signals, silently, scheduleTime, parameters, getAnimatedTransitionSource, sendCompletion in
|
||||
completion(signals, silently, scheduleTime, parameters, getAnimatedTransitionSource, sendCompletion)
|
||||
}
|
||||
present(controller, mediaPickerContext)
|
||||
}
|
||||
@ -1492,7 +1509,7 @@ extension ChatControllerImpl {
|
||||
self.effectiveNavigationController?.pushViewController(contactsController)
|
||||
self.controllerNavigationDisposable.set((contactsController.result
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] peers in
|
||||
if let strongSelf = self, let (peers, _, _, _, _) = peers {
|
||||
if let strongSelf = self, let (peers, _, _, _, _, _) = peers {
|
||||
if peers.count > 1 {
|
||||
var enqueueMessages: [EnqueueMessage] = []
|
||||
for peer in peers {
|
||||
|
@ -158,7 +158,7 @@ public class ComposeControllerImpl: ViewController, ComposeController {
|
||||
strongSelf.createActionDisposable.set((controller.result
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak controller] result in
|
||||
if let strongSelf = self, let (contactPeers, _, _, _, _) = result, case let .peer(peer, _, _) = contactPeers.first {
|
||||
if let strongSelf = self, let (contactPeers, _, _, _, _, _) = result, case let .peer(peer, _, _) = contactPeers.first {
|
||||
controller?.dismissSearch()
|
||||
controller?.displayNavigationActivity = true
|
||||
strongSelf.createActionDisposable.set((strongSelf.context.engine.peers.createSecretChat(peerId: peer.id) |> deliverOnMainQueue).startStrict(next: { peerId in
|
||||
|
@ -12,6 +12,8 @@ import ContactListUI
|
||||
import SearchUI
|
||||
import AttachmentUI
|
||||
import SearchBarNode
|
||||
import ChatSendAudioMessageContextPreview
|
||||
import ChatSendMessageActionUI
|
||||
|
||||
class ContactSelectionControllerImpl: ViewController, ContactSelectionController, PresentableController, AttachmentContainable {
|
||||
private let context: AccountContext
|
||||
@ -46,8 +48,8 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
|
||||
fileprivate var caption: NSAttributedString?
|
||||
|
||||
private let _result = Promise<([ContactListPeer], ContactListAction, Bool, Int32?, NSAttributedString?)?>()
|
||||
var result: Signal<([ContactListPeer], ContactListAction, Bool, Int32?, NSAttributedString?)?, NoError> {
|
||||
private let _result = Promise<([ContactListPeer], ContactListAction, Bool, Int32?, NSAttributedString?, ChatSendMessageActionSheetController.SendParameters?)?>()
|
||||
var result: Signal<([ContactListPeer], ContactListAction, Bool, Int32?, NSAttributedString?, ChatSendMessageActionSheetController.SendParameters?)?, NoError> {
|
||||
return self._result.get()
|
||||
}
|
||||
|
||||
@ -87,6 +89,8 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
var isContainerPanning: () -> Bool = { return false }
|
||||
var isContainerExpanded: () -> Bool = { return false }
|
||||
|
||||
var getCurrentSendMessageContextMediaPreview: (() -> ChatSendMessageContextScreenMediaPreview?)?
|
||||
|
||||
init(_ params: ContactSelectionControllerParams) {
|
||||
self.context = params.context
|
||||
self.autoDismiss = params.autoDismiss
|
||||
@ -145,6 +149,24 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
if params.multipleSelection {
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCompactSearchIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.beginSearch))
|
||||
}
|
||||
|
||||
self.getCurrentSendMessageContextMediaPreview = { [weak self] in
|
||||
guard let self else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let selectedPeers = self.contactsNode.contactListNode.selectedPeers
|
||||
if selectedPeers.isEmpty {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ChatSendContactMessageContextPreview(
|
||||
context: self.context,
|
||||
presentationData: self.presentationData,
|
||||
wallpaperBackgroundNode: nil,
|
||||
contactPeers: selectedPeers
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
required init(coder aDecoder: NSCoder) {
|
||||
@ -235,10 +257,10 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
}
|
||||
}
|
||||
|
||||
self.contactsNode.requestMultipleAction = { [weak self] silent, scheduleTime in
|
||||
self.contactsNode.requestMultipleAction = { [weak self] silent, scheduleTime, parameters in
|
||||
if let strongSelf = self {
|
||||
let selectedPeers = strongSelf.contactsNode.contactListNode.selectedPeers
|
||||
strongSelf._result.set(.single((selectedPeers, .generic, silent, scheduleTime, strongSelf.caption)))
|
||||
strongSelf._result.set(.single((selectedPeers, .generic, silent, scheduleTime, strongSelf.caption, parameters)))
|
||||
if strongSelf.autoDismiss {
|
||||
strongSelf.dismiss()
|
||||
}
|
||||
@ -337,7 +359,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
self.confirmationDisposable.set((self.confirmation(peer) |> deliverOnMainQueue).startStrict(next: { [weak self] value in
|
||||
if let strongSelf = self {
|
||||
if value {
|
||||
strongSelf._result.set(.single(([peer], action, false, nil, nil)))
|
||||
strongSelf._result.set(.single(([peer], action, false, nil, nil, nil)))
|
||||
if strongSelf.autoDismiss {
|
||||
strongSelf.dismiss()
|
||||
}
|
||||
@ -435,6 +457,17 @@ final class ContactsPickerContext: AttachmentMediaPickerContext {
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
var hasCaption: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
var captionIsAboveMedia: Signal<Bool, NoError> {
|
||||
return .single(false)
|
||||
}
|
||||
|
||||
func setCaptionIsAboveMedia(_ captionIsAboveMedia: Bool) -> Void {
|
||||
}
|
||||
|
||||
public var loadingProgress: Signal<CGFloat?, NoError> {
|
||||
return .single(nil)
|
||||
}
|
||||
@ -451,13 +484,13 @@ final class ContactsPickerContext: AttachmentMediaPickerContext {
|
||||
self.controller?.caption = caption
|
||||
}
|
||||
|
||||
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, messageEffect: ChatSendMessageActionSheetController.MessageEffect?) {
|
||||
self.controller?.contactsNode.requestMultipleAction?(mode == .silently, mode == .whenOnline ? scheduleWhenOnlineTimestamp : nil)
|
||||
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, parameters: ChatSendMessageActionSheetController.SendParameters?) {
|
||||
self.controller?.contactsNode.requestMultipleAction?(mode == .silently, mode == .whenOnline ? scheduleWhenOnlineTimestamp : nil, parameters)
|
||||
}
|
||||
|
||||
func schedule(messageEffect: ChatSendMessageActionSheetController.MessageEffect?) {
|
||||
func schedule(parameters: ChatSendMessageActionSheetController.SendParameters?) {
|
||||
self.controller?.presentScheduleTimePicker ({ time in
|
||||
self.controller?.contactsNode.requestMultipleAction?(false, time)
|
||||
self.controller?.contactsNode.requestMultipleAction?(false, time, parameters)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
||||
var requestDeactivateSearch: (() -> Void)?
|
||||
var requestOpenPeerFromSearch: ((ContactListPeer) -> Void)?
|
||||
var requestOpenDisabledPeerFromSearch: ((EnginePeer, ChatListDisabledPeerReason) -> Void)?
|
||||
var requestMultipleAction: ((_ silent: Bool, _ scheduleTime: Int32?) -> Void)?
|
||||
var requestMultipleAction: ((_ silent: Bool, _ scheduleTime: Int32?, _ parameters: ChatSendMessageActionSheetController.SendParameters?) -> Void)?
|
||||
var dismiss: (() -> Void)?
|
||||
var cancelSearch: (() -> Void)?
|
||||
|
||||
@ -110,7 +110,7 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
shareImpl = { [weak self] in
|
||||
self?.requestMultipleAction?(false, nil)
|
||||
self?.requestMultipleAction?(false, nil, nil)
|
||||
}
|
||||
|
||||
contextActionImpl = { [weak self] peer, node, gesture, _ in
|
||||
|
@ -1903,6 +1903,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return HashtagSearchController(context: context, peer: peer, query: query, all: all)
|
||||
}
|
||||
|
||||
public func makeStorySearchController(context: AccountContext, query: String) -> ViewController {
|
||||
return StorySearchGridScreen(context: context, searchQuery: query)
|
||||
}
|
||||
|
||||
public func makeMyStoriesController(context: AccountContext, isArchive: Bool) -> ViewController {
|
||||
return PeerInfoStoryGridScreen(context: context, peerId: context.account.peerId, scope: isArchive ? .archive : .saved)
|
||||
}
|
||||
|
@ -35,14 +35,14 @@ final class WebSearchControllerInteraction {
|
||||
let setSearchQuery: (String) -> Void
|
||||
let deleteRecentQuery: (String) -> Void
|
||||
let toggleSelection: (ChatContextResult, Bool) -> Bool
|
||||
let sendSelected: (ChatContextResult?, Bool, Int32?, ChatSendMessageActionSheetController.MessageEffect?) -> Void
|
||||
let schedule: (ChatSendMessageActionSheetController.MessageEffect?) -> Void
|
||||
let sendSelected: (ChatContextResult?, Bool, Int32?, ChatSendMessageActionSheetController.SendParameters?) -> Void
|
||||
let schedule: (ChatSendMessageActionSheetController.SendParameters?) -> Void
|
||||
let avatarCompleted: (UIImage) -> Void
|
||||
let selectionState: TGMediaSelectionContext?
|
||||
let editingState: TGMediaEditingContext
|
||||
var hiddenMediaId: String?
|
||||
|
||||
init(openResult: @escaping (ChatContextResult) -> Void, setSearchQuery: @escaping (String) -> Void, deleteRecentQuery: @escaping (String) -> Void, toggleSelection: @escaping (ChatContextResult, Bool) -> Bool, sendSelected: @escaping (ChatContextResult?, Bool, Int32?, ChatSendMessageActionSheetController.MessageEffect?) -> Void, schedule: @escaping (ChatSendMessageActionSheetController.MessageEffect?) -> Void, avatarCompleted: @escaping (UIImage) -> Void, selectionState: TGMediaSelectionContext?, editingState: TGMediaEditingContext) {
|
||||
init(openResult: @escaping (ChatContextResult) -> Void, setSearchQuery: @escaping (String) -> Void, deleteRecentQuery: @escaping (String) -> Void, toggleSelection: @escaping (ChatContextResult, Bool) -> Bool, sendSelected: @escaping (ChatContextResult?, Bool, Int32?, ChatSendMessageActionSheetController.SendParameters?) -> Void, schedule: @escaping (ChatSendMessageActionSheetController.SendParameters?) -> Void, avatarCompleted: @escaping (UIImage) -> Void, selectionState: TGMediaSelectionContext?, editingState: TGMediaEditingContext) {
|
||||
self.openResult = openResult
|
||||
self.setSearchQuery = setSearchQuery
|
||||
self.deleteRecentQuery = deleteRecentQuery
|
||||
@ -589,6 +589,17 @@ public class WebSearchPickerContext: AttachmentMediaPickerContext {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var hasCaption: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
public var captionIsAboveMedia: Signal<Bool, NoError> {
|
||||
return .single(false)
|
||||
}
|
||||
|
||||
public func setCaptionIsAboveMedia(_ captionIsAboveMedia: Bool) -> Void {
|
||||
}
|
||||
|
||||
public var loadingProgress: Signal<CGFloat?, NoError> {
|
||||
return .single(nil)
|
||||
@ -606,12 +617,12 @@ public class WebSearchPickerContext: AttachmentMediaPickerContext {
|
||||
self.interaction?.editingState.setForcedCaption(caption, skipUpdate: true)
|
||||
}
|
||||
|
||||
public func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, messageEffect: ChatSendMessageActionSheetController.MessageEffect?) {
|
||||
self.interaction?.sendSelected(nil, mode == .silently, nil, messageEffect)
|
||||
public func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, parameters: ChatSendMessageActionSheetController.SendParameters?) {
|
||||
self.interaction?.sendSelected(nil, mode == .silently, nil, parameters)
|
||||
}
|
||||
|
||||
public func schedule(messageEffect: ChatSendMessageActionSheetController.MessageEffect?) {
|
||||
self.interaction?.schedule(messageEffect)
|
||||
public func schedule(parameters: ChatSendMessageActionSheetController.SendParameters?) {
|
||||
self.interaction?.schedule(parameters)
|
||||
}
|
||||
|
||||
public func mainButtonAction() {
|
||||
|
@ -2075,6 +2075,17 @@ final class WebAppPickerContext: AttachmentMediaPickerContext {
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
var hasCaption: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
var captionIsAboveMedia: Signal<Bool, NoError> {
|
||||
return .single(false)
|
||||
}
|
||||
|
||||
func setCaptionIsAboveMedia(_ captionIsAboveMedia: Bool) -> Void {
|
||||
}
|
||||
|
||||
public var loadingProgress: Signal<CGFloat?, NoError> {
|
||||
return self.controller?.controllerNode.loadingProgressPromise.get() ?? .single(nil)
|
||||
}
|
||||
@ -2090,10 +2101,10 @@ final class WebAppPickerContext: AttachmentMediaPickerContext {
|
||||
func setCaption(_ caption: NSAttributedString) {
|
||||
}
|
||||
|
||||
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, messageEffect: ChatSendMessageActionSheetController.MessageEffect?) {
|
||||
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode, parameters: ChatSendMessageActionSheetController.SendParameters?) {
|
||||
}
|
||||
|
||||
func schedule(messageEffect: ChatSendMessageActionSheetController.MessageEffect?) {
|
||||
func schedule(parameters: ChatSendMessageActionSheetController.SendParameters?) {
|
||||
}
|
||||
|
||||
func mainButtonAction() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user