Message preview improvements

This commit is contained in:
Isaac 2024-05-17 17:10:19 +04:00
parent 3aed18be08
commit 378b7e8ed5
40 changed files with 1459 additions and 478 deletions

View File

@ -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 {

View File

@ -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 }

View File

@ -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 }
}

View File

@ -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

View File

@ -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)
}
}
}

View File

@ -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,

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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() {

View File

@ -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() {

View File

@ -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() {

View File

@ -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

View File

@ -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, _, _):

View File

@ -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>,

View File

@ -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)

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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()

View File

@ -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,

View File

@ -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)
}
}

View File

@ -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

View File

@ -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 {

View File

@ -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() {

View File

@ -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() {

View File

@ -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)
}
}
}))
}

View File

@ -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() {

View File

@ -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)
})
}
},

View File

@ -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
}

View File

@ -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
}
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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)
})
}

View File

@ -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

View File

@ -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)
}

View File

@ -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() {

View File

@ -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() {