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";
|
||||
|
||||
"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
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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: {
|
||||
|
@ -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)
|
||||
}
|
||||
//}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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))
|
||||
|
Loading…
x
Reference in New Issue
Block a user