mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] Edit message preview
This commit is contained in:
parent
6ff4cd3681
commit
667aa0349a
@ -12298,3 +12298,13 @@ Sorry for the inconvenience.";
|
|||||||
"Stars.Transfer.Balance" = "Balance";
|
"Stars.Transfer.Balance" = "Balance";
|
||||||
|
|
||||||
"Settings.Stars" = "Your Stars";
|
"Settings.Stars" = "Your Stars";
|
||||||
|
|
||||||
|
"Chat.MessageEffectMenu.TitleAddEffect" = "Add an animated effect";
|
||||||
|
"Chat.MessageEffectMenu.SectionMessageEffects" = "Message Effects";
|
||||||
|
"Chat.SendMessageMenu.MoveCaptionUp" = "Move Caption Up";
|
||||||
|
"Chat.SendMessageMenu.MoveCaptionDown" = "Move Caption Down";
|
||||||
|
"Chat.SendMessageMenu.ToastPremiumRequired.Text" = "Subscribe to [Telegram Premium]() to add this animated effect.";
|
||||||
|
|
||||||
|
"BusinessLink.AlertTextLimitText" = "The message text limit is 4096 characters";
|
||||||
|
|
||||||
|
"Chat.SendMessageMenu.EditMessage" = "Edit Message";
|
||||||
|
@ -57,7 +57,7 @@ final class AttachmentTextInputActionButtonsNode: ASDisplayNode, ChatSendMessage
|
|||||||
|
|
||||||
self.sendButton.highligthedChanged = { [weak self] highlighted in
|
self.sendButton.highligthedChanged = { [weak self] highlighted in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if strongSelf.sendButtonHasApplyIcon || !strongSelf.sendButtonLongPressEnabled {
|
if !strongSelf.sendButtonLongPressEnabled {
|
||||||
if highlighted {
|
if highlighted {
|
||||||
strongSelf.sendContainerNode.layer.removeAnimation(forKey: "opacity")
|
strongSelf.sendContainerNode.layer.removeAnimation(forKey: "opacity")
|
||||||
strongSelf.sendContainerNode.alpha = 0.4
|
strongSelf.sendContainerNode.alpha = 0.4
|
||||||
|
@ -1002,21 +1002,24 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
context: strongSelf.context,
|
context: strongSelf.context,
|
||||||
updatedPresentationData: strongSelf.updatedPresentationData,
|
updatedPresentationData: strongSelf.updatedPresentationData,
|
||||||
peerId: strongSelf.presentationInterfaceState.chatLocation.peerId,
|
peerId: strongSelf.presentationInterfaceState.chatLocation.peerId,
|
||||||
forwardMessageIds: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds,
|
params: .sendMessage(SendMessageActionSheetControllerParams.SendMessage(
|
||||||
|
isScheduledMessages: false,
|
||||||
|
mediaPreview: mediaPreview,
|
||||||
|
mediaCaptionIsAbove: (captionIsAboveMedia, { [weak strongSelf] value in
|
||||||
|
guard let strongSelf, let controller = strongSelf.controller, let mediaPickerContext = controller.mediaPickerContext else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mediaPickerContext.setCaptionIsAboveMedia(value)
|
||||||
|
}),
|
||||||
|
attachment: true,
|
||||||
|
canSendWhenOnline: sendWhenOnlineAvailable,
|
||||||
|
forwardMessageIds: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? []
|
||||||
|
)),
|
||||||
hasEntityKeyboard: hasEntityKeyboard,
|
hasEntityKeyboard: hasEntityKeyboard,
|
||||||
gesture: gesture,
|
gesture: gesture,
|
||||||
sourceSendButton: node,
|
sourceSendButton: node,
|
||||||
textInputView: textInputNode.textView,
|
textInputView: textInputNode.textView,
|
||||||
mediaPreview: mediaPreview,
|
|
||||||
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,
|
emojiViewProvider: textInputPanelNode.emojiViewProvider,
|
||||||
attachment: true,
|
|
||||||
canSendWhenOnline: sendWhenOnlineAvailable,
|
|
||||||
completion: {
|
completion: {
|
||||||
},
|
},
|
||||||
sendMessage: { [weak textInputPanelNode] mode, messageEffect in
|
sendMessage: { [weak textInputPanelNode] mode, messageEffect in
|
||||||
|
@ -11,169 +11,57 @@ import TextFormat
|
|||||||
import ReactionSelectionNode
|
import ReactionSelectionNode
|
||||||
import WallpaperBackgroundNode
|
import WallpaperBackgroundNode
|
||||||
|
|
||||||
private final class ChatSendMessageActionSheetControllerImpl: ViewController, ChatSendMessageActionSheetController {
|
public enum SendMessageActionSheetControllerParams {
|
||||||
private var controllerNode: ChatSendMessageActionSheetControllerNode {
|
public final class SendMessage {
|
||||||
return self.displayNode as! ChatSendMessageActionSheetControllerNode
|
public let isScheduledMessages: Bool
|
||||||
}
|
public let mediaPreview: ChatSendMessageContextScreenMediaPreview?
|
||||||
|
public let mediaCaptionIsAbove: (Bool, (Bool) -> Void)?
|
||||||
private let context: AccountContext
|
public let attachment: Bool
|
||||||
|
public let canSendWhenOnline: Bool
|
||||||
private let peerId: EnginePeer.Id?
|
public let forwardMessageIds: [EngineMessage.Id]
|
||||||
private let isScheduledMessages: Bool
|
|
||||||
private let forwardMessageIds: [EngineMessage.Id]?
|
|
||||||
private let hasEntityKeyboard: Bool
|
|
||||||
|
|
||||||
private let gesture: ContextGesture
|
|
||||||
private let sourceSendButton: ASDisplayNode
|
|
||||||
private let textInputView: UITextView
|
|
||||||
private let attachment: Bool
|
|
||||||
private let canSendWhenOnline: Bool
|
|
||||||
private let completion: () -> Void
|
|
||||||
private let sendMessage: (SendMode, SendParameters?) -> Void
|
|
||||||
private let schedule: (SendParameters?) -> Void
|
|
||||||
private let reactionItems: [ReactionItem]?
|
|
||||||
|
|
||||||
private var presentationData: PresentationData
|
|
||||||
private var presentationDataDisposable: Disposable?
|
|
||||||
|
|
||||||
private var didPlayPresentationAnimation = false
|
|
||||||
|
|
||||||
private var validLayout: ContainerViewLayout?
|
|
||||||
|
|
||||||
private let hapticFeedback = HapticFeedback()
|
|
||||||
|
|
||||||
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, SendParameters?) -> Void, schedule: @escaping (SendParameters?) -> Void, reactionItems: [ReactionItem]? = nil) {
|
|
||||||
self.context = context
|
|
||||||
self.peerId = peerId
|
|
||||||
self.isScheduledMessages = isScheduledMessages
|
|
||||||
self.forwardMessageIds = forwardMessageIds
|
|
||||||
self.hasEntityKeyboard = hasEntityKeyboard
|
|
||||||
self.gesture = gesture
|
|
||||||
self.sourceSendButton = sourceSendButton
|
|
||||||
self.textInputView = textInputView
|
|
||||||
self.emojiViewProvider = emojiViewProvider
|
|
||||||
self.attachment = attachment
|
|
||||||
self.canSendWhenOnline = canSendWhenOnline
|
|
||||||
self.completion = completion
|
|
||||||
self.sendMessage = sendMessage
|
|
||||||
self.schedule = schedule
|
|
||||||
self.reactionItems = reactionItems
|
|
||||||
|
|
||||||
self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
public init(
|
||||||
|
isScheduledMessages: Bool,
|
||||||
super.init(navigationBarPresentationData: nil)
|
mediaPreview: ChatSendMessageContextScreenMediaPreview?,
|
||||||
|
mediaCaptionIsAbove: (Bool, (Bool) -> Void)?,
|
||||||
self.blocksBackgroundWhenInOverlay = true
|
attachment: Bool,
|
||||||
|
canSendWhenOnline: Bool,
|
||||||
self.presentationDataDisposable = ((updatedPresentationData?.signal ?? context.sharedContext.presentationData)
|
forwardMessageIds: [EngineMessage.Id]
|
||||||
|> deliverOnMainQueue).startStrict(next: { [weak self] presentationData in
|
) {
|
||||||
if let strongSelf = self {
|
self.isScheduledMessages = isScheduledMessages
|
||||||
strongSelf.presentationData = presentationData
|
self.mediaPreview = mediaPreview
|
||||||
if strongSelf.isNodeLoaded {
|
self.mediaCaptionIsAbove = mediaCaptionIsAbove
|
||||||
strongSelf.controllerNode.updatePresentationData(presentationData)
|
self.attachment = attachment
|
||||||
}
|
self.canSendWhenOnline = canSendWhenOnline
|
||||||
}
|
self.forwardMessageIds = forwardMessageIds
|
||||||
}).strict()
|
|
||||||
|
|
||||||
self.statusBar.statusBarStyle = .Hide
|
|
||||||
self.statusBar.ignoreInCall = true
|
|
||||||
}
|
|
||||||
|
|
||||||
required init(coder aDecoder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
self.presentationDataDisposable?.dispose()
|
|
||||||
}
|
|
||||||
|
|
||||||
override public func loadDisplayNode() {
|
|
||||||
var forwardedCount: Int?
|
|
||||||
if let forwardMessageIds = self.forwardMessageIds, forwardMessageIds.count > 0 {
|
|
||||||
forwardedCount = forwardMessageIds.count
|
|
||||||
}
|
|
||||||
|
|
||||||
var reminders = false
|
|
||||||
var isSecret = false
|
|
||||||
var canSchedule = false
|
|
||||||
if let peerId = self.peerId {
|
|
||||||
reminders = peerId == context.account.peerId
|
|
||||||
isSecret = peerId.namespace == Namespaces.Peer.SecretChat
|
|
||||||
canSchedule = !isSecret
|
|
||||||
}
|
|
||||||
if self.isScheduledMessages {
|
|
||||||
canSchedule = false
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
self?.sendMessage(.generic, nil)
|
|
||||||
self?.dismiss(cancel: false)
|
|
||||||
}, sendSilently: { [weak self] in
|
|
||||||
self?.sendMessage(.silently, nil)
|
|
||||||
self?.dismiss(cancel: false)
|
|
||||||
}, sendWhenOnline: { [weak self] in
|
|
||||||
self?.sendMessage(.whenOnline, nil)
|
|
||||||
self?.dismiss(cancel: false)
|
|
||||||
}, schedule: !canSchedule ? nil : { [weak self] in
|
|
||||||
self?.schedule(nil)
|
|
||||||
self?.dismiss(cancel: false)
|
|
||||||
}, cancel: { [weak self] in
|
|
||||||
self?.dismiss(cancel: true)
|
|
||||||
}, reactionItems: self.reactionItems)
|
|
||||||
self.displayNodeDidLoad()
|
|
||||||
}
|
|
||||||
|
|
||||||
override public func viewDidAppear(_ animated: Bool) {
|
|
||||||
super.viewDidAppear(animated)
|
|
||||||
|
|
||||||
if !self.didPlayPresentationAnimation {
|
|
||||||
self.didPlayPresentationAnimation = true
|
|
||||||
|
|
||||||
self.hapticFeedback.impact()
|
|
||||||
self.controllerNode.animateIn()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
public final class EditMessage {
|
||||||
self.validLayout = layout
|
public let messages: [EngineMessage]
|
||||||
|
public let mediaPreview: ChatSendMessageContextScreenMediaPreview?
|
||||||
|
|
||||||
super.containerLayoutUpdated(layout, transition: transition)
|
public init(messages: [EngineMessage], mediaPreview: ChatSendMessageContextScreenMediaPreview?) {
|
||||||
|
self.messages = messages
|
||||||
self.controllerNode.containerLayoutUpdated(layout, transition: transition)
|
self.mediaPreview = mediaPreview
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
case sendMessage(SendMessage)
|
||||||
self.dismiss(cancel: true)
|
case editMessage(EditMessage)
|
||||||
}
|
|
||||||
|
|
||||||
private func dismiss(cancel: Bool) {
|
|
||||||
self.statusBar.statusBarStyle = .Ignore
|
|
||||||
self.controllerNode.animateOut(cancel: cancel, completion: { [weak self] in
|
|
||||||
self?.completion()
|
|
||||||
self?.didPlayPresentationAnimation = false
|
|
||||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeChatSendMessageActionSheetController(
|
public func makeChatSendMessageActionSheetController(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
|
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
|
||||||
peerId: EnginePeer.Id?,
|
peerId: EnginePeer.Id?,
|
||||||
isScheduledMessages: Bool = false,
|
params: SendMessageActionSheetControllerParams,
|
||||||
forwardMessageIds: [EngineMessage.Id]?,
|
|
||||||
hasEntityKeyboard: Bool,
|
hasEntityKeyboard: Bool,
|
||||||
gesture: ContextGesture,
|
gesture: ContextGesture,
|
||||||
sourceSendButton: ASDisplayNode,
|
sourceSendButton: ASDisplayNode,
|
||||||
textInputView: UITextView,
|
textInputView: UITextView,
|
||||||
mediaPreview: ChatSendMessageContextScreenMediaPreview? = nil,
|
|
||||||
mediaCaptionIsAbove: (Bool, (Bool) -> Void)? = nil,
|
|
||||||
emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?,
|
emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?,
|
||||||
wallpaperBackgroundNode: WallpaperBackgroundNode? = nil,
|
wallpaperBackgroundNode: WallpaperBackgroundNode? = nil,
|
||||||
attachment: Bool = false,
|
|
||||||
canSendWhenOnline: Bool,
|
|
||||||
completion: @escaping () -> Void,
|
completion: @escaping () -> Void,
|
||||||
sendMessage: @escaping (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.SendParameters?) -> Void,
|
sendMessage: @escaping (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.SendParameters?) -> Void,
|
||||||
schedule: @escaping (ChatSendMessageActionSheetController.SendParameters?) -> Void,
|
schedule: @escaping (ChatSendMessageActionSheetController.SendParameters?) -> Void,
|
||||||
@ -182,43 +70,17 @@ public func makeChatSendMessageActionSheetController(
|
|||||||
availableMessageEffects: AvailableMessageEffects? = nil,
|
availableMessageEffects: AvailableMessageEffects? = nil,
|
||||||
isPremium: Bool = false
|
isPremium: Bool = false
|
||||||
) -> ChatSendMessageActionSheetController {
|
) -> ChatSendMessageActionSheetController {
|
||||||
if textInputView.text.isEmpty && !"".isEmpty {
|
|
||||||
return ChatSendMessageActionSheetControllerImpl(
|
|
||||||
context: context,
|
|
||||||
updatedPresentationData: updatedPresentationData,
|
|
||||||
peerId: peerId,
|
|
||||||
isScheduledMessages: isScheduledMessages,
|
|
||||||
forwardMessageIds: forwardMessageIds,
|
|
||||||
hasEntityKeyboard: hasEntityKeyboard,
|
|
||||||
gesture: gesture,
|
|
||||||
sourceSendButton: sourceSendButton,
|
|
||||||
textInputView: textInputView,
|
|
||||||
emojiViewProvider: emojiViewProvider,
|
|
||||||
attachment: attachment,
|
|
||||||
canSendWhenOnline: canSendWhenOnline,
|
|
||||||
completion: completion,
|
|
||||||
sendMessage: sendMessage,
|
|
||||||
schedule: schedule,
|
|
||||||
reactionItems: nil
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ChatSendMessageContextScreen(
|
return ChatSendMessageContextScreen(
|
||||||
context: context,
|
context: context,
|
||||||
updatedPresentationData: updatedPresentationData,
|
updatedPresentationData: updatedPresentationData,
|
||||||
peerId: peerId,
|
peerId: peerId,
|
||||||
isScheduledMessages: isScheduledMessages,
|
params: params,
|
||||||
forwardMessageIds: forwardMessageIds,
|
|
||||||
hasEntityKeyboard: hasEntityKeyboard,
|
hasEntityKeyboard: hasEntityKeyboard,
|
||||||
gesture: gesture,
|
gesture: gesture,
|
||||||
sourceSendButton: sourceSendButton,
|
sourceSendButton: sourceSendButton,
|
||||||
textInputView: textInputView,
|
textInputView: textInputView,
|
||||||
mediaPreview: mediaPreview,
|
|
||||||
mediaCaptionIsAbove: mediaCaptionIsAbove,
|
|
||||||
emojiViewProvider: emojiViewProvider,
|
emojiViewProvider: emojiViewProvider,
|
||||||
wallpaperBackgroundNode: wallpaperBackgroundNode,
|
wallpaperBackgroundNode: wallpaperBackgroundNode,
|
||||||
attachment: attachment,
|
|
||||||
canSendWhenOnline: canSendWhenOnline,
|
|
||||||
completion: completion,
|
completion: completion,
|
||||||
sendMessage: sendMessage,
|
sendMessage: sendMessage,
|
||||||
schedule: schedule,
|
schedule: schedule,
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -56,18 +56,13 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
|
let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
|
||||||
let peerId: EnginePeer.Id?
|
let peerId: EnginePeer.Id?
|
||||||
let isScheduledMessages: Bool
|
let params: SendMessageActionSheetControllerParams
|
||||||
let forwardMessageIds: [EngineMessage.Id]?
|
|
||||||
let hasEntityKeyboard: Bool
|
let hasEntityKeyboard: Bool
|
||||||
let gesture: ContextGesture
|
let gesture: ContextGesture
|
||||||
let sourceSendButton: ASDisplayNode
|
let sourceSendButton: ASDisplayNode
|
||||||
let textInputView: UITextView
|
let textInputView: UITextView
|
||||||
let mediaPreview: ChatSendMessageContextScreenMediaPreview?
|
|
||||||
let mediaCaptionIsAbove: (Bool, (Bool) -> Void)?
|
|
||||||
let emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?
|
let emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?
|
||||||
let wallpaperBackgroundNode: WallpaperBackgroundNode?
|
let wallpaperBackgroundNode: WallpaperBackgroundNode?
|
||||||
let attachment: Bool
|
|
||||||
let canSendWhenOnline: Bool
|
|
||||||
let completion: () -> Void
|
let completion: () -> Void
|
||||||
let sendMessage: (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.SendParameters?) -> Void
|
let sendMessage: (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.SendParameters?) -> Void
|
||||||
let schedule: (ChatSendMessageActionSheetController.SendParameters?) -> Void
|
let schedule: (ChatSendMessageActionSheetController.SendParameters?) -> Void
|
||||||
@ -80,18 +75,13 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?,
|
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?,
|
||||||
peerId: EnginePeer.Id?,
|
peerId: EnginePeer.Id?,
|
||||||
isScheduledMessages: Bool,
|
params: SendMessageActionSheetControllerParams,
|
||||||
forwardMessageIds: [EngineMessage.Id]?,
|
|
||||||
hasEntityKeyboard: Bool,
|
hasEntityKeyboard: Bool,
|
||||||
gesture: ContextGesture,
|
gesture: ContextGesture,
|
||||||
sourceSendButton: ASDisplayNode,
|
sourceSendButton: ASDisplayNode,
|
||||||
textInputView: UITextView,
|
textInputView: UITextView,
|
||||||
mediaPreview: ChatSendMessageContextScreenMediaPreview?,
|
|
||||||
mediaCaptionIsAbove: (Bool, (Bool) -> Void)?,
|
|
||||||
emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?,
|
emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?,
|
||||||
wallpaperBackgroundNode: WallpaperBackgroundNode?,
|
wallpaperBackgroundNode: WallpaperBackgroundNode?,
|
||||||
attachment: Bool,
|
|
||||||
canSendWhenOnline: Bool,
|
|
||||||
completion: @escaping () -> Void,
|
completion: @escaping () -> Void,
|
||||||
sendMessage: @escaping (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.SendParameters?) -> Void,
|
sendMessage: @escaping (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.SendParameters?) -> Void,
|
||||||
schedule: @escaping (ChatSendMessageActionSheetController.SendParameters?) -> Void,
|
schedule: @escaping (ChatSendMessageActionSheetController.SendParameters?) -> Void,
|
||||||
@ -103,18 +93,13 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
self.context = context
|
self.context = context
|
||||||
self.updatedPresentationData = updatedPresentationData
|
self.updatedPresentationData = updatedPresentationData
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
self.isScheduledMessages = isScheduledMessages
|
self.params = params
|
||||||
self.forwardMessageIds = forwardMessageIds
|
|
||||||
self.hasEntityKeyboard = hasEntityKeyboard
|
self.hasEntityKeyboard = hasEntityKeyboard
|
||||||
self.gesture = gesture
|
self.gesture = gesture
|
||||||
self.sourceSendButton = sourceSendButton
|
self.sourceSendButton = sourceSendButton
|
||||||
self.textInputView = textInputView
|
self.textInputView = textInputView
|
||||||
self.mediaPreview = mediaPreview
|
|
||||||
self.mediaCaptionIsAbove = mediaCaptionIsAbove
|
|
||||||
self.emojiViewProvider = emojiViewProvider
|
self.emojiViewProvider = emojiViewProvider
|
||||||
self.wallpaperBackgroundNode = wallpaperBackgroundNode
|
self.wallpaperBackgroundNode = wallpaperBackgroundNode
|
||||||
self.attachment = attachment
|
|
||||||
self.canSendWhenOnline = canSendWhenOnline
|
|
||||||
self.completion = completion
|
self.completion = completion
|
||||||
self.sendMessage = sendMessage
|
self.sendMessage = sendMessage
|
||||||
self.schedule = schedule
|
self.schedule = schedule
|
||||||
@ -331,7 +316,14 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
let themeUpdated = environment.theme !== self.environment?.theme
|
let themeUpdated = environment.theme !== self.environment?.theme
|
||||||
|
|
||||||
if self.component == nil {
|
if self.component == nil {
|
||||||
self.mediaCaptionIsAbove = component.mediaCaptionIsAbove?.0 ?? false
|
switch component.params {
|
||||||
|
case let .sendMessage(sendMessage):
|
||||||
|
self.mediaCaptionIsAbove = sendMessage.mediaCaptionIsAbove?.0 ?? false
|
||||||
|
case let .editMessage(editMessage):
|
||||||
|
self.mediaCaptionIsAbove = editMessage.messages.contains(where: {
|
||||||
|
return $0.attributes.contains(where: { $0 is InvertMediaMessageAttribute })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
component.gesture.externalUpdated = { [weak self] view, location in
|
component.gesture.externalUpdated = { [weak self] view, location in
|
||||||
guard let self, let actionsStackNode = self.actionsStackNode else {
|
guard let self, let actionsStackNode = self.actionsStackNode else {
|
||||||
@ -375,7 +367,15 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
var isMessageVisible = component.mediaPreview != nil
|
var mediaPreview: ChatSendMessageContextScreenMediaPreview?
|
||||||
|
switch component.params {
|
||||||
|
case let .sendMessage(sendMessage):
|
||||||
|
mediaPreview = sendMessage.mediaPreview
|
||||||
|
case let .editMessage(editMessage):
|
||||||
|
mediaPreview = editMessage.mediaPreview
|
||||||
|
}
|
||||||
|
|
||||||
|
var isMessageVisible: Bool = mediaPreview != nil
|
||||||
|
|
||||||
let textString: NSAttributedString
|
let textString: NSAttributedString
|
||||||
if let attributedText = component.textInputView.attributedText {
|
if let attributedText = component.textInputView.attributedText {
|
||||||
@ -391,7 +391,14 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
if let current = self.sendButton {
|
if let current = self.sendButton {
|
||||||
sendButton = current
|
sendButton = current
|
||||||
} else {
|
} else {
|
||||||
sendButton = SendButton()
|
let sendButtonKind: SendButton.Kind
|
||||||
|
switch component.params {
|
||||||
|
case .sendMessage:
|
||||||
|
sendButtonKind = .send
|
||||||
|
case .editMessage:
|
||||||
|
sendButtonKind = .edit
|
||||||
|
}
|
||||||
|
sendButton = SendButton(kind: sendButtonKind)
|
||||||
sendButton.accessibilityLabel = environment.strings.MediaPicker_Send
|
sendButton.accessibilityLabel = environment.strings.MediaPicker_Send
|
||||||
sendButton.addTarget(self, action: #selector(self.onSendButtonPressed), for: .touchUpInside)
|
sendButton.addTarget(self, action: #selector(self.onSendButtonPressed), for: .touchUpInside)
|
||||||
/*if let snapshotView = component.sourceSendButton.view.snapshotView(afterScreenUpdates: false) {
|
/*if let snapshotView = component.sourceSendButton.view.snapshotView(afterScreenUpdates: false) {
|
||||||
@ -427,22 +434,43 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
var reminders = false
|
var reminders = false
|
||||||
var isSecret = false
|
var isSecret = false
|
||||||
var canSchedule = false
|
var canSchedule = false
|
||||||
if let peerId = component.peerId {
|
switch component.params {
|
||||||
reminders = peerId == component.context.account.peerId
|
case let .sendMessage(sendMessage):
|
||||||
isSecret = peerId.namespace == Namespaces.Peer.SecretChat
|
if let peerId = component.peerId {
|
||||||
canSchedule = !isSecret
|
reminders = peerId == component.context.account.peerId
|
||||||
}
|
isSecret = peerId.namespace == Namespaces.Peer.SecretChat
|
||||||
if component.isScheduledMessages {
|
canSchedule = !isSecret
|
||||||
canSchedule = false
|
}
|
||||||
|
if sendMessage.isScheduledMessages {
|
||||||
|
canSchedule = false
|
||||||
|
}
|
||||||
|
case .editMessage:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
var items: [ContextMenuItem] = []
|
var items: [ContextMenuItem] = []
|
||||||
if component.mediaCaptionIsAbove != nil, textString.length != 0, case .media = component.mediaPreview?.layoutType {
|
|
||||||
//TODO:localize
|
let canAdjustMediaCaptionPosition: Bool
|
||||||
|
switch component.params {
|
||||||
|
case let .sendMessage(sendMessage):
|
||||||
|
if case .media = mediaPreview?.layoutType {
|
||||||
|
canAdjustMediaCaptionPosition = sendMessage.mediaCaptionIsAbove != nil
|
||||||
|
} else {
|
||||||
|
canAdjustMediaCaptionPosition = false
|
||||||
|
}
|
||||||
|
case .editMessage:
|
||||||
|
if case .media = mediaPreview?.layoutType {
|
||||||
|
canAdjustMediaCaptionPosition = textString.length != 0
|
||||||
|
} else {
|
||||||
|
canAdjustMediaCaptionPosition = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if canAdjustMediaCaptionPosition, textString.length != 0 {
|
||||||
let mediaCaptionIsAbove = self.mediaCaptionIsAbove
|
let mediaCaptionIsAbove = self.mediaCaptionIsAbove
|
||||||
items.append(.action(ContextMenuActionItem(
|
items.append(.action(ContextMenuActionItem(
|
||||||
id: AnyHashable("captionPosition"),
|
id: AnyHashable("captionPosition"),
|
||||||
text: mediaCaptionIsAbove ? "Move Caption Down" : "Move Caption Up",
|
text: mediaCaptionIsAbove ? presentationData.strings.Chat_SendMessageMenu_MoveCaptionDown : presentationData.strings.Chat_SendMessageMenu_MoveCaptionUp,
|
||||||
icon: { _ in
|
icon: { _ in
|
||||||
return nil
|
return nil
|
||||||
}, iconAnimation: ContextMenuActionItem.IconAnimation(
|
}, iconAnimation: ContextMenuActionItem.IconAnimation(
|
||||||
@ -452,7 +480,12 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.mediaCaptionIsAbove = !self.mediaCaptionIsAbove
|
self.mediaCaptionIsAbove = !self.mediaCaptionIsAbove
|
||||||
component.mediaCaptionIsAbove?.1(self.mediaCaptionIsAbove)
|
switch component.params {
|
||||||
|
case let .sendMessage(sendMessage):
|
||||||
|
sendMessage.mediaCaptionIsAbove?.1(self.mediaCaptionIsAbove)
|
||||||
|
case .editMessage:
|
||||||
|
break
|
||||||
|
}
|
||||||
if !self.isUpdating {
|
if !self.isUpdating {
|
||||||
self.state?.updated(transition: .spring(duration: 0.35))
|
self.state?.updated(transition: .spring(duration: 0.35))
|
||||||
}
|
}
|
||||||
@ -461,34 +494,14 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
|
|
||||||
items.append(.separator)
|
items.append(.separator)
|
||||||
}
|
}
|
||||||
if !reminders {
|
switch component.params {
|
||||||
items.append(.action(ContextMenuActionItem(
|
case let.sendMessage(sendMessage):
|
||||||
id: AnyHashable("silent"),
|
if !reminders {
|
||||||
text: environment.strings.Conversation_SendMessage_SendSilently,
|
|
||||||
icon: { theme in
|
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/SilentIcon"), color: theme.contextMenu.primaryColor)
|
|
||||||
}, action: { [weak self] _, _ in
|
|
||||||
guard let self, let component = self.component else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.animateOutToEmpty = true
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
)))
|
|
||||||
|
|
||||||
if component.canSendWhenOnline && canSchedule {
|
|
||||||
items.append(.action(ContextMenuActionItem(
|
items.append(.action(ContextMenuActionItem(
|
||||||
id: AnyHashable("whenOnline"),
|
id: AnyHashable("silent"),
|
||||||
text: environment.strings.Conversation_SendMessage_SendWhenOnline,
|
text: environment.strings.Conversation_SendMessage_SendSilently,
|
||||||
icon: { theme in
|
icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/WhenOnlineIcon"), color: theme.contextMenu.primaryColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/SilentIcon"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { [weak self] _, _ in
|
}, action: { [weak self] _, _ in
|
||||||
guard let self, let component = self.component else {
|
guard let self, let component = self.component else {
|
||||||
return
|
return
|
||||||
@ -500,18 +513,62 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
textIsAboveMedia: self.mediaCaptionIsAbove
|
textIsAboveMedia: self.mediaCaptionIsAbove
|
||||||
)
|
)
|
||||||
|
|
||||||
component.sendMessage(.whenOnline, sendParameters)
|
component.sendMessage(.silently, sendParameters)
|
||||||
|
self.environment?.controller()?.dismiss()
|
||||||
|
}
|
||||||
|
)))
|
||||||
|
|
||||||
|
if sendMessage.canSendWhenOnline && canSchedule {
|
||||||
|
items.append(.action(ContextMenuActionItem(
|
||||||
|
id: AnyHashable("whenOnline"),
|
||||||
|
text: environment.strings.Conversation_SendMessage_SendWhenOnline,
|
||||||
|
icon: { theme in
|
||||||
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/WhenOnlineIcon"), color: theme.contextMenu.primaryColor)
|
||||||
|
}, action: { [weak self] _, _ in
|
||||||
|
guard let self, let component = self.component else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.animateOutToEmpty = true
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if canSchedule {
|
||||||
|
items.append(.action(ContextMenuActionItem(
|
||||||
|
id: AnyHashable("schedule"),
|
||||||
|
text: reminders ? environment.strings.Conversation_SendMessage_SetReminder: environment.strings.Conversation_SendMessage_ScheduleMessage,
|
||||||
|
icon: { theme in
|
||||||
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/ScheduleIcon"), color: theme.contextMenu.primaryColor)
|
||||||
|
}, action: { [weak self] _, _ in
|
||||||
|
guard let self, let component = self.component else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.animateOutToEmpty = true
|
||||||
|
|
||||||
|
let sendParameters = ChatSendMessageActionSheetController.SendParameters(
|
||||||
|
effect: self.selectedMessageEffect.flatMap({ ChatSendMessageActionSheetController.SendParameters.Effect(id: $0.id) }),
|
||||||
|
textIsAboveMedia: self.mediaCaptionIsAbove
|
||||||
|
)
|
||||||
|
|
||||||
|
component.schedule(sendParameters)
|
||||||
self.environment?.controller()?.dismiss()
|
self.environment?.controller()?.dismiss()
|
||||||
}
|
}
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
case .editMessage:
|
||||||
if canSchedule {
|
|
||||||
items.append(.action(ContextMenuActionItem(
|
items.append(.action(ContextMenuActionItem(
|
||||||
id: AnyHashable("schedule"),
|
id: AnyHashable("silent"),
|
||||||
text: reminders ? environment.strings.Conversation_SendMessage_SetReminder: environment.strings.Conversation_SendMessage_ScheduleMessage,
|
text: environment.strings.Chat_SendMessageMenu_EditMessage,
|
||||||
icon: { theme in
|
icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/ScheduleIcon"), color: theme.contextMenu.primaryColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { [weak self] _, _ in
|
}, action: { [weak self] _, _ in
|
||||||
guard let self, let component = self.component else {
|
guard let self, let component = self.component else {
|
||||||
return
|
return
|
||||||
@ -519,11 +576,11 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
self.animateOutToEmpty = true
|
self.animateOutToEmpty = true
|
||||||
|
|
||||||
let sendParameters = ChatSendMessageActionSheetController.SendParameters(
|
let sendParameters = ChatSendMessageActionSheetController.SendParameters(
|
||||||
effect: self.selectedMessageEffect.flatMap({ ChatSendMessageActionSheetController.SendParameters.Effect(id: $0.id) }),
|
effect: nil,
|
||||||
textIsAboveMedia: self.mediaCaptionIsAbove
|
textIsAboveMedia: self.mediaCaptionIsAbove
|
||||||
)
|
)
|
||||||
|
|
||||||
component.schedule(sendParameters)
|
component.sendMessage(.generic, sendParameters)
|
||||||
self.environment?.controller()?.dismiss()
|
self.environment?.controller()?.dismiss()
|
||||||
}
|
}
|
||||||
)))
|
)))
|
||||||
@ -636,7 +693,7 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
let messageTextInsets = sourceMessageTextInsets
|
let messageTextInsets = sourceMessageTextInsets
|
||||||
|
|
||||||
let messageItemViewContainerSize: CGSize
|
let messageItemViewContainerSize: CGSize
|
||||||
if let mediaPreview = component.mediaPreview {
|
if let mediaPreview {
|
||||||
switch mediaPreview.layoutType {
|
switch mediaPreview.layoutType {
|
||||||
case .message, .media:
|
case .message, .media:
|
||||||
messageItemViewContainerSize = CGSize(width: availableSize.width - 16.0 - 40.0, height: availableSize.height)
|
messageItemViewContainerSize = CGSize(width: availableSize.width - 16.0 - 40.0, height: availableSize.height)
|
||||||
@ -654,7 +711,7 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
textString: textString,
|
textString: textString,
|
||||||
sourceTextInputView: component.textInputView as? ChatInputTextView,
|
sourceTextInputView: component.textInputView as? ChatInputTextView,
|
||||||
emojiViewProvider: component.emojiViewProvider,
|
emojiViewProvider: component.emojiViewProvider,
|
||||||
sourceMediaPreview: component.mediaPreview,
|
sourceMediaPreview: mediaPreview,
|
||||||
mediaCaptionIsAbove: self.mediaCaptionIsAbove,
|
mediaCaptionIsAbove: self.mediaCaptionIsAbove,
|
||||||
textInsets: messageTextInsets,
|
textInsets: messageTextInsets,
|
||||||
explicitBackgroundSize: explicitMessageBackgroundSize,
|
explicitBackgroundSize: explicitMessageBackgroundSize,
|
||||||
@ -671,7 +728,6 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
if let current = self.reactionContextNode {
|
if let current = self.reactionContextNode {
|
||||||
reactionContextNode = current
|
reactionContextNode = current
|
||||||
} else {
|
} else {
|
||||||
//TODO:localize
|
|
||||||
reactionContextNode = ReactionContextNode(
|
reactionContextNode = ReactionContextNode(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
animationCache: component.context.animationCache,
|
animationCache: component.context.animationCache,
|
||||||
@ -692,7 +748,7 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
return ReactionContextItem.reaction(item: item, icon: icon)
|
return ReactionContextItem.reaction(item: item, icon: icon)
|
||||||
},
|
},
|
||||||
selectedItems: Set(),
|
selectedItems: Set(),
|
||||||
title: "Add an animated effect",
|
title: presentationData.strings.Chat_MessageEffectMenu_TitleAddEffect,
|
||||||
reactionsLocked: false,
|
reactionsLocked: false,
|
||||||
alwaysAllowPremiumReactions: false,
|
alwaysAllowPremiumReactions: false,
|
||||||
allPresetReactionsAreAvailable: true,
|
allPresetReactionsAreAvailable: true,
|
||||||
@ -937,13 +993,12 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
let presentationData = component.updatedPresentationData?.initial ?? component.context.sharedContext.currentPresentationData.with({ $0 })
|
let presentationData = component.updatedPresentationData?.initial ?? component.context.sharedContext.currentPresentationData.with({ $0 })
|
||||||
self.environment?.controller()?.present(UndoOverlayController(
|
self.environment?.controller()?.present(UndoOverlayController(
|
||||||
presentationData: presentationData,
|
presentationData: presentationData,
|
||||||
content: .premiumPaywall(
|
content: .premiumPaywall(
|
||||||
title: nil,
|
title: nil,
|
||||||
text: "Subscribe to [Telegram Premium]() to add this animated effect.",
|
text: presentationData.strings.Chat_SendMessageMenu_ToastPremiumRequired_Text,
|
||||||
customUndoText: nil,
|
customUndoText: nil,
|
||||||
timeout: nil,
|
timeout: nil,
|
||||||
linkAction: nil
|
linkAction: nil
|
||||||
@ -984,7 +1039,7 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var readyMessageItemFrame = CGRect(origin: CGPoint(x: readySendButtonFrame.minX + 8.0 - messageItemSize.width, y: readySendButtonFrame.maxY - 6.0 - messageItemSize.height), size: messageItemSize)
|
var readyMessageItemFrame = CGRect(origin: CGPoint(x: readySendButtonFrame.minX + 8.0 - messageItemSize.width, y: readySendButtonFrame.maxY - 6.0 - messageItemSize.height), size: messageItemSize)
|
||||||
if let mediaPreview = component.mediaPreview {
|
if let mediaPreview {
|
||||||
switch mediaPreview.layoutType {
|
switch mediaPreview.layoutType {
|
||||||
case .message, .media:
|
case .message, .media:
|
||||||
break
|
break
|
||||||
@ -1012,7 +1067,7 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
readySendButtonFrame.origin.y -= inputCoverOverflow
|
readySendButtonFrame.origin.y -= inputCoverOverflow
|
||||||
}
|
}
|
||||||
|
|
||||||
if let mediaPreview = component.mediaPreview {
|
if let mediaPreview {
|
||||||
switch mediaPreview.layoutType {
|
switch mediaPreview.layoutType {
|
||||||
case .message, .media:
|
case .message, .media:
|
||||||
break
|
break
|
||||||
@ -1034,7 +1089,7 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
let sendButtonFrame: CGRect
|
let sendButtonFrame: CGRect
|
||||||
switch self.presentationAnimationState {
|
switch self.presentationAnimationState {
|
||||||
case .initial:
|
case .initial:
|
||||||
if component.mediaPreview != nil {
|
if mediaPreview != nil {
|
||||||
messageItemFrame = readyMessageItemFrame
|
messageItemFrame = readyMessageItemFrame
|
||||||
actionsStackFrame = readyActionsStackFrame
|
actionsStackFrame = readyActionsStackFrame
|
||||||
} else {
|
} else {
|
||||||
@ -1048,7 +1103,7 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
actionsStackFrame = readyActionsStackFrame
|
actionsStackFrame = readyActionsStackFrame
|
||||||
sendButtonFrame = readySendButtonFrame
|
sendButtonFrame = readySendButtonFrame
|
||||||
} else {
|
} else {
|
||||||
if component.mediaPreview != nil {
|
if mediaPreview != nil {
|
||||||
messageItemFrame = readyMessageItemFrame
|
messageItemFrame = readyMessageItemFrame
|
||||||
actionsStackFrame = readyActionsStackFrame
|
actionsStackFrame = readyActionsStackFrame
|
||||||
} else {
|
} else {
|
||||||
@ -1066,7 +1121,7 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
transition.setFrame(view: messageItemView, frame: messageItemFrame)
|
transition.setFrame(view: messageItemView, frame: messageItemFrame)
|
||||||
transition.setAlpha(view: messageItemView, alpha: isMessageVisible ? 1.0 : 0.0)
|
transition.setAlpha(view: messageItemView, alpha: isMessageVisible ? 1.0 : 0.0)
|
||||||
messageItemView.updateClippingRect(
|
messageItemView.updateClippingRect(
|
||||||
sourceMediaPreview: component.mediaPreview,
|
sourceMediaPreview: mediaPreview,
|
||||||
isAnimatedIn: self.presentationAnimationState.key == .animatedIn,
|
isAnimatedIn: self.presentationAnimationState.key == .animatedIn,
|
||||||
localFrame: messageItemFrame,
|
localFrame: messageItemFrame,
|
||||||
containerSize: availableSize,
|
containerSize: availableSize,
|
||||||
@ -1120,7 +1175,7 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
let reactionContextY = environment.statusBarHeight
|
let reactionContextY = environment.statusBarHeight
|
||||||
let size = availableSize
|
let size = availableSize
|
||||||
var reactionsAnchorRect = messageItemFrame
|
var reactionsAnchorRect = messageItemFrame
|
||||||
if let mediaPreview = component.mediaPreview {
|
if let mediaPreview {
|
||||||
switch mediaPreview.layoutType {
|
switch mediaPreview.layoutType {
|
||||||
case .message, .media:
|
case .message, .media:
|
||||||
reactionsAnchorRect.size.width += 100.0
|
reactionsAnchorRect.size.width += 100.0
|
||||||
@ -1193,14 +1248,14 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
guard let component = self.component else {
|
guard let component = self.component else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if component.mediaPreview == nil {
|
if mediaPreview == nil {
|
||||||
component.textInputView.isHidden = true
|
component.textInputView.isHidden = true
|
||||||
}
|
}
|
||||||
component.sourceSendButton.isHidden = true
|
component.sourceSendButton.isHidden = true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if component.mediaPreview == nil {
|
if mediaPreview == nil {
|
||||||
component.textInputView.isHidden = true
|
component.textInputView.isHidden = true
|
||||||
}
|
}
|
||||||
component.sourceSendButton.isHidden = true
|
component.sourceSendButton.isHidden = true
|
||||||
@ -1212,7 +1267,7 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
backgroundAlpha = 0.0
|
backgroundAlpha = 0.0
|
||||||
|
|
||||||
if self.animateOutToEmpty {
|
if self.animateOutToEmpty {
|
||||||
if component.mediaPreview == nil {
|
if mediaPreview == nil {
|
||||||
component.textInputView.isHidden = false
|
component.textInputView.isHidden = false
|
||||||
}
|
}
|
||||||
component.sourceSendButton.isHidden = false
|
component.sourceSendButton.isHidden = false
|
||||||
@ -1234,7 +1289,7 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
if !self.performedActionsOnAnimateOut {
|
if !self.performedActionsOnAnimateOut {
|
||||||
self.performedActionsOnAnimateOut = true
|
self.performedActionsOnAnimateOut = true
|
||||||
if let component = self.component, !self.animateOutToEmpty {
|
if let component = self.component, !self.animateOutToEmpty {
|
||||||
if component.mediaPreview == nil {
|
if mediaPreview == nil {
|
||||||
component.textInputView.isHidden = false
|
component.textInputView.isHidden = false
|
||||||
}
|
}
|
||||||
component.sourceSendButton.isHidden = false
|
component.sourceSendButton.isHidden = false
|
||||||
@ -1277,18 +1332,13 @@ public class ChatSendMessageContextScreen: ViewControllerComponentContainer, Cha
|
|||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?,
|
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?,
|
||||||
peerId: EnginePeer.Id?,
|
peerId: EnginePeer.Id?,
|
||||||
isScheduledMessages: Bool,
|
params: SendMessageActionSheetControllerParams,
|
||||||
forwardMessageIds: [EngineMessage.Id]?,
|
|
||||||
hasEntityKeyboard: Bool,
|
hasEntityKeyboard: Bool,
|
||||||
gesture: ContextGesture,
|
gesture: ContextGesture,
|
||||||
sourceSendButton: ASDisplayNode,
|
sourceSendButton: ASDisplayNode,
|
||||||
textInputView: UITextView,
|
textInputView: UITextView,
|
||||||
mediaPreview: ChatSendMessageContextScreenMediaPreview?,
|
|
||||||
mediaCaptionIsAbove: (Bool, (Bool) -> Void)?,
|
|
||||||
emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?,
|
emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?,
|
||||||
wallpaperBackgroundNode: WallpaperBackgroundNode?,
|
wallpaperBackgroundNode: WallpaperBackgroundNode?,
|
||||||
attachment: Bool,
|
|
||||||
canSendWhenOnline: Bool,
|
|
||||||
completion: @escaping () -> Void,
|
completion: @escaping () -> Void,
|
||||||
sendMessage: @escaping (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.SendParameters?) -> Void,
|
sendMessage: @escaping (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.SendParameters?) -> Void,
|
||||||
schedule: @escaping (ChatSendMessageActionSheetController.SendParameters?) -> Void,
|
schedule: @escaping (ChatSendMessageActionSheetController.SendParameters?) -> Void,
|
||||||
@ -1305,18 +1355,13 @@ public class ChatSendMessageContextScreen: ViewControllerComponentContainer, Cha
|
|||||||
context: context,
|
context: context,
|
||||||
updatedPresentationData: updatedPresentationData,
|
updatedPresentationData: updatedPresentationData,
|
||||||
peerId: peerId,
|
peerId: peerId,
|
||||||
isScheduledMessages: isScheduledMessages,
|
params: params,
|
||||||
forwardMessageIds: forwardMessageIds,
|
|
||||||
hasEntityKeyboard: hasEntityKeyboard,
|
hasEntityKeyboard: hasEntityKeyboard,
|
||||||
gesture: gesture,
|
gesture: gesture,
|
||||||
sourceSendButton: sourceSendButton,
|
sourceSendButton: sourceSendButton,
|
||||||
textInputView: textInputView,
|
textInputView: textInputView,
|
||||||
mediaPreview: mediaPreview,
|
|
||||||
mediaCaptionIsAbove: mediaCaptionIsAbove,
|
|
||||||
emojiViewProvider: emojiViewProvider,
|
emojiViewProvider: emojiViewProvider,
|
||||||
wallpaperBackgroundNode: wallpaperBackgroundNode,
|
wallpaperBackgroundNode: wallpaperBackgroundNode,
|
||||||
attachment: attachment,
|
|
||||||
canSendWhenOnline: canSendWhenOnline,
|
|
||||||
completion: completion,
|
completion: completion,
|
||||||
sendMessage: sendMessage,
|
sendMessage: sendMessage,
|
||||||
schedule: schedule,
|
schedule: schedule,
|
||||||
|
@ -18,6 +18,7 @@ import MultilineTextWithEntitiesComponent
|
|||||||
import ReactionButtonListComponent
|
import ReactionButtonListComponent
|
||||||
import MultilineTextComponent
|
import MultilineTextComponent
|
||||||
import ChatInputTextNode
|
import ChatInputTextNode
|
||||||
|
import EmojiTextAttachmentView
|
||||||
|
|
||||||
private final class EffectIcon: Component {
|
private final class EffectIcon: Component {
|
||||||
enum Content: Equatable {
|
enum Content: Equatable {
|
||||||
@ -134,6 +135,68 @@ private final class EffectIcon: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final class CustomEmojiContainerView: UIView {
|
||||||
|
private let emojiViewProvider: (ChatTextInputTextCustomEmojiAttribute) -> UIView?
|
||||||
|
|
||||||
|
private var emojiLayers: [InlineStickerItemLayer.Key: UIView] = [:]
|
||||||
|
|
||||||
|
init(emojiViewProvider: @escaping (ChatTextInputTextCustomEmojiAttribute) -> UIView?) {
|
||||||
|
self.emojiViewProvider = emojiViewProvider
|
||||||
|
|
||||||
|
super.init(frame: CGRect())
|
||||||
|
}
|
||||||
|
|
||||||
|
required init(coder: NSCoder) {
|
||||||
|
preconditionFailure()
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(emojiRects: [(CGRect, ChatTextInputTextCustomEmojiAttribute)]) {
|
||||||
|
var nextIndexById: [Int64: Int] = [:]
|
||||||
|
|
||||||
|
var validKeys = Set<InlineStickerItemLayer.Key>()
|
||||||
|
for (rect, emoji) in emojiRects {
|
||||||
|
let index: Int
|
||||||
|
if let nextIndex = nextIndexById[emoji.fileId] {
|
||||||
|
index = nextIndex
|
||||||
|
} else {
|
||||||
|
index = 0
|
||||||
|
}
|
||||||
|
nextIndexById[emoji.fileId] = index + 1
|
||||||
|
|
||||||
|
let key = InlineStickerItemLayer.Key(id: emoji.fileId, index: index)
|
||||||
|
|
||||||
|
let view: UIView
|
||||||
|
if let current = self.emojiLayers[key] {
|
||||||
|
view = current
|
||||||
|
} else if let newView = self.emojiViewProvider(emoji) {
|
||||||
|
view = newView
|
||||||
|
self.addSubview(newView)
|
||||||
|
self.emojiLayers[key] = view
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = CGSize(width: 24.0, height: 24.0)
|
||||||
|
|
||||||
|
view.frame = CGRect(origin: CGPoint(x: floor(rect.midX - size.width / 2.0), y: floor(rect.midY - size.height / 2.0)), size: size)
|
||||||
|
|
||||||
|
validKeys.insert(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
var removeKeys: [InlineStickerItemLayer.Key] = []
|
||||||
|
for (key, view) in self.emojiLayers {
|
||||||
|
if !validKeys.contains(key) {
|
||||||
|
removeKeys.append(key)
|
||||||
|
view.removeFromSuperview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for key in removeKeys {
|
||||||
|
self.emojiLayers.removeValue(forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
final class MessageItemView: UIView {
|
final class MessageItemView: UIView {
|
||||||
private let backgroundWallpaperNode: ChatMessageBubbleBackdrop
|
private let backgroundWallpaperNode: ChatMessageBubbleBackdrop
|
||||||
private let backgroundNode: ChatMessageBackground
|
private let backgroundNode: ChatMessageBackground
|
||||||
|
@ -19,6 +19,13 @@ import ActivityIndicator
|
|||||||
import RadialStatusNode
|
import RadialStatusNode
|
||||||
|
|
||||||
final class SendButton: HighlightTrackingButton {
|
final class SendButton: HighlightTrackingButton {
|
||||||
|
enum Kind {
|
||||||
|
case send
|
||||||
|
case edit
|
||||||
|
}
|
||||||
|
|
||||||
|
private let kind: Kind
|
||||||
|
|
||||||
private let containerView: UIView
|
private let containerView: UIView
|
||||||
private var backgroundContent: WallpaperBubbleBackgroundNode?
|
private var backgroundContent: WallpaperBubbleBackgroundNode?
|
||||||
private let backgroundLayer: SimpleLayer
|
private let backgroundLayer: SimpleLayer
|
||||||
@ -28,7 +35,9 @@ final class SendButton: HighlightTrackingButton {
|
|||||||
private var didProcessSourceCustomContent: Bool = false
|
private var didProcessSourceCustomContent: Bool = false
|
||||||
private var sourceCustomContentView: UIView?
|
private var sourceCustomContentView: UIView?
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
init(kind: Kind) {
|
||||||
|
self.kind = kind
|
||||||
|
|
||||||
self.containerView = UIView()
|
self.containerView = UIView()
|
||||||
self.containerView.isUserInteractionEnabled = false
|
self.containerView.isUserInteractionEnabled = false
|
||||||
|
|
||||||
@ -37,7 +46,7 @@ final class SendButton: HighlightTrackingButton {
|
|||||||
self.iconView = UIImageView()
|
self.iconView = UIImageView()
|
||||||
self.iconView.isUserInteractionEnabled = false
|
self.iconView.isUserInteractionEnabled = false
|
||||||
|
|
||||||
super.init(frame: frame)
|
super.init(frame: CGRect())
|
||||||
|
|
||||||
self.containerView.clipsToBounds = true
|
self.containerView.clipsToBounds = true
|
||||||
self.addSubview(self.containerView)
|
self.addSubview(self.containerView)
|
||||||
@ -100,7 +109,12 @@ final class SendButton: HighlightTrackingButton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.iconView.image == nil {
|
if self.iconView.image == nil {
|
||||||
self.iconView.image = PresentationResourcesChat.chatInputPanelSendIconImage(presentationData.theme)
|
switch self.kind {
|
||||||
|
case .send:
|
||||||
|
self.iconView.image = PresentationResourcesChat.chatInputPanelSendIconImage(presentationData.theme)
|
||||||
|
case .edit:
|
||||||
|
self.iconView.image = PresentationResourcesChat.chatInputPanelApplyIconImage(presentationData.theme)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let sourceCustomContentView = self.sourceCustomContentView {
|
if let sourceCustomContentView = self.sourceCustomContentView {
|
||||||
|
@ -2502,8 +2502,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
mediaCaptionIsAbove = interaction.captionIsAboveMedia
|
mediaCaptionIsAbove = interaction.captionIsAboveMedia
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO:localize
|
items.append(.action(ContextMenuActionItem(text: mediaCaptionIsAbove ? strings.Chat_SendMessageMenu_MoveCaptionDown : strings.Chat_SendMessageMenu_MoveCaptionUp, icon: { _ in return nil }, iconAnimation: ContextMenuActionItem.IconAnimation(
|
||||||
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"
|
name: !mediaCaptionIsAbove ? "message_preview_sort_above" : "message_preview_sort_below"
|
||||||
), action: { [weak self] _, f in
|
), action: { [weak self] _, f in
|
||||||
f(.default)
|
f(.default)
|
||||||
|
@ -1796,6 +1796,9 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let presentationData = self.context.sharedContext.currentPresentationData.with({ $0 })
|
||||||
|
let strings = presentationData.strings
|
||||||
|
|
||||||
switch query {
|
switch query {
|
||||||
case .none:
|
case .none:
|
||||||
self.emojiSearchDisposable.set(nil)
|
self.emojiSearchDisposable.set(nil)
|
||||||
@ -1929,7 +1932,7 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
} else {
|
} else {
|
||||||
resultGroupIndexById[groupId] = resultGroups.count
|
resultGroupIndexById[groupId] = resultGroups.count
|
||||||
//TODO:localize
|
//TODO:localize
|
||||||
resultGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: i == 0 ? nil : "Message Effects", subtitle: nil, actionButtonTitle: nil, isPremiumLocked: false, isFeatured: false, displayPremiumBadges: false, hasEdit: false, headerItem: nil, items: [resultItem]))
|
resultGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: i == 0 ? nil : strings.Chat_MessageEffectMenu_SectionMessageEffects, subtitle: nil, actionButtonTitle: nil, isPremiumLocked: false, isFeatured: false, displayPremiumBadges: false, hasEdit: false, headerItem: nil, items: [resultItem]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2281,7 +2284,7 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
} else {
|
} else {
|
||||||
resultGroupIndexById[groupId] = resultGroups.count
|
resultGroupIndexById[groupId] = resultGroups.count
|
||||||
//TODO:localize
|
//TODO:localize
|
||||||
resultGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: i == 0 ? nil : "Message Effects", subtitle: nil, actionButtonTitle: nil, isPremiumLocked: false, isFeatured: false, displayPremiumBadges: false, hasEdit: false, headerItem: nil, items: [resultItem]))
|
resultGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: i == 0 ? nil : strings.Chat_MessageEffectMenu_SectionMessageEffects, subtitle: nil, actionButtonTitle: nil, isPremiumLocked: false, isFeatured: false, displayPremiumBadges: false, hasEdit: false, headerItem: nil, items: [resultItem]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2256,8 +2256,7 @@ public extension EmojiPagerContentComponent {
|
|||||||
itemGroups[groupIndex].items.append(resultItem)
|
itemGroups[groupIndex].items.append(resultItem)
|
||||||
} else {
|
} else {
|
||||||
itemGroupIndexById[groupId] = itemGroups.count
|
itemGroupIndexById[groupId] = itemGroups.count
|
||||||
//TODO:localize
|
itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: i == 0 ? nil : strings.Chat_MessageEffectMenu_SectionMessageEffects, subtitle: nil, actionButtonTitle: nil, isPremiumLocked: false, isFeatured: false, displayPremiumBadges: false, hasEdit: false, headerItem: nil, items: [resultItem]))
|
||||||
itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: i == 0 ? nil : "Message Effects", subtitle: nil, actionButtonTitle: nil, isPremiumLocked: false, isFeatured: false, displayPremiumBadges: false, hasEdit: false, headerItem: nil, items: [resultItem]))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -691,24 +691,44 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
|||||||
hasEntityKeyboard = true
|
hasEntityKeyboard = true
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = makeChatSendMessageActionSheetController(context: strongSelf.context, peerId: strongSelf.presentationInterfaceState.chatLocation.peerId, forwardMessageIds: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds, hasEntityKeyboard: hasEntityKeyboard, gesture: gesture, sourceSendButton: node, textInputView: textInputNode.textView, emojiViewProvider: textInputPanelNode.emojiViewProvider, canSendWhenOnline: false, completion: {
|
let controller = makeChatSendMessageActionSheetController(
|
||||||
}, sendMessage: { [weak textInputPanelNode] mode, messageEffect in
|
context: strongSelf.context,
|
||||||
switch mode {
|
peerId: strongSelf.presentationInterfaceState.chatLocation.peerId,
|
||||||
case .generic:
|
params: .sendMessage(SendMessageActionSheetControllerParams.SendMessage(
|
||||||
textInputPanelNode?.sendMessage(.generic, messageEffect)
|
isScheduledMessages: false,
|
||||||
case .silently:
|
mediaPreview: nil,
|
||||||
textInputPanelNode?.sendMessage(.silent, messageEffect)
|
mediaCaptionIsAbove: nil,
|
||||||
case .whenOnline:
|
attachment: false,
|
||||||
textInputPanelNode?.sendMessage(.whenOnline, messageEffect)
|
canSendWhenOnline: false,
|
||||||
|
forwardMessageIds: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? []
|
||||||
|
)),
|
||||||
|
hasEntityKeyboard: hasEntityKeyboard,
|
||||||
|
gesture: gesture,
|
||||||
|
sourceSendButton: node,
|
||||||
|
textInputView: textInputNode.textView,
|
||||||
|
emojiViewProvider: textInputPanelNode.emojiViewProvider,
|
||||||
|
completion: {
|
||||||
|
},
|
||||||
|
sendMessage: { [weak textInputPanelNode] mode, messageEffect in
|
||||||
|
switch mode {
|
||||||
|
case .generic:
|
||||||
|
textInputPanelNode?.sendMessage(.generic, messageEffect)
|
||||||
|
case .silently:
|
||||||
|
textInputPanelNode?.sendMessage(.silent, messageEffect)
|
||||||
|
case .whenOnline:
|
||||||
|
textInputPanelNode?.sendMessage(.whenOnline, messageEffect)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
schedule: { [weak textInputPanelNode] messageEffect in
|
||||||
|
textInputPanelNode?.sendMessage(.schedule, messageEffect)
|
||||||
|
},
|
||||||
|
openPremiumPaywall: { [weak controller] c in
|
||||||
|
guard let controller else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
controller.push(c)
|
||||||
}
|
}
|
||||||
}, schedule: { [weak textInputPanelNode] messageEffect in
|
)
|
||||||
textInputPanelNode?.sendMessage(.schedule, messageEffect)
|
|
||||||
}, openPremiumPaywall: { [weak controller] c in
|
|
||||||
guard let controller else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
controller.push(c)
|
|
||||||
})
|
|
||||||
strongSelf.presentInGlobalOverlay(controller, nil)
|
strongSelf.presentInGlobalOverlay(controller, nil)
|
||||||
}, openScheduledMessages: {
|
}, openScheduledMessages: {
|
||||||
}, openPeersNearby: {
|
}, openPeersNearby: {
|
||||||
|
@ -2918,13 +2918,13 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !hashtag.isEmpty {
|
if !hashtag.isEmpty {
|
||||||
if "".isEmpty {
|
/*if !"".isEmpty {
|
||||||
let searchController = component.context.sharedContext.makeStorySearchController(context: component.context, query: hashtag)
|
let searchController = component.context.sharedContext.makeStorySearchController(context: component.context, query: hashtag)
|
||||||
navigationController.pushViewController(searchController)
|
navigationController.pushViewController(searchController)
|
||||||
} else {
|
} else {*/
|
||||||
let searchController = component.context.sharedContext.makeHashtagSearchController(context: component.context, peer: peer.flatMap(EnginePeer.init), query: hashtag, all: true)
|
let searchController = component.context.sharedContext.makeHashtagSearchController(context: component.context, peer: peer.flatMap(EnginePeer.init), query: hashtag, all: true)
|
||||||
navigationController.pushViewController(searchController)
|
navigationController.pushViewController(searchController)
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -1276,8 +1276,7 @@ extension ChatControllerImpl {
|
|||||||
strongSelf.chatDisplayNode.historyNode.scrollToEndOfHistory()
|
strongSelf.chatDisplayNode.historyNode.scrollToEndOfHistory()
|
||||||
case let .businessLinkSetup(link):
|
case let .businessLinkSetup(link):
|
||||||
if messages.count > 1 {
|
if messages.count > 1 {
|
||||||
//TODO:localize
|
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.BusinessLink_AlertTextLimitText, actions: [
|
||||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: "The message text limit is 4096 characters", actions: [
|
|
||||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})
|
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})
|
||||||
]), in: .window(.root))
|
]), in: .window(.root))
|
||||||
|
|
||||||
|
@ -55,106 +55,178 @@ func chatMessageDisplaySendMessageOptions(selfController: ChatControllerImpl, no
|
|||||||
return user.isPremium
|
return user.isPremium
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let editMessages: Signal<[EngineMessage], NoError>
|
||||||
|
if let editMessage = selfController.presentationInterfaceState.interfaceState.editMessage {
|
||||||
|
editMessages = selfController.context.engine.data.get(
|
||||||
|
TelegramEngine.EngineData.Item.Messages.Message(id: editMessage.messageId)
|
||||||
|
)
|
||||||
|
|> map { message -> [EngineMessage] in
|
||||||
|
if let message {
|
||||||
|
return [message]
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
editMessages = .single([])
|
||||||
|
}
|
||||||
|
|
||||||
let _ = (combineLatest(
|
let _ = (combineLatest(
|
||||||
selfController.context.account.viewTracker.peerView(peerId) |> take(1),
|
selfController.context.account.viewTracker.peerView(peerId) |> take(1),
|
||||||
effectItems,
|
effectItems,
|
||||||
availableMessageEffects,
|
availableMessageEffects,
|
||||||
hasPremium
|
hasPremium,
|
||||||
|
editMessages
|
||||||
)
|
)
|
||||||
|> deliverOnMainQueue).startStandalone(next: { [weak selfController] peerView, effectItems, availableMessageEffects, hasPremium in
|
|> deliverOnMainQueue).startStandalone(next: { [weak selfController] peerView, effectItems, availableMessageEffects, hasPremium, editMessages in
|
||||||
guard let selfController, let peer = peerViewMainPeer(peerView) else {
|
guard let selfController, let peer = peerViewMainPeer(peerView) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var sendWhenOnlineAvailable = false
|
|
||||||
if let presence = peerView.peerPresences[peer.id] as? TelegramUserPresence, case let .present(until) = presence.status {
|
if let _ = selfController.presentationInterfaceState.interfaceState.editMessage {
|
||||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
if editMessages.isEmpty {
|
||||||
if currentTime > until {
|
return
|
||||||
sendWhenOnlineAvailable = true
|
|
||||||
}
|
}
|
||||||
}
|
let controller = makeChatSendMessageActionSheetController(
|
||||||
if peer.id.namespace == Namespaces.Peer.CloudUser && peer.id.id._internalGetInt64Value() == 777000 {
|
context: selfController.context,
|
||||||
sendWhenOnlineAvailable = false
|
updatedPresentationData: selfController.updatedPresentationData,
|
||||||
}
|
peerId: selfController.presentationInterfaceState.chatLocation.peerId,
|
||||||
|
params: .editMessage(SendMessageActionSheetControllerParams.EditMessage(
|
||||||
if sendWhenOnlineAvailable {
|
messages: editMessages,
|
||||||
let _ = ApplicationSpecificNotice.incrementSendWhenOnlineTip(accountManager: selfController.context.sharedContext.accountManager, count: 4).startStandalone()
|
mediaPreview: nil
|
||||||
}
|
)),
|
||||||
|
hasEntityKeyboard: hasEntityKeyboard,
|
||||||
var mediaPreview: ChatSendMessageContextScreenMediaPreview?
|
gesture: gesture,
|
||||||
if let videoRecorderValue = selfController.videoRecorderValue {
|
sourceSendButton: node,
|
||||||
mediaPreview = videoRecorderValue.makeSendMessageContextPreview()
|
textInputView: textInputView,
|
||||||
}
|
emojiViewProvider: selfController.chatDisplayNode.textInputPanelNode?.emojiViewProvider,
|
||||||
if let mediaDraftState = selfController.presentationInterfaceState.interfaceState.mediaDraftState {
|
wallpaperBackgroundNode: selfController.chatDisplayNode.backgroundNode,
|
||||||
if case let .audio(audio) = mediaDraftState {
|
completion: { [weak selfController] in
|
||||||
mediaPreview = ChatSendAudioMessageContextPreview(
|
guard let selfController else {
|
||||||
context: selfController.context,
|
return
|
||||||
presentationData: selfController.presentationData,
|
|
||||||
wallpaperBackgroundNode: selfController.chatDisplayNode.backgroundNode,
|
|
||||||
waveform: audio.waveform
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let controller = makeChatSendMessageActionSheetController(
|
|
||||||
context: selfController.context,
|
|
||||||
updatedPresentationData: selfController.updatedPresentationData,
|
|
||||||
peerId: selfController.presentationInterfaceState.chatLocation.peerId,
|
|
||||||
forwardMessageIds: selfController.presentationInterfaceState.interfaceState.forwardMessageIds,
|
|
||||||
hasEntityKeyboard: hasEntityKeyboard,
|
|
||||||
gesture: gesture,
|
|
||||||
sourceSendButton: node,
|
|
||||||
textInputView: textInputView,
|
|
||||||
mediaPreview: mediaPreview,
|
|
||||||
emojiViewProvider: selfController.chatDisplayNode.textInputPanelNode?.emojiViewProvider,
|
|
||||||
wallpaperBackgroundNode: selfController.chatDisplayNode.backgroundNode,
|
|
||||||
canSendWhenOnline: sendWhenOnlineAvailable,
|
|
||||||
completion: { [weak selfController] in
|
|
||||||
guard let selfController else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
selfController.supportedOrientations = previousSupportedOrientations
|
|
||||||
},
|
|
||||||
sendMessage: { [weak selfController] mode, parameters in
|
|
||||||
guard let selfController else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch mode {
|
|
||||||
case .generic:
|
|
||||||
selfController.controllerInteraction?.sendCurrentMessage(false, parameters?.effect.flatMap(ChatSendMessageEffect.init))
|
|
||||||
case .silently:
|
|
||||||
selfController.controllerInteraction?.sendCurrentMessage(true, parameters?.effect.flatMap(ChatSendMessageEffect.init))
|
|
||||||
case .whenOnline:
|
|
||||||
selfController.chatDisplayNode.sendCurrentMessage(scheduleTime: scheduleWhenOnlineTimestamp, messageEffect: parameters?.effect.flatMap(ChatSendMessageEffect.init)) { [weak selfController] in
|
|
||||||
guard let selfController else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
selfController.updateChatPresentationInterfaceState(animated: true, interactive: false, saveInterfaceState: selfController.presentationInterfaceState.subject != .scheduledMessages, {
|
|
||||||
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedForwardMessageIds(nil).withUpdatedForwardOptionsState(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))) }
|
|
||||||
})
|
|
||||||
selfController.openScheduledMessages()
|
|
||||||
}
|
}
|
||||||
}
|
selfController.supportedOrientations = previousSupportedOrientations
|
||||||
},
|
},
|
||||||
schedule: { [weak selfController] effect in
|
sendMessage: { [weak selfController] mode, parameters in
|
||||||
guard let selfController else {
|
guard let selfController else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
selfController.controllerInteraction?.scheduleCurrentMessage()
|
selfController.interfaceInteraction?.editMessage()
|
||||||
}, openPremiumPaywall: { [weak selfController] c in
|
},
|
||||||
guard let selfController else {
|
schedule: { _ in
|
||||||
return
|
}, openPremiumPaywall: { [weak selfController] c in
|
||||||
}
|
guard let selfController else {
|
||||||
selfController.push(c)
|
return
|
||||||
},
|
}
|
||||||
reactionItems: (!textInputView.text.isEmpty || mediaPreview != nil) ? effectItems : nil,
|
selfController.push(c)
|
||||||
availableMessageEffects: availableMessageEffects,
|
},
|
||||||
isPremium: hasPremium
|
reactionItems: nil,
|
||||||
)
|
availableMessageEffects: nil,
|
||||||
selfController.sendMessageActionsController = controller
|
isPremium: hasPremium
|
||||||
if layout.isNonExclusive {
|
)
|
||||||
selfController.present(controller, in: .window(.root))
|
selfController.sendMessageActionsController = controller
|
||||||
|
if layout.isNonExclusive {
|
||||||
|
selfController.present(controller, in: .window(.root))
|
||||||
|
} else {
|
||||||
|
selfController.presentInGlobalOverlay(controller, with: nil)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
selfController.presentInGlobalOverlay(controller, with: nil)
|
var sendWhenOnlineAvailable = false
|
||||||
|
if let presence = peerView.peerPresences[peer.id] as? TelegramUserPresence, case let .present(until) = presence.status {
|
||||||
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
|
if currentTime > until {
|
||||||
|
sendWhenOnlineAvailable = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if peer.id.namespace == Namespaces.Peer.CloudUser && peer.id.id._internalGetInt64Value() == 777000 {
|
||||||
|
sendWhenOnlineAvailable = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if sendWhenOnlineAvailable {
|
||||||
|
let _ = ApplicationSpecificNotice.incrementSendWhenOnlineTip(accountManager: selfController.context.sharedContext.accountManager, count: 4).startStandalone()
|
||||||
|
}
|
||||||
|
|
||||||
|
var mediaPreview: ChatSendMessageContextScreenMediaPreview?
|
||||||
|
if let videoRecorderValue = selfController.videoRecorderValue {
|
||||||
|
mediaPreview = videoRecorderValue.makeSendMessageContextPreview()
|
||||||
|
}
|
||||||
|
if let mediaDraftState = selfController.presentationInterfaceState.interfaceState.mediaDraftState {
|
||||||
|
if case let .audio(audio) = mediaDraftState {
|
||||||
|
mediaPreview = ChatSendAudioMessageContextPreview(
|
||||||
|
context: selfController.context,
|
||||||
|
presentationData: selfController.presentationData,
|
||||||
|
wallpaperBackgroundNode: selfController.chatDisplayNode.backgroundNode,
|
||||||
|
waveform: audio.waveform
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let controller = makeChatSendMessageActionSheetController(
|
||||||
|
context: selfController.context,
|
||||||
|
updatedPresentationData: selfController.updatedPresentationData,
|
||||||
|
peerId: selfController.presentationInterfaceState.chatLocation.peerId,
|
||||||
|
params: .sendMessage(SendMessageActionSheetControllerParams.SendMessage(
|
||||||
|
isScheduledMessages: false,
|
||||||
|
mediaPreview: mediaPreview,
|
||||||
|
mediaCaptionIsAbove: nil,
|
||||||
|
attachment: false,
|
||||||
|
canSendWhenOnline: sendWhenOnlineAvailable,
|
||||||
|
forwardMessageIds: selfController.presentationInterfaceState.interfaceState.forwardMessageIds ?? []
|
||||||
|
)),
|
||||||
|
hasEntityKeyboard: hasEntityKeyboard,
|
||||||
|
gesture: gesture,
|
||||||
|
sourceSendButton: node,
|
||||||
|
textInputView: textInputView,
|
||||||
|
emojiViewProvider: selfController.chatDisplayNode.textInputPanelNode?.emojiViewProvider,
|
||||||
|
wallpaperBackgroundNode: selfController.chatDisplayNode.backgroundNode,
|
||||||
|
completion: { [weak selfController] in
|
||||||
|
guard let selfController else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
selfController.supportedOrientations = previousSupportedOrientations
|
||||||
|
},
|
||||||
|
sendMessage: { [weak selfController] mode, parameters in
|
||||||
|
guard let selfController else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch mode {
|
||||||
|
case .generic:
|
||||||
|
selfController.controllerInteraction?.sendCurrentMessage(false, parameters?.effect.flatMap(ChatSendMessageEffect.init))
|
||||||
|
case .silently:
|
||||||
|
selfController.controllerInteraction?.sendCurrentMessage(true, parameters?.effect.flatMap(ChatSendMessageEffect.init))
|
||||||
|
case .whenOnline:
|
||||||
|
selfController.chatDisplayNode.sendCurrentMessage(scheduleTime: scheduleWhenOnlineTimestamp, messageEffect: parameters?.effect.flatMap(ChatSendMessageEffect.init)) { [weak selfController] in
|
||||||
|
guard let selfController else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
selfController.updateChatPresentationInterfaceState(animated: true, interactive: false, saveInterfaceState: selfController.presentationInterfaceState.subject != .scheduledMessages, {
|
||||||
|
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedForwardMessageIds(nil).withUpdatedForwardOptionsState(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))) }
|
||||||
|
})
|
||||||
|
selfController.openScheduledMessages()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
schedule: { [weak selfController] effect in
|
||||||
|
guard let selfController else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
selfController.controllerInteraction?.scheduleCurrentMessage()
|
||||||
|
}, openPremiumPaywall: { [weak selfController] c in
|
||||||
|
guard let selfController else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
selfController.push(c)
|
||||||
|
},
|
||||||
|
reactionItems: (!textInputView.text.isEmpty || mediaPreview != nil) ? effectItems : nil,
|
||||||
|
availableMessageEffects: availableMessageEffects,
|
||||||
|
isPremium: hasPremium
|
||||||
|
)
|
||||||
|
selfController.sendMessageActionsController = controller
|
||||||
|
if layout.isNonExclusive {
|
||||||
|
selfController.present(controller, in: .window(.root))
|
||||||
|
} else {
|
||||||
|
selfController.presentInGlobalOverlay(controller, with: nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode {
|
|||||||
|
|
||||||
self.sendButton.highligthedChanged = { [weak self] highlighted in
|
self.sendButton.highligthedChanged = { [weak self] highlighted in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if strongSelf.sendButtonHasApplyIcon || !strongSelf.sendButtonLongPressEnabled {
|
if !strongSelf.sendButtonLongPressEnabled {
|
||||||
if highlighted {
|
if highlighted {
|
||||||
strongSelf.sendContainerNode.layer.removeAnimation(forKey: "opacity")
|
strongSelf.sendContainerNode.layer.removeAnimation(forKey: "opacity")
|
||||||
strongSelf.sendContainerNode.alpha = 0.4
|
strongSelf.sendContainerNode.alpha = 0.4
|
||||||
@ -109,9 +109,7 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !strongSelf.sendButtonHasApplyIcon {
|
strongSelf.sendButtonLongPressed?(strongSelf, recognizer)
|
||||||
strongSelf.sendButtonLongPressed?(strongSelf, recognizer)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.micButtonPointerInteraction = PointerInteraction(view: self.micButton, style: .circle(36.0))
|
self.micButtonPointerInteraction = PointerInteraction(view: self.micButton, style: .circle(36.0))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user