[WIP] Edit message preview

This commit is contained in:
Isaac 2024-05-26 18:59:44 +04:00
parent 6ff4cd3681
commit 667aa0349a
16 changed files with 487 additions and 1603 deletions

View File

@ -12298,3 +12298,13 @@ Sorry for the inconvenience.";
"Stars.Transfer.Balance" = "Balance";
"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";

View File

@ -57,7 +57,7 @@ final class AttachmentTextInputActionButtonsNode: ASDisplayNode, ChatSendMessage
self.sendButton.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self {
if strongSelf.sendButtonHasApplyIcon || !strongSelf.sendButtonLongPressEnabled {
if !strongSelf.sendButtonLongPressEnabled {
if highlighted {
strongSelf.sendContainerNode.layer.removeAnimation(forKey: "opacity")
strongSelf.sendContainerNode.alpha = 0.4

View File

@ -1002,21 +1002,24 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
context: strongSelf.context,
updatedPresentationData: strongSelf.updatedPresentationData,
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,
gesture: gesture,
sourceSendButton: node,
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,
attachment: true,
canSendWhenOnline: sendWhenOnlineAvailable,
completion: {
},
sendMessage: { [weak textInputPanelNode] mode, messageEffect in

View File

@ -11,169 +11,57 @@ import TextFormat
import ReactionSelectionNode
import WallpaperBackgroundNode
private final class ChatSendMessageActionSheetControllerImpl: ViewController, ChatSendMessageActionSheetController {
private var controllerNode: ChatSendMessageActionSheetControllerNode {
return self.displayNode as! ChatSendMessageActionSheetControllerNode
}
private let context: AccountContext
private let peerId: EnginePeer.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
public enum SendMessageActionSheetControllerParams {
public final class SendMessage {
public let isScheduledMessages: Bool
public let mediaPreview: ChatSendMessageContextScreenMediaPreview?
public let mediaCaptionIsAbove: (Bool, (Bool) -> Void)?
public let attachment: Bool
public let canSendWhenOnline: Bool
public let forwardMessageIds: [EngineMessage.Id]
self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
super.init(navigationBarPresentationData: nil)
self.blocksBackgroundWhenInOverlay = true
self.presentationDataDisposable = ((updatedPresentationData?.signal ?? context.sharedContext.presentationData)
|> deliverOnMainQueue).startStrict(next: { [weak self] presentationData in
if let strongSelf = self {
strongSelf.presentationData = presentationData
if strongSelf.isNodeLoaded {
strongSelf.controllerNode.updatePresentationData(presentationData)
}
}
}).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()
public init(
isScheduledMessages: Bool,
mediaPreview: ChatSendMessageContextScreenMediaPreview?,
mediaCaptionIsAbove: (Bool, (Bool) -> Void)?,
attachment: Bool,
canSendWhenOnline: Bool,
forwardMessageIds: [EngineMessage.Id]
) {
self.isScheduledMessages = isScheduledMessages
self.mediaPreview = mediaPreview
self.mediaCaptionIsAbove = mediaCaptionIsAbove
self.attachment = attachment
self.canSendWhenOnline = canSendWhenOnline
self.forwardMessageIds = forwardMessageIds
}
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
self.validLayout = layout
public final class EditMessage {
public let messages: [EngineMessage]
public let mediaPreview: ChatSendMessageContextScreenMediaPreview?
super.containerLayoutUpdated(layout, transition: transition)
self.controllerNode.containerLayoutUpdated(layout, transition: transition)
public init(messages: [EngineMessage], mediaPreview: ChatSendMessageContextScreenMediaPreview?) {
self.messages = messages
self.mediaPreview = mediaPreview
}
}
override public func dismiss(completion: (() -> Void)? = nil) {
self.dismiss(cancel: true)
}
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)
})
}
case sendMessage(SendMessage)
case editMessage(EditMessage)
}
public func makeChatSendMessageActionSheetController(
context: AccountContext,
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
peerId: EnginePeer.Id?,
isScheduledMessages: Bool = false,
forwardMessageIds: [EngineMessage.Id]?,
params: SendMessageActionSheetControllerParams,
hasEntityKeyboard: Bool,
gesture: ContextGesture,
sourceSendButton: ASDisplayNode,
textInputView: UITextView,
mediaPreview: ChatSendMessageContextScreenMediaPreview? = nil,
mediaCaptionIsAbove: (Bool, (Bool) -> Void)? = nil,
emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?,
wallpaperBackgroundNode: WallpaperBackgroundNode? = nil,
attachment: Bool = false,
canSendWhenOnline: Bool,
completion: @escaping () -> Void,
sendMessage: @escaping (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.SendParameters?) -> Void,
schedule: @escaping (ChatSendMessageActionSheetController.SendParameters?) -> Void,
@ -182,43 +70,17 @@ public func makeChatSendMessageActionSheetController(
availableMessageEffects: AvailableMessageEffects? = nil,
isPremium: Bool = false
) -> 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(
context: context,
updatedPresentationData: updatedPresentationData,
peerId: peerId,
isScheduledMessages: isScheduledMessages,
forwardMessageIds: forwardMessageIds,
params: params,
hasEntityKeyboard: hasEntityKeyboard,
gesture: gesture,
sourceSendButton: sourceSendButton,
textInputView: textInputView,
mediaPreview: mediaPreview,
mediaCaptionIsAbove: mediaCaptionIsAbove,
emojiViewProvider: emojiViewProvider,
wallpaperBackgroundNode: wallpaperBackgroundNode,
attachment: attachment,
canSendWhenOnline: canSendWhenOnline,
completion: completion,
sendMessage: sendMessage,
schedule: schedule,

View File

@ -56,18 +56,13 @@ final class ChatSendMessageContextScreenComponent: Component {
let context: AccountContext
let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
let peerId: EnginePeer.Id?
let isScheduledMessages: Bool
let forwardMessageIds: [EngineMessage.Id]?
let params: SendMessageActionSheetControllerParams
let hasEntityKeyboard: Bool
let gesture: ContextGesture
let sourceSendButton: ASDisplayNode
let textInputView: UITextView
let mediaPreview: ChatSendMessageContextScreenMediaPreview?
let mediaCaptionIsAbove: (Bool, (Bool) -> Void)?
let emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?
let wallpaperBackgroundNode: WallpaperBackgroundNode?
let attachment: Bool
let canSendWhenOnline: Bool
let completion: () -> Void
let sendMessage: (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.SendParameters?) -> Void
let schedule: (ChatSendMessageActionSheetController.SendParameters?) -> Void
@ -80,18 +75,13 @@ final class ChatSendMessageContextScreenComponent: Component {
context: AccountContext,
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?,
peerId: EnginePeer.Id?,
isScheduledMessages: Bool,
forwardMessageIds: [EngineMessage.Id]?,
params: SendMessageActionSheetControllerParams,
hasEntityKeyboard: Bool,
gesture: ContextGesture,
sourceSendButton: ASDisplayNode,
textInputView: UITextView,
mediaPreview: ChatSendMessageContextScreenMediaPreview?,
mediaCaptionIsAbove: (Bool, (Bool) -> Void)?,
emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?,
wallpaperBackgroundNode: WallpaperBackgroundNode?,
attachment: Bool,
canSendWhenOnline: Bool,
completion: @escaping () -> Void,
sendMessage: @escaping (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.SendParameters?) -> Void,
schedule: @escaping (ChatSendMessageActionSheetController.SendParameters?) -> Void,
@ -103,18 +93,13 @@ final class ChatSendMessageContextScreenComponent: Component {
self.context = context
self.updatedPresentationData = updatedPresentationData
self.peerId = peerId
self.isScheduledMessages = isScheduledMessages
self.forwardMessageIds = forwardMessageIds
self.params = params
self.hasEntityKeyboard = hasEntityKeyboard
self.gesture = gesture
self.sourceSendButton = sourceSendButton
self.textInputView = textInputView
self.mediaPreview = mediaPreview
self.mediaCaptionIsAbove = mediaCaptionIsAbove
self.emojiViewProvider = emojiViewProvider
self.wallpaperBackgroundNode = wallpaperBackgroundNode
self.attachment = attachment
self.canSendWhenOnline = canSendWhenOnline
self.completion = completion
self.sendMessage = sendMessage
self.schedule = schedule
@ -331,7 +316,14 @@ final class ChatSendMessageContextScreenComponent: Component {
let themeUpdated = environment.theme !== self.environment?.theme
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
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
if let attributedText = component.textInputView.attributedText {
@ -391,7 +391,14 @@ final class ChatSendMessageContextScreenComponent: Component {
if let current = self.sendButton {
sendButton = current
} 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.addTarget(self, action: #selector(self.onSendButtonPressed), for: .touchUpInside)
/*if let snapshotView = component.sourceSendButton.view.snapshotView(afterScreenUpdates: false) {
@ -427,22 +434,43 @@ final class ChatSendMessageContextScreenComponent: Component {
var reminders = false
var isSecret = false
var canSchedule = false
if let peerId = component.peerId {
reminders = peerId == component.context.account.peerId
isSecret = peerId.namespace == Namespaces.Peer.SecretChat
canSchedule = !isSecret
}
if component.isScheduledMessages {
canSchedule = false
switch component.params {
case let .sendMessage(sendMessage):
if let peerId = component.peerId {
reminders = peerId == component.context.account.peerId
isSecret = peerId.namespace == Namespaces.Peer.SecretChat
canSchedule = !isSecret
}
if sendMessage.isScheduledMessages {
canSchedule = false
}
case .editMessage:
break
}
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
items.append(.action(ContextMenuActionItem(
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
return nil
}, iconAnimation: ContextMenuActionItem.IconAnimation(
@ -452,7 +480,12 @@ final class ChatSendMessageContextScreenComponent: Component {
return
}
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 {
self.state?.updated(transition: .spring(duration: 0.35))
}
@ -461,34 +494,14 @@ final class ChatSendMessageContextScreenComponent: Component {
items.append(.separator)
}
if !reminders {
items.append(.action(ContextMenuActionItem(
id: AnyHashable("silent"),
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 {
switch component.params {
case let.sendMessage(sendMessage):
if !reminders {
items.append(.action(ContextMenuActionItem(
id: AnyHashable("whenOnline"),
text: environment.strings.Conversation_SendMessage_SendWhenOnline,
id: AnyHashable("silent"),
text: environment.strings.Conversation_SendMessage_SendSilently,
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
guard let self, let component = self.component else {
return
@ -500,18 +513,62 @@ final class ChatSendMessageContextScreenComponent: Component {
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()
}
)))
}
}
if canSchedule {
case .editMessage:
items.append(.action(ContextMenuActionItem(
id: AnyHashable("schedule"),
text: reminders ? environment.strings.Conversation_SendMessage_SetReminder: environment.strings.Conversation_SendMessage_ScheduleMessage,
id: AnyHashable("silent"),
text: environment.strings.Chat_SendMessageMenu_EditMessage,
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
guard let self, let component = self.component else {
return
@ -519,11 +576,11 @@ final class ChatSendMessageContextScreenComponent: Component {
self.animateOutToEmpty = true
let sendParameters = ChatSendMessageActionSheetController.SendParameters(
effect: self.selectedMessageEffect.flatMap({ ChatSendMessageActionSheetController.SendParameters.Effect(id: $0.id) }),
effect: nil,
textIsAboveMedia: self.mediaCaptionIsAbove
)
component.schedule(sendParameters)
component.sendMessage(.generic, sendParameters)
self.environment?.controller()?.dismiss()
}
)))
@ -636,7 +693,7 @@ final class ChatSendMessageContextScreenComponent: Component {
let messageTextInsets = sourceMessageTextInsets
let messageItemViewContainerSize: CGSize
if let mediaPreview = component.mediaPreview {
if let mediaPreview {
switch mediaPreview.layoutType {
case .message, .media:
messageItemViewContainerSize = CGSize(width: availableSize.width - 16.0 - 40.0, height: availableSize.height)
@ -654,7 +711,7 @@ final class ChatSendMessageContextScreenComponent: Component {
textString: textString,
sourceTextInputView: component.textInputView as? ChatInputTextView,
emojiViewProvider: component.emojiViewProvider,
sourceMediaPreview: component.mediaPreview,
sourceMediaPreview: mediaPreview,
mediaCaptionIsAbove: self.mediaCaptionIsAbove,
textInsets: messageTextInsets,
explicitBackgroundSize: explicitMessageBackgroundSize,
@ -671,7 +728,6 @@ final class ChatSendMessageContextScreenComponent: Component {
if let current = self.reactionContextNode {
reactionContextNode = current
} else {
//TODO:localize
reactionContextNode = ReactionContextNode(
context: component.context,
animationCache: component.context.animationCache,
@ -692,7 +748,7 @@ final class ChatSendMessageContextScreenComponent: Component {
return ReactionContextItem.reaction(item: item, icon: icon)
},
selectedItems: Set(),
title: "Add an animated effect",
title: presentationData.strings.Chat_MessageEffectMenu_TitleAddEffect,
reactionsLocked: false,
alwaysAllowPremiumReactions: false,
allPresetReactionsAreAvailable: true,
@ -937,13 +993,12 @@ final class ChatSendMessageContextScreenComponent: Component {
}
}
//TODO:localize
let presentationData = component.updatedPresentationData?.initial ?? component.context.sharedContext.currentPresentationData.with({ $0 })
self.environment?.controller()?.present(UndoOverlayController(
presentationData: presentationData,
content: .premiumPaywall(
title: nil,
text: "Subscribe to [Telegram Premium]() to add this animated effect.",
text: presentationData.strings.Chat_SendMessageMenu_ToastPremiumRequired_Text,
customUndoText: nil,
timeout: 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)
if let mediaPreview = component.mediaPreview {
if let mediaPreview {
switch mediaPreview.layoutType {
case .message, .media:
break
@ -1012,7 +1067,7 @@ final class ChatSendMessageContextScreenComponent: Component {
readySendButtonFrame.origin.y -= inputCoverOverflow
}
if let mediaPreview = component.mediaPreview {
if let mediaPreview {
switch mediaPreview.layoutType {
case .message, .media:
break
@ -1034,7 +1089,7 @@ final class ChatSendMessageContextScreenComponent: Component {
let sendButtonFrame: CGRect
switch self.presentationAnimationState {
case .initial:
if component.mediaPreview != nil {
if mediaPreview != nil {
messageItemFrame = readyMessageItemFrame
actionsStackFrame = readyActionsStackFrame
} else {
@ -1048,7 +1103,7 @@ final class ChatSendMessageContextScreenComponent: Component {
actionsStackFrame = readyActionsStackFrame
sendButtonFrame = readySendButtonFrame
} else {
if component.mediaPreview != nil {
if mediaPreview != nil {
messageItemFrame = readyMessageItemFrame
actionsStackFrame = readyActionsStackFrame
} else {
@ -1066,7 +1121,7 @@ final class ChatSendMessageContextScreenComponent: Component {
transition.setFrame(view: messageItemView, frame: messageItemFrame)
transition.setAlpha(view: messageItemView, alpha: isMessageVisible ? 1.0 : 0.0)
messageItemView.updateClippingRect(
sourceMediaPreview: component.mediaPreview,
sourceMediaPreview: mediaPreview,
isAnimatedIn: self.presentationAnimationState.key == .animatedIn,
localFrame: messageItemFrame,
containerSize: availableSize,
@ -1120,7 +1175,7 @@ final class ChatSendMessageContextScreenComponent: Component {
let reactionContextY = environment.statusBarHeight
let size = availableSize
var reactionsAnchorRect = messageItemFrame
if let mediaPreview = component.mediaPreview {
if let mediaPreview {
switch mediaPreview.layoutType {
case .message, .media:
reactionsAnchorRect.size.width += 100.0
@ -1193,14 +1248,14 @@ final class ChatSendMessageContextScreenComponent: Component {
guard let component = self.component else {
return
}
if component.mediaPreview == nil {
if mediaPreview == nil {
component.textInputView.isHidden = true
}
component.sourceSendButton.isHidden = true
})
}
} else {
if component.mediaPreview == nil {
if mediaPreview == nil {
component.textInputView.isHidden = true
}
component.sourceSendButton.isHidden = true
@ -1212,7 +1267,7 @@ final class ChatSendMessageContextScreenComponent: Component {
backgroundAlpha = 0.0
if self.animateOutToEmpty {
if component.mediaPreview == nil {
if mediaPreview == nil {
component.textInputView.isHidden = false
}
component.sourceSendButton.isHidden = false
@ -1234,7 +1289,7 @@ final class ChatSendMessageContextScreenComponent: Component {
if !self.performedActionsOnAnimateOut {
self.performedActionsOnAnimateOut = true
if let component = self.component, !self.animateOutToEmpty {
if component.mediaPreview == nil {
if mediaPreview == nil {
component.textInputView.isHidden = false
}
component.sourceSendButton.isHidden = false
@ -1277,18 +1332,13 @@ public class ChatSendMessageContextScreen: ViewControllerComponentContainer, Cha
context: AccountContext,
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?,
peerId: EnginePeer.Id?,
isScheduledMessages: Bool,
forwardMessageIds: [EngineMessage.Id]?,
params: SendMessageActionSheetControllerParams,
hasEntityKeyboard: Bool,
gesture: ContextGesture,
sourceSendButton: ASDisplayNode,
textInputView: UITextView,
mediaPreview: ChatSendMessageContextScreenMediaPreview?,
mediaCaptionIsAbove: (Bool, (Bool) -> Void)?,
emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?,
wallpaperBackgroundNode: WallpaperBackgroundNode?,
attachment: Bool,
canSendWhenOnline: Bool,
completion: @escaping () -> Void,
sendMessage: @escaping (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.SendParameters?) -> Void,
schedule: @escaping (ChatSendMessageActionSheetController.SendParameters?) -> Void,
@ -1305,18 +1355,13 @@ public class ChatSendMessageContextScreen: ViewControllerComponentContainer, Cha
context: context,
updatedPresentationData: updatedPresentationData,
peerId: peerId,
isScheduledMessages: isScheduledMessages,
forwardMessageIds: forwardMessageIds,
params: params,
hasEntityKeyboard: hasEntityKeyboard,
gesture: gesture,
sourceSendButton: sourceSendButton,
textInputView: textInputView,
mediaPreview: mediaPreview,
mediaCaptionIsAbove: mediaCaptionIsAbove,
emojiViewProvider: emojiViewProvider,
wallpaperBackgroundNode: wallpaperBackgroundNode,
attachment: attachment,
canSendWhenOnline: canSendWhenOnline,
completion: completion,
sendMessage: sendMessage,
schedule: schedule,

View File

@ -18,6 +18,7 @@ import MultilineTextWithEntitiesComponent
import ReactionButtonListComponent
import MultilineTextComponent
import ChatInputTextNode
import EmojiTextAttachmentView
private final class EffectIcon: Component {
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 {
private let backgroundWallpaperNode: ChatMessageBubbleBackdrop
private let backgroundNode: ChatMessageBackground

View File

@ -19,6 +19,13 @@ import ActivityIndicator
import RadialStatusNode
final class SendButton: HighlightTrackingButton {
enum Kind {
case send
case edit
}
private let kind: Kind
private let containerView: UIView
private var backgroundContent: WallpaperBubbleBackgroundNode?
private let backgroundLayer: SimpleLayer
@ -28,7 +35,9 @@ final class SendButton: HighlightTrackingButton {
private var didProcessSourceCustomContent: Bool = false
private var sourceCustomContentView: UIView?
override init(frame: CGRect) {
init(kind: Kind) {
self.kind = kind
self.containerView = UIView()
self.containerView.isUserInteractionEnabled = false
@ -37,7 +46,7 @@ final class SendButton: HighlightTrackingButton {
self.iconView = UIImageView()
self.iconView.isUserInteractionEnabled = false
super.init(frame: frame)
super.init(frame: CGRect())
self.containerView.clipsToBounds = true
self.addSubview(self.containerView)
@ -100,7 +109,12 @@ final class SendButton: HighlightTrackingButton {
}
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 {

View File

@ -2502,8 +2502,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
mediaCaptionIsAbove = interaction.captionIsAboveMedia
}
//TODO:localize
items.append(.action(ContextMenuActionItem(text: mediaCaptionIsAbove ? "Move Caption Down" : "Move Caption Up", icon: { _ in return nil }, iconAnimation: ContextMenuActionItem.IconAnimation(
items.append(.action(ContextMenuActionItem(text: mediaCaptionIsAbove ? strings.Chat_SendMessageMenu_MoveCaptionDown : strings.Chat_SendMessageMenu_MoveCaptionUp, icon: { _ in return nil }, iconAnimation: ContextMenuActionItem.IconAnimation(
name: !mediaCaptionIsAbove ? "message_preview_sort_above" : "message_preview_sort_below"
), action: { [weak self] _, f in
f(.default)

View File

@ -1796,6 +1796,9 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate {
return
}
let presentationData = self.context.sharedContext.currentPresentationData.with({ $0 })
let strings = presentationData.strings
switch query {
case .none:
self.emojiSearchDisposable.set(nil)
@ -1929,7 +1932,7 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate {
} else {
resultGroupIndexById[groupId] = resultGroups.count
//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 {
resultGroupIndexById[groupId] = resultGroups.count
//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]))
}
}
}

View File

@ -2256,8 +2256,7 @@ public extension EmojiPagerContentComponent {
itemGroups[groupIndex].items.append(resultItem)
} else {
itemGroupIndexById[groupId] = itemGroups.count
//TODO:localize
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]))
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]))
}
}
}

View File

@ -691,24 +691,44 @@ final class PeerSelectionControllerNode: ASDisplayNode {
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: {
}, 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)
let controller = makeChatSendMessageActionSheetController(
context: strongSelf.context,
peerId: strongSelf.presentationInterfaceState.chatLocation.peerId,
params: .sendMessage(SendMessageActionSheetControllerParams.SendMessage(
isScheduledMessages: false,
mediaPreview: nil,
mediaCaptionIsAbove: nil,
attachment: false,
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)
}, openScheduledMessages: {
}, openPeersNearby: {

View File

@ -2918,13 +2918,13 @@ final class StoryItemSetContainerSendMessage {
return
}
if !hashtag.isEmpty {
if "".isEmpty {
/*if !"".isEmpty {
let searchController = component.context.sharedContext.makeStorySearchController(context: component.context, query: hashtag)
navigationController.pushViewController(searchController)
} else {
} else {*/
let searchController = component.context.sharedContext.makeHashtagSearchController(context: component.context, peer: peer.flatMap(EnginePeer.init), query: hashtag, all: true)
navigationController.pushViewController(searchController)
}
//}
}
}))
}

View File

@ -1276,8 +1276,7 @@ extension ChatControllerImpl {
strongSelf.chatDisplayNode.historyNode.scrollToEndOfHistory()
case let .businessLinkSetup(link):
if messages.count > 1 {
//TODO:localize
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: "The message text limit is 4096 characters", actions: [
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.BusinessLink_AlertTextLimitText, actions: [
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})
]), in: .window(.root))

View File

@ -55,106 +55,178 @@ func chatMessageDisplaySendMessageOptions(selfController: ChatControllerImpl, no
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(
selfController.context.account.viewTracker.peerView(peerId) |> take(1),
effectItems,
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 {
return
}
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 let _ = selfController.presentationInterfaceState.interfaceState.editMessage {
if editMessages.isEmpty {
return
}
}
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,
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()
let controller = makeChatSendMessageActionSheetController(
context: selfController.context,
updatedPresentationData: selfController.updatedPresentationData,
peerId: selfController.presentationInterfaceState.chatLocation.peerId,
params: .editMessage(SendMessageActionSheetControllerParams.EditMessage(
messages: editMessages,
mediaPreview: nil
)),
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
}
}
},
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))
selfController.supportedOrientations = previousSupportedOrientations
},
sendMessage: { [weak selfController] mode, parameters in
guard let selfController else {
return
}
selfController.interfaceInteraction?.editMessage()
},
schedule: { _ in
}, openPremiumPaywall: { [weak selfController] c in
guard let selfController else {
return
}
selfController.push(c)
},
reactionItems: nil,
availableMessageEffects: nil,
isPremium: hasPremium
)
selfController.sendMessageActionsController = controller
if layout.isNonExclusive {
selfController.present(controller, in: .window(.root))
} else {
selfController.presentInGlobalOverlay(controller, with: nil)
}
} 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)
}
}
})
}

View File

@ -67,7 +67,7 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode {
self.sendButton.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self {
if strongSelf.sendButtonHasApplyIcon || !strongSelf.sendButtonLongPressEnabled {
if !strongSelf.sendButtonLongPressEnabled {
if highlighted {
strongSelf.sendContainerNode.layer.removeAnimation(forKey: "opacity")
strongSelf.sendContainerNode.alpha = 0.4
@ -109,9 +109,7 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode {
guard let strongSelf = self else {
return
}
if !strongSelf.sendButtonHasApplyIcon {
strongSelf.sendButtonLongPressed?(strongSelf, recognizer)
}
strongSelf.sendButtonLongPressed?(strongSelf, recognizer)
}
self.micButtonPointerInteraction = PointerInteraction(view: self.micButton, style: .circle(36.0))