mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] Message preview
This commit is contained in:
parent
b40a6c413e
commit
5edecdb686
@ -998,6 +998,8 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
sourceSendButton: node,
|
sourceSendButton: node,
|
||||||
textInputView: textInputNode.textView,
|
textInputView: textInputNode.textView,
|
||||||
mediaPreview: mediaPreview,
|
mediaPreview: mediaPreview,
|
||||||
|
mediaCaptionIsAbove: (false, { _ in
|
||||||
|
}),
|
||||||
emojiViewProvider: textInputPanelNode.emojiViewProvider,
|
emojiViewProvider: textInputPanelNode.emojiViewProvider,
|
||||||
attachment: true,
|
attachment: true,
|
||||||
canSendWhenOnline: sendWhenOnlineAvailable,
|
canSendWhenOnline: sendWhenOnlineAvailable,
|
||||||
|
@ -185,6 +185,7 @@ public func makeChatSendMessageActionSheetController(
|
|||||||
sourceSendButton: ASDisplayNode,
|
sourceSendButton: ASDisplayNode,
|
||||||
textInputView: UITextView,
|
textInputView: UITextView,
|
||||||
mediaPreview: ChatSendMessageContextScreenMediaPreview? = nil,
|
mediaPreview: ChatSendMessageContextScreenMediaPreview? = nil,
|
||||||
|
mediaCaptionIsAbove: (Bool, (Bool) -> Void)? = nil,
|
||||||
emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?,
|
emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?,
|
||||||
wallpaperBackgroundNode: WallpaperBackgroundNode? = nil,
|
wallpaperBackgroundNode: WallpaperBackgroundNode? = nil,
|
||||||
attachment: Bool = false,
|
attachment: Bool = false,
|
||||||
@ -228,6 +229,7 @@ public func makeChatSendMessageActionSheetController(
|
|||||||
sourceSendButton: sourceSendButton,
|
sourceSendButton: sourceSendButton,
|
||||||
textInputView: textInputView,
|
textInputView: textInputView,
|
||||||
mediaPreview: mediaPreview,
|
mediaPreview: mediaPreview,
|
||||||
|
mediaCaptionIsAbove: mediaCaptionIsAbove,
|
||||||
emojiViewProvider: emojiViewProvider,
|
emojiViewProvider: emojiViewProvider,
|
||||||
wallpaperBackgroundNode: wallpaperBackgroundNode,
|
wallpaperBackgroundNode: wallpaperBackgroundNode,
|
||||||
attachment: attachment,
|
attachment: attachment,
|
||||||
|
@ -63,6 +63,7 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
let sourceSendButton: ASDisplayNode
|
let sourceSendButton: ASDisplayNode
|
||||||
let textInputView: UITextView
|
let textInputView: UITextView
|
||||||
let mediaPreview: ChatSendMessageContextScreenMediaPreview?
|
let mediaPreview: ChatSendMessageContextScreenMediaPreview?
|
||||||
|
let mediaCaptionIsAbove: (Bool, (Bool) -> Void)?
|
||||||
let emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?
|
let emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?
|
||||||
let wallpaperBackgroundNode: WallpaperBackgroundNode?
|
let wallpaperBackgroundNode: WallpaperBackgroundNode?
|
||||||
let attachment: Bool
|
let attachment: Bool
|
||||||
@ -85,6 +86,7 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
sourceSendButton: ASDisplayNode,
|
sourceSendButton: ASDisplayNode,
|
||||||
textInputView: UITextView,
|
textInputView: UITextView,
|
||||||
mediaPreview: ChatSendMessageContextScreenMediaPreview?,
|
mediaPreview: ChatSendMessageContextScreenMediaPreview?,
|
||||||
|
mediaCaptionIsAbove: (Bool, (Bool) -> Void)?,
|
||||||
emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?,
|
emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?,
|
||||||
wallpaperBackgroundNode: WallpaperBackgroundNode?,
|
wallpaperBackgroundNode: WallpaperBackgroundNode?,
|
||||||
attachment: Bool,
|
attachment: Bool,
|
||||||
@ -106,6 +108,7 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
self.sourceSendButton = sourceSendButton
|
self.sourceSendButton = sourceSendButton
|
||||||
self.textInputView = textInputView
|
self.textInputView = textInputView
|
||||||
self.mediaPreview = mediaPreview
|
self.mediaPreview = mediaPreview
|
||||||
|
self.mediaCaptionIsAbove = mediaCaptionIsAbove
|
||||||
self.emojiViewProvider = emojiViewProvider
|
self.emojiViewProvider = emojiViewProvider
|
||||||
self.wallpaperBackgroundNode = wallpaperBackgroundNode
|
self.wallpaperBackgroundNode = wallpaperBackgroundNode
|
||||||
self.attachment = attachment
|
self.attachment = attachment
|
||||||
@ -160,6 +163,8 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
private weak var state: EmptyComponentState?
|
private weak var state: EmptyComponentState?
|
||||||
private var isUpdating: Bool = false
|
private var isUpdating: Bool = false
|
||||||
|
|
||||||
|
private var mediaCaptionIsAbove: Bool = false
|
||||||
|
|
||||||
private let messageEffectDisposable = MetaDisposable()
|
private let messageEffectDisposable = MetaDisposable()
|
||||||
private var selectedMessageEffect: AvailableMessageEffects.MessageEffect?
|
private var selectedMessageEffect: AvailableMessageEffects.MessageEffect?
|
||||||
private var standaloneReactionAnimation: AnimatedStickerNode?
|
private var standaloneReactionAnimation: AnimatedStickerNode?
|
||||||
@ -278,6 +283,8 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
let themeUpdated = environment.theme !== self.environment?.theme
|
let themeUpdated = environment.theme !== self.environment?.theme
|
||||||
|
|
||||||
if self.component == nil {
|
if self.component == nil {
|
||||||
|
self.mediaCaptionIsAbove = component.mediaCaptionIsAbove?.0 ?? false
|
||||||
|
|
||||||
component.gesture.externalUpdated = { [weak self] view, location in
|
component.gesture.externalUpdated = { [weak self] view, location in
|
||||||
guard let self, let actionsStackNode = self.actionsStackNode else {
|
guard let self, let actionsStackNode = self.actionsStackNode else {
|
||||||
return
|
return
|
||||||
@ -345,9 +352,103 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
sendButtonScale = 1.0
|
sendButtonScale = 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
var items: [ContextMenuItem] = []
|
||||||
|
if component.mediaCaptionIsAbove != nil {
|
||||||
|
//TODO:localize
|
||||||
|
let mediaCaptionIsAbove = self.mediaCaptionIsAbove
|
||||||
|
items.append(.action(ContextMenuActionItem(
|
||||||
|
id: AnyHashable("captionPosition"),
|
||||||
|
text: mediaCaptionIsAbove ? "Move Caption Down" : "Move Caption Up",
|
||||||
|
icon: { _ in
|
||||||
|
return nil
|
||||||
|
}, iconAnimation: ContextMenuActionItem.IconAnimation(
|
||||||
|
name: !mediaCaptionIsAbove ? "message_preview_sort_above" : "message_preview_sort_below"
|
||||||
|
), action: { [weak self] _, _ in
|
||||||
|
guard let self, let component = self.component else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.mediaCaptionIsAbove = !self.mediaCaptionIsAbove
|
||||||
|
component.mediaCaptionIsAbove?.1(self.mediaCaptionIsAbove)
|
||||||
|
if !self.isUpdating {
|
||||||
|
self.state?.updated(transition: .spring(duration: 0.35))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
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
|
||||||
|
component.sendMessage(.silently, self.selectedMessageEffect.flatMap({ ChatSendMessageActionSheetController.MessageEffect(id: $0.id) }))
|
||||||
|
self.environment?.controller()?.dismiss()
|
||||||
|
}
|
||||||
|
)))
|
||||||
|
|
||||||
|
if component.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
|
||||||
|
component.sendMessage(.whenOnline, self.selectedMessageEffect.flatMap({ ChatSendMessageActionSheetController.MessageEffect(id: $0.id) }))
|
||||||
|
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
|
||||||
|
component.schedule(self.selectedMessageEffect.flatMap({ ChatSendMessageActionSheetController.MessageEffect(id: $0.id) }))
|
||||||
|
self.environment?.controller()?.dismiss()
|
||||||
|
}
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
let actionsStackNode: ContextControllerActionsStackNode
|
let actionsStackNode: ContextControllerActionsStackNode
|
||||||
if let current = self.actionsStackNode {
|
if let current = self.actionsStackNode {
|
||||||
actionsStackNode = current
|
actionsStackNode = current
|
||||||
|
|
||||||
|
actionsStackNode.replace(item: ContextControllerActionsListStackItem(
|
||||||
|
id: AnyHashable("items"),
|
||||||
|
items: items,
|
||||||
|
reactionItems: nil,
|
||||||
|
tip: nil,
|
||||||
|
tipSignal: .single(nil),
|
||||||
|
dismissed: nil
|
||||||
|
), animated: !transition.animation.isImmediate)
|
||||||
} else {
|
} else {
|
||||||
actionsStackNode = ContextControllerActionsStackNode(
|
actionsStackNode = ContextControllerActionsStackNode(
|
||||||
getController: {
|
getController: {
|
||||||
@ -366,69 +467,9 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
)
|
)
|
||||||
actionsStackNode.layer.anchorPoint = CGPoint(x: 1.0, y: 0.0)
|
actionsStackNode.layer.anchorPoint = CGPoint(x: 1.0, y: 0.0)
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
var items: [ContextMenuItem] = []
|
|
||||||
if !reminders {
|
|
||||||
items.append(.action(ContextMenuActionItem(
|
|
||||||
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
|
|
||||||
component.sendMessage(.silently, self.selectedMessageEffect.flatMap({ ChatSendMessageActionSheetController.MessageEffect(id: $0.id) }))
|
|
||||||
self.environment?.controller()?.dismiss()
|
|
||||||
}
|
|
||||||
)))
|
|
||||||
|
|
||||||
if component.canSendWhenOnline && canSchedule {
|
|
||||||
items.append(.action(ContextMenuActionItem(
|
|
||||||
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
|
|
||||||
component.sendMessage(.whenOnline, self.selectedMessageEffect.flatMap({ ChatSendMessageActionSheetController.MessageEffect(id: $0.id) }))
|
|
||||||
self.environment?.controller()?.dismiss()
|
|
||||||
}
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if canSchedule {
|
|
||||||
items.append(.action(ContextMenuActionItem(
|
|
||||||
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
|
|
||||||
component.schedule(self.selectedMessageEffect.flatMap({ ChatSendMessageActionSheetController.MessageEffect(id: $0.id) }))
|
|
||||||
self.environment?.controller()?.dismiss()
|
|
||||||
}
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
|
|
||||||
actionsStackNode.push(
|
actionsStackNode.push(
|
||||||
item: ContextControllerActionsListStackItem(
|
item: ContextControllerActionsListStackItem(
|
||||||
id: nil,
|
id: AnyHashable("items"),
|
||||||
items: items,
|
items: items,
|
||||||
reactionItems: nil,
|
reactionItems: nil,
|
||||||
tip: nil,
|
tip: nil,
|
||||||
@ -505,6 +546,7 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||||||
sourceTextInputView: component.textInputView as? ChatInputTextView,
|
sourceTextInputView: component.textInputView as? ChatInputTextView,
|
||||||
emojiViewProvider: component.emojiViewProvider,
|
emojiViewProvider: component.emojiViewProvider,
|
||||||
sourceMediaPreview: component.mediaPreview,
|
sourceMediaPreview: component.mediaPreview,
|
||||||
|
mediaCaptionIsAbove: self.mediaCaptionIsAbove,
|
||||||
textInsets: messageTextInsets,
|
textInsets: messageTextInsets,
|
||||||
explicitBackgroundSize: explicitMessageBackgroundSize,
|
explicitBackgroundSize: explicitMessageBackgroundSize,
|
||||||
maxTextWidth: localSourceTextInputViewFrame.width,
|
maxTextWidth: localSourceTextInputViewFrame.width,
|
||||||
@ -1093,6 +1135,7 @@ public class ChatSendMessageContextScreen: ViewControllerComponentContainer, Cha
|
|||||||
sourceSendButton: ASDisplayNode,
|
sourceSendButton: ASDisplayNode,
|
||||||
textInputView: UITextView,
|
textInputView: UITextView,
|
||||||
mediaPreview: ChatSendMessageContextScreenMediaPreview?,
|
mediaPreview: ChatSendMessageContextScreenMediaPreview?,
|
||||||
|
mediaCaptionIsAbove: (Bool, (Bool) -> Void)?,
|
||||||
emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?,
|
emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?,
|
||||||
wallpaperBackgroundNode: WallpaperBackgroundNode?,
|
wallpaperBackgroundNode: WallpaperBackgroundNode?,
|
||||||
attachment: Bool,
|
attachment: Bool,
|
||||||
@ -1119,6 +1162,7 @@ public class ChatSendMessageContextScreen: ViewControllerComponentContainer, Cha
|
|||||||
sourceSendButton: sourceSendButton,
|
sourceSendButton: sourceSendButton,
|
||||||
textInputView: textInputView,
|
textInputView: textInputView,
|
||||||
mediaPreview: mediaPreview,
|
mediaPreview: mediaPreview,
|
||||||
|
mediaCaptionIsAbove: mediaCaptionIsAbove,
|
||||||
emojiViewProvider: emojiViewProvider,
|
emojiViewProvider: emojiViewProvider,
|
||||||
wallpaperBackgroundNode: wallpaperBackgroundNode,
|
wallpaperBackgroundNode: wallpaperBackgroundNode,
|
||||||
attachment: attachment,
|
attachment: attachment,
|
||||||
|
@ -202,6 +202,7 @@ final class MessageItemView: UIView {
|
|||||||
sourceTextInputView: ChatInputTextView?,
|
sourceTextInputView: ChatInputTextView?,
|
||||||
emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?,
|
emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?,
|
||||||
sourceMediaPreview: ChatSendMessageContextScreenMediaPreview?,
|
sourceMediaPreview: ChatSendMessageContextScreenMediaPreview?,
|
||||||
|
mediaCaptionIsAbove: Bool,
|
||||||
textInsets: UIEdgeInsets,
|
textInsets: UIEdgeInsets,
|
||||||
explicitBackgroundSize: CGSize?,
|
explicitBackgroundSize: CGSize?,
|
||||||
maxTextWidth: CGFloat,
|
maxTextWidth: CGFloat,
|
||||||
@ -255,6 +256,16 @@ final class MessageItemView: UIView {
|
|||||||
backgroundNode: backgroundNode
|
backgroundNode: backgroundNode
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.backgroundNode.setType(
|
||||||
|
type: .outgoing(.None),
|
||||||
|
highlighted: false,
|
||||||
|
graphics: themeGraphics,
|
||||||
|
maskMode: true,
|
||||||
|
hasWallpaper: true,
|
||||||
|
transition: transition.containedViewLayoutTransition,
|
||||||
|
backgroundNode: backgroundNode
|
||||||
|
)
|
||||||
|
|
||||||
if let sourceMediaPreview {
|
if let sourceMediaPreview {
|
||||||
let mediaPreviewClippingView: UIView
|
let mediaPreviewClippingView: UIView
|
||||||
if let current = self.mediaPreviewClippingView {
|
if let current = self.mediaPreviewClippingView {
|
||||||
@ -281,7 +292,7 @@ final class MessageItemView: UIView {
|
|||||||
let mediaPreviewSize = sourceMediaPreview.update(containerSize: containerSize, transition: transition)
|
let mediaPreviewSize = sourceMediaPreview.update(containerSize: containerSize, transition: transition)
|
||||||
|
|
||||||
var backgroundSize = CGSize(width: mediaPreviewSize.width, height: mediaPreviewSize.height)
|
var backgroundSize = CGSize(width: mediaPreviewSize.width, height: mediaPreviewSize.height)
|
||||||
let mediaPreviewFrame: CGRect
|
var mediaPreviewFrame: CGRect
|
||||||
switch sourceMediaPreview.layoutType {
|
switch sourceMediaPreview.layoutType {
|
||||||
case .message, .media:
|
case .message, .media:
|
||||||
backgroundSize.width += 7.0
|
backgroundSize.width += 7.0
|
||||||
@ -290,8 +301,135 @@ final class MessageItemView: UIView {
|
|||||||
mediaPreviewFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: mediaPreviewSize)
|
mediaPreviewFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: mediaPreviewSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let backgroundAlpha: CGFloat
|
||||||
|
switch sourceMediaPreview.layoutType {
|
||||||
|
case .media:
|
||||||
|
backgroundAlpha = explicitBackgroundSize != nil ? 0.0 : 1.0
|
||||||
|
case .message, .videoMessage:
|
||||||
|
backgroundAlpha = 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
var backgroundFrame = mediaPreviewFrame.insetBy(dx: -2.0, dy: -2.0)
|
||||||
|
backgroundFrame.size.width += 6.0
|
||||||
|
|
||||||
|
if textString.length != 0 {
|
||||||
|
let textNode: ChatInputTextNode
|
||||||
|
if let current = self.textNode {
|
||||||
|
textNode = current
|
||||||
|
} else {
|
||||||
|
textNode = ChatInputTextNode(disableTiling: true)
|
||||||
|
textNode.textView.isScrollEnabled = false
|
||||||
|
textNode.isUserInteractionEnabled = false
|
||||||
|
self.textNode = textNode
|
||||||
|
self.textClippingContainer.addSubview(textNode.view)
|
||||||
|
|
||||||
|
if let sourceTextInputView {
|
||||||
|
var textContainerInset = sourceTextInputView.defaultTextContainerInset
|
||||||
|
textContainerInset.right = 0.0
|
||||||
|
textNode.textView.defaultTextContainerInset = textContainerInset
|
||||||
|
}
|
||||||
|
|
||||||
|
let messageAttributedText = NSMutableAttributedString(attributedString: textString)
|
||||||
|
textNode.attributedText = messageAttributedText
|
||||||
|
}
|
||||||
|
|
||||||
|
let mainColor = presentationData.theme.chat.message.outgoing.accentControlColor
|
||||||
|
let mappedLineStyle: ChatInputTextView.Theme.Quote.LineStyle
|
||||||
|
if let sourceTextInputView, let textTheme = sourceTextInputView.theme {
|
||||||
|
switch textTheme.quote.lineStyle {
|
||||||
|
case .solid:
|
||||||
|
mappedLineStyle = .solid(color: mainColor)
|
||||||
|
case .doubleDashed:
|
||||||
|
mappedLineStyle = .doubleDashed(mainColor: mainColor, secondaryColor: .clear)
|
||||||
|
case .tripleDashed:
|
||||||
|
mappedLineStyle = .tripleDashed(mainColor: mainColor, secondaryColor: .clear, tertiaryColor: .clear)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mappedLineStyle = .solid(color: mainColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
textNode.textView.theme = ChatInputTextView.Theme(
|
||||||
|
quote: ChatInputTextView.Theme.Quote(
|
||||||
|
background: mainColor.withMultipliedAlpha(0.1),
|
||||||
|
foreground: mainColor,
|
||||||
|
lineStyle: mappedLineStyle,
|
||||||
|
codeBackground: mainColor.withMultipliedAlpha(0.1),
|
||||||
|
codeForeground: mainColor
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let maxTextWidth = mediaPreviewFrame.width
|
||||||
|
|
||||||
|
let textPositioningInsets = UIEdgeInsets(top: -5.0, left: 0.0, bottom: -4.0, right: -4.0)
|
||||||
|
|
||||||
|
let currentRightInset: CGFloat = 0.0
|
||||||
|
let textHeight = textNode.textHeightForWidth(maxTextWidth, rightInset: currentRightInset)
|
||||||
|
textNode.updateLayout(size: CGSize(width: maxTextWidth, height: textHeight))
|
||||||
|
|
||||||
|
let textBoundingRect = textNode.textView.currentTextBoundingRect().integral
|
||||||
|
let lastLineBoundingRect = textNode.textView.lastLineBoundingRect().integral
|
||||||
|
|
||||||
|
let textWidth = textBoundingRect.width
|
||||||
|
let textSize = CGSize(width: textWidth, height: textHeight)
|
||||||
|
|
||||||
|
var positionedTextSize = CGSize(width: textSize.width + textPositioningInsets.left + textPositioningInsets.right, height: textSize.height + textPositioningInsets.top + textPositioningInsets.bottom)
|
||||||
|
|
||||||
|
let effectInset: CGFloat = 12.0
|
||||||
|
if effect != nil, lastLineBoundingRect.width > textSize.width - effectInset {
|
||||||
|
if lastLineBoundingRect != textBoundingRect {
|
||||||
|
positionedTextSize.height += 11.0
|
||||||
|
} else {
|
||||||
|
positionedTextSize.width += effectInset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let unclippedPositionedTextHeight = positionedTextSize.height - (textPositioningInsets.top + textPositioningInsets.bottom)
|
||||||
|
|
||||||
|
positionedTextSize.height = min(positionedTextSize.height, maxTextHeight)
|
||||||
|
|
||||||
|
let size = CGSize(width: positionedTextSize.width + textInsets.left + textInsets.right, height: positionedTextSize.height + textInsets.top + textInsets.bottom)
|
||||||
|
|
||||||
|
var textFrame = CGRect(origin: CGPoint(x: textInsets.left - 6.0, y: backgroundFrame.height - 4.0 + textInsets.top), size: positionedTextSize)
|
||||||
|
if mediaCaptionIsAbove {
|
||||||
|
textFrame.origin.y = 5.0
|
||||||
|
}
|
||||||
|
|
||||||
|
backgroundFrame.size.height += textSize.height + 2.0
|
||||||
|
if mediaCaptionIsAbove {
|
||||||
|
mediaPreviewFrame.origin.y += textSize.height + 2.0
|
||||||
|
}
|
||||||
|
|
||||||
|
let backgroundSize = explicitBackgroundSize ?? size
|
||||||
|
|
||||||
|
let previousSize = self.currentSize
|
||||||
|
self.currentSize = backgroundFrame.size
|
||||||
|
let _ = previousSize
|
||||||
|
|
||||||
|
let textClippingContainerFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX + 1.0, y: backgroundFrame.minY + 1.0), size: CGSize(width: backgroundFrame.width - 1.0 - 7.0, height: backgroundFrame.height - 1.0 - 1.0))
|
||||||
|
|
||||||
|
var textClippingContainerBounds = CGRect(origin: CGPoint(), size: textClippingContainerFrame.size)
|
||||||
|
if explicitBackgroundSize != nil, let sourceTextInputView {
|
||||||
|
textClippingContainerBounds.origin.y = sourceTextInputView.contentOffset.y
|
||||||
|
} else {
|
||||||
|
textClippingContainerBounds.origin.y = unclippedPositionedTextHeight - backgroundSize.height + 4.0
|
||||||
|
textClippingContainerBounds.origin.y = max(0.0, textClippingContainerBounds.origin.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
transition.setPosition(view: self.textClippingContainer, position: textClippingContainerFrame.center)
|
||||||
|
transition.setBounds(view: self.textClippingContainer, bounds: textClippingContainerBounds)
|
||||||
|
|
||||||
|
transition.setFrame(view: textNode.view, frame: CGRect(origin: CGPoint(x: textFrame.minX + textPositioningInsets.left - textClippingContainerFrame.minX, y: textFrame.minY + textPositioningInsets.top - textClippingContainerFrame.minY), size: CGSize(width: maxTextWidth, height: textHeight)))
|
||||||
|
self.updateTextContents()
|
||||||
|
}
|
||||||
|
|
||||||
transition.setFrame(view: sourceMediaPreview.view, frame: mediaPreviewFrame)
|
transition.setFrame(view: sourceMediaPreview.view, frame: mediaPreviewFrame)
|
||||||
|
|
||||||
|
transition.setFrame(view: self.backgroundWallpaperNode.view, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
|
||||||
|
transition.setAlpha(view: self.backgroundWallpaperNode.view, alpha: backgroundAlpha)
|
||||||
|
self.backgroundWallpaperNode.updateFrame(backgroundFrame, transition: transition.containedViewLayoutTransition)
|
||||||
|
transition.setFrame(view: self.backgroundNode.view, frame: backgroundFrame)
|
||||||
|
transition.setAlpha(view: self.backgroundNode.view, alpha: backgroundAlpha)
|
||||||
|
self.backgroundNode.updateLayout(size: backgroundFrame.size, transition: transition.containedViewLayoutTransition)
|
||||||
|
|
||||||
if let effectIcon = self.effectIcon, let effectIconSize {
|
if let effectIcon = self.effectIcon, let effectIconSize {
|
||||||
if let effectIconView = effectIcon.view {
|
if let effectIconView = effectIcon.view {
|
||||||
var animateIn = false
|
var animateIn = false
|
||||||
@ -367,7 +505,7 @@ final class MessageItemView: UIView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return backgroundSize
|
return backgroundFrame.size
|
||||||
} else {
|
} else {
|
||||||
let textNode: ChatInputTextNode
|
let textNode: ChatInputTextNode
|
||||||
if let current = self.textNode {
|
if let current = self.textNode {
|
||||||
@ -384,7 +522,6 @@ final class MessageItemView: UIView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let messageAttributedText = NSMutableAttributedString(attributedString: textString)
|
let messageAttributedText = NSMutableAttributedString(attributedString: textString)
|
||||||
//messageAttributedText.addAttribute(NSAttributedString.Key.foregroundColor, value: presentationData.theme.chat.message.outgoing.primaryTextColor, range: NSMakeRange(0, (messageAttributedText.string as NSString).length))
|
|
||||||
textNode.attributedText = messageAttributedText
|
textNode.attributedText = messageAttributedText
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,16 +583,6 @@ final class MessageItemView: UIView {
|
|||||||
|
|
||||||
let textFrame = CGRect(origin: CGPoint(x: textInsets.left, y: textInsets.top), size: positionedTextSize)
|
let textFrame = CGRect(origin: CGPoint(x: textInsets.left, y: textInsets.top), size: positionedTextSize)
|
||||||
|
|
||||||
self.backgroundNode.setType(
|
|
||||||
type: .outgoing(.None),
|
|
||||||
highlighted: false,
|
|
||||||
graphics: themeGraphics,
|
|
||||||
maskMode: true,
|
|
||||||
hasWallpaper: true,
|
|
||||||
transition: transition.containedViewLayoutTransition,
|
|
||||||
backgroundNode: backgroundNode
|
|
||||||
)
|
|
||||||
|
|
||||||
let backgroundSize = explicitBackgroundSize ?? size
|
let backgroundSize = explicitBackgroundSize ?? size
|
||||||
|
|
||||||
let previousSize = self.currentSize
|
let previousSize = self.currentSize
|
||||||
|
Loading…
x
Reference in New Issue
Block a user