Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2023-10-30 01:35:47 +04:00
commit c06e869cd1
22 changed files with 273 additions and 79 deletions

View File

@ -1473,7 +1473,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
return ASEditableTextNodeTargetForAction(target: nil)
}
}
} else if action == #selector(self.formatAttributesBold(_:)) || action == #selector(self.formatAttributesItalic(_:)) || action == #selector(self.formatAttributesMonospace(_:)) || action == #selector(self.formatAttributesLink(_:)) || action == #selector(self.formatAttributesStrikethrough(_:)) || action == #selector(self.formatAttributesUnderline(_:)) || action == #selector(self.formatAttributesSpoiler(_:)) {
} else if action == #selector(self.formatAttributesBold(_:)) || action == #selector(self.formatAttributesItalic(_:)) || action == #selector(self.formatAttributesMonospace(_:)) || action == #selector(self.formatAttributesLink(_:)) || action == #selector(self.formatAttributesStrikethrough(_:)) || action == #selector(self.formatAttributesUnderline(_:)) || action == #selector(self.formatAttributesSpoiler(_:)) || action == #selector(self.formatAttributesQuote(_:)) {
if case .format = self.inputMenu.state {
return ASEditableTextNodeTargetForAction(target: self)
} else {
@ -1646,6 +1646,14 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
self.updateSpoilersRevealed(animated: animated)
}
@objc func formatAttributesQuote(_ sender: Any) {
self.inputMenu.back()
self.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in
return (chatTextInputAddFormattingAttribute(current, attribute: ChatTextInputAttributes.quote), inputMode)
}
}
public func chatInputTextNode(shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
guard let editableTextNode = self.textInputNode else {
return true
@ -1728,6 +1736,10 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
return true
}
public func chatInputTextNodeTargetForAction(action: Selector) -> ChatInputTextNode.TargetForAction? {
return nil
}
@objc public func editableTextNodeShouldPaste(_ editableTextNode: ASEditableTextNode) -> Bool {
return self.chatInputTextNodeShouldPaste()
}

View File

@ -554,6 +554,13 @@ public class CreatePollTextInputItemNode: ListViewItemNode, ASEditableTextNodeDe
}
}
@objc func formatAttributesQuote(_ sender: Any) {
self.inputMenu.back()
if let item = self.item {
chatTextInputAddFormattingAttribute(item: item, textNode: self.textNode, theme: item.presentationData.theme, attribute: ChatTextInputAttributes.quote)
}
}
public func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if let item = self.item {
if text.count > 1, let processPaste = item.processPaste {

View File

@ -947,7 +947,11 @@ public final class TextNodeLayout: NSObject {
rects.append((lineFrame, CGRect(origin: CGPoint(x: lineFrame.minX + min(leftOffset, rightOffset) + self.insets.left, y: lineFrame.minY + self.insets.top), size: CGSize(width: width, height: lineFrame.size.height))))
}
}
if !rects.isEmpty, let startEdge = startEdge, let endEdge = endEdge {
if !rects.isEmpty, var startEdge = startEdge, var endEdge = endEdge {
startEdge.x += self.insets.left
startEdge.y += self.insets.top
endEdge.x += self.insets.left
endEdge.y += self.insets.top
return (rects.map { $1 }, startEdge, endEdge)
}
return nil

View File

@ -207,18 +207,23 @@ void stickerThumbnailAlphaBlur(int imageWidth, int imageHeight, int imageStride,
srcBuffer.rowBytes = imageStride;
srcBuffer.data = pixels;
void *tempBytes = malloc(imageHeight * imageStride);
{
vImage_Buffer dstBuffer;
dstBuffer.width = imageWidth;
dstBuffer.height = imageHeight;
dstBuffer.rowBytes = imageStride;
dstBuffer.data = pixels;
dstBuffer.data = tempBytes;
int boxSize = 2;
boxSize = boxSize - (boxSize % 2) + 1;
vImageBoxConvolve_ARGB8888(&srcBuffer, &dstBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
}
memcpy(pixels, tempBytes, imageHeight * imageStride);
free(tempBytes);
}
static void modifyImage(void *pixels, unsigned int width, unsigned int height, unsigned int stride, int16_t * _Nonnull matrix)

View File

@ -387,6 +387,7 @@ public func chatMessageStickerPackThumbnail(postbox: Postbox, resource: MediaRes
public func chatMessageSticker(postbox: Postbox, userLocation: MediaResourceUserLocation, file: TelegramMediaFile, small: Bool, fetched: Bool = false, onlyFullSize: Bool = false, thumbnail: Bool = false, synchronousLoad: Bool = false, colorSpace: CGColorSpace? = nil) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
let signal: Signal<Tuple3<Data?, Data?, Bool>, NoError>
if thumbnail {
signal = chatMessageStickerThumbnailData(postbox: postbox, userLocation: userLocation, file: file, synchronousLoad: synchronousLoad)
|> map { data -> Tuple3<Data?, Data?, Bool>in

View File

@ -27,8 +27,8 @@ public enum MessageTextEntityType: Equatable {
}
public struct MessageTextEntity: PostboxCoding, Codable, Equatable {
public let range: Range<Int>
public let type: MessageTextEntityType
public var range: Range<Int>
public var type: MessageTextEntityType
public init(range: Range<Int>, type: MessageTextEntityType) {
self.range = range

View File

@ -17,6 +17,7 @@
@property (nonatomic, copy) bool (^ _Nullable shouldCopy)();
@property (nonatomic, copy) bool (^ _Nullable shouldPaste)();
@property (nonatomic, copy) bool (^ _Nullable shouldRespondToAction)(SEL _Nullable);
@property (nonatomic, copy) ChatInputTextViewImplTargetForAction * _Nullable (^ _Nullable targetForAction)(SEL _Nullable);
@property (nonatomic, copy) bool (^ _Nullable shouldReturn)();
@property (nonatomic, copy) void (^ _Nullable backspaceWhileEmpty)();
@property (nonatomic, copy) void (^ _Nullable dropAutocorrectioniOS16)();

View File

@ -68,6 +68,13 @@
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
if (_targetForAction) {
ChatInputTextViewImplTargetForAction *result = _targetForAction(action);
if (result) {
return result.target != nil;
}
}
if (_shouldRespondToAction) {
if (!_shouldRespondToAction(action)) {
return false;
@ -102,6 +109,13 @@
}
- (id)targetForAction:(SEL)action withSender:(id)__unused sender {
if (_targetForAction) {
ChatInputTextViewImplTargetForAction *result = _targetForAction(action);
if (result) {
return result.target;
}
}
return [super targetForAction:action withSender:sender];
}

View File

@ -22,9 +22,18 @@ public protocol ChatInputTextNodeDelegate: AnyObject {
func chatInputTextNodeShouldPaste() -> Bool
func chatInputTextNodeShouldRespondToAction(action: Selector) -> Bool
func chatInputTextNodeTargetForAction(action: Selector) -> ChatInputTextNode.TargetForAction?
}
open class ChatInputTextNode: ASDisplayNode, UITextViewDelegate {
public final class TargetForAction {
public let target: Any?
public init(target: Any?) {
self.target = target
}
}
public weak var delegate: ChatInputTextNodeDelegate? {
didSet {
self.textView.customDelegate = self.delegate
@ -124,6 +133,18 @@ open class ChatInputTextNode: ASDisplayNode, UITextViewDelegate {
return true
}
}
self.textView.targetForAction = { [weak self] action in
guard let self, let action else {
return nil
}
if let delegate = self.delegate {
return delegate.chatInputTextNodeTargetForAction(action: action).flatMap { value in
return ChatInputTextViewImplTargetForAction(target: value.target)
}
} else {
return nil
}
}
}
public func resetInitialPrimaryLanguage() {

View File

@ -4699,7 +4699,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
if let backgroundType = self.backgroundType {
let graphics = PresentationResourcesChat.principalGraphics(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners)
if self.highlightedState != nil {
if self.highlightedState != nil, !(self.backgroundNode.layer.mask is SimpleLayer) {
let backgroundHighlightNode: ChatMessageBackground
if let current = self.backgroundHighlightNode {
backgroundHighlightNode = current

View File

@ -27,6 +27,7 @@ import ChatMessageReplyInfoNode
import InstantVideoRadialStatusNode
import ChatInstantVideoMessageDurationNode
import ChatControllerInteraction
import WallpaperBackgroundNode
public struct ChatMessageInstantVideoItemLayoutResult {
public let contentSize: CGSize
@ -109,8 +110,9 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
public var viaBotNode: TextNode?
public var replyInfoNode: ChatMessageReplyInfoNode?
public var replyBackgroundNode: NavigationBackgroundNode?
public var replyBackgroundContent: WallpaperBubbleBackgroundNode?
public var forwardInfoNode: ChatMessageForwardInfoNode?
public var forwardBackgroundContent: WallpaperBubbleBackgroundNode?
private var status: FileMediaResourceStatus?
private var playerStatus: MediaPlayerStatus? {
@ -240,7 +242,6 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
let viaBotLayout = TextNode.asyncLayout(self.viaBotNode)
let makeReplyInfoLayout = ChatMessageReplyInfoNode.asyncLayout(self.replyInfoNode)
let makeForwardInfoLayout = ChatMessageForwardInfoNode.asyncLayout(self.forwardInfoNode)
let currentReplyBackgroundNode = self.replyBackgroundNode
return { item, width, displaySize, maximumDisplaySize, scaleProgress, statusDisplayType, automaticDownload, avatarInset in
var secretVideoPlaceholderBackgroundImage: UIImage?
@ -598,15 +599,14 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
strongSelf.automaticDownload = automaticDownload
var updatedReplyBackgroundNode: NavigationBackgroundNode?
if replyInfoApply != nil || viaBotApply != nil || forwardInfoSizeApply != nil {
if let currentReplyBackgroundNode = currentReplyBackgroundNode {
updatedReplyBackgroundNode = currentReplyBackgroundNode
} else {
updatedReplyBackgroundNode = NavigationBackgroundNode(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: item.controllerInteraction.enableFullTranslucency && dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper))
}
updatedReplyBackgroundNode?.updateColor(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: item.controllerInteraction.enableFullTranslucency && dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), transition: .immediate)
var needsReplyBackground = false
if replyInfoApply != nil {
needsReplyBackground = true
}
var needsForwardBackground = false
if viaBotApply != nil || forwardInfoSizeApply != nil {
needsForwardBackground = true
}
if let updatedAudioTranscriptionState = updatedAudioTranscriptionState {
@ -918,7 +918,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
if let telegramFile = updatedFile, previousAutomaticDownload != automaticDownload, automaticDownload {
strongSelf.fetchDisposable.set(messageMediaFileInteractiveFetched(context: item.context, message: item.message, file: telegramFile, userInitiated: false).startStrict())
}
if let forwardInfo = item.message.forwardInfo, forwardInfo.flags.contains(.isImported) {
strongSelf.dateAndStatusNode.pressed = {
guard let strongSelf = self else {
@ -930,42 +930,75 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
strongSelf.dateAndStatusNode.pressed = nil
}
if let updatedReplyBackgroundNode = updatedReplyBackgroundNode {
if strongSelf.replyBackgroundNode == nil {
strongSelf.replyBackgroundNode = updatedReplyBackgroundNode
strongSelf.addSubnode(updatedReplyBackgroundNode)
}
} else if let replyBackgroundNode = strongSelf.replyBackgroundNode {
replyBackgroundNode.removeFromSupernode()
strongSelf.replyBackgroundNode = nil
}
var messageInfoSize = CGSize()
if let (viaBotLayout, _) = viaBotApply, forwardInfoSizeApply == nil {
messageInfoSize = CGSize(width: viaBotLayout.size.width + 1.0, height: 0.0)
}
if let (forwardInfoSize, _) = forwardInfoSizeApply {
messageInfoSize = CGSize(width: max(messageInfoSize.width, forwardInfoSize.width + 2.0), height: 0.0)
}
if let (replyInfoSize, _) = replyInfoApply {
messageInfoSize = CGSize(width: max(messageInfoSize.width, replyInfoSize.width), height: 0.0)
}
var width = width
if !scaleProgress.isZero {
width += avatarInset
}
if needsReplyBackground {
if strongSelf.replyBackgroundContent == nil, let backgroundContent = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) {
backgroundContent.clipsToBounds = true
strongSelf.replyBackgroundContent = backgroundContent
strongSelf.insertSubnode(backgroundContent, at: 0)
}
} else {
if let replyBackgroundContent = strongSelf.replyBackgroundContent {
replyBackgroundContent.removeFromSupernode()
strongSelf.replyBackgroundContent = nil
}
}
if needsForwardBackground {
if strongSelf.forwardBackgroundContent == nil, let backgroundContent = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) {
backgroundContent.clipsToBounds = true
strongSelf.forwardBackgroundContent = backgroundContent
strongSelf.insertSubnode(backgroundContent, at: 0)
}
} else {
if let forwardBackgroundContent = strongSelf.forwardBackgroundContent {
forwardBackgroundContent.removeFromSupernode()
strongSelf.forwardBackgroundContent = nil
}
}
var headersOffset: CGFloat = 0.0
var forwardAreaSize = CGSize()
if let (viaBotLayout, _) = viaBotApply, forwardInfoSizeApply == nil {
forwardAreaSize = CGSize(width: viaBotLayout.size.width + 1.0, height: 0.0)
}
if let (forwardInfoSize, _) = forwardInfoSizeApply {
forwardAreaSize = CGSize(width: max(forwardAreaSize.width, forwardInfoSize.width + 2.0), height: 0.0)
}
var replyAreaSize = CGSize()
if let (replyInfoSize, _) = replyInfoApply {
replyAreaSize = CGSize(width: max(replyAreaSize.width, replyInfoSize.width), height: 0.0)
}
let edgeInset: CGFloat = 4.0
let leftInset: CGFloat = 0.0
let rightInset: CGFloat = 0.0
var forwardAreaFrame: CGRect?
var messageInfoSize = CGSize()
if let (viaBotLayout, viaBotApply) = viaBotApply, forwardInfoSizeApply == nil {
let viaBotNode = viaBotApply()
if strongSelf.viaBotNode == nil {
strongSelf.viaBotNode = viaBotNode
strongSelf.addSubnode(viaBotNode)
}
let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (leftInset + edgeInset) : (width - rightInset - forwardAreaSize.width - edgeInset)), y: headersOffset + 8.0), size: viaBotLayout.size)
let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (displayVideoFrame.maxX - width + 6.0) : (width - messageInfoSize.width - bubbleEdgeInset - 9.0 + 10.0)), y: 8.0), size: viaBotLayout.size)
animation.animator.updateFrame(layer: viaBotNode.layer, frame: viaBotFrame, completion: nil)
viaBotNode.frame = viaBotFrame
messageInfoSize = CGSize(width: messageInfoSize.width, height: viaBotLayout.size.height)
if let forwardAreaFrameValue = forwardAreaFrame {
forwardAreaFrame = forwardAreaFrameValue.union(viaBotFrame)
} else {
forwardAreaFrame = viaBotFrame
}
} else if let viaBotNode = strongSelf.viaBotNode {
viaBotNode.removeFromSupernode()
strongSelf.viaBotNode = nil
@ -981,10 +1014,16 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
forwardInfoNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (displayVideoFrame.maxX - width + 6.0) : (width - messageInfoSize.width - bubbleEdgeInset - 8.0 + 10.0)), y: 8.0 + messageInfoSize.height), size: forwardInfoSize)
animation.animator.updateFrame(layer: forwardInfoNode.layer, frame: forwardInfoFrame, completion: nil)
let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (displayVideoFrame.minX - forwardAreaSize.width - 4.0) : (displayVideoFrame.maxX + 6.0)), y: headersOffset + 8.0 + messageInfoSize.height), size: forwardInfoSize)
forwardInfoNode.frame = forwardInfoFrame
messageInfoSize = CGSize(width: messageInfoSize.width, height: messageInfoSize.height + forwardInfoSize.height - 1.0)
messageInfoSize = CGSize(width: messageInfoSize.width, height: messageInfoSize.height + forwardInfoSize.height + 8.0)
if let forwardAreaFrameValue = forwardAreaFrame {
forwardAreaFrame = forwardAreaFrameValue.union(forwardInfoFrame)
} else {
forwardAreaFrame = forwardInfoFrame
}
} else if let forwardInfoNode = strongSelf.forwardInfoNode {
if animation.isAnimated {
if let forwardInfoNode = strongSelf.forwardInfoNode {
@ -999,9 +1038,18 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
}
}
var forwardBackgroundFrame: CGRect?
if let forwardAreaFrame {
forwardBackgroundFrame = forwardAreaFrame.insetBy(dx: -6.0, dy: -3.0)
}
var replyBackgroundFrame: CGRect?
if let (replyInfoSize, replyInfoApply) = replyInfoApply {
let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (displayVideoFrame.maxX - width + 5.0) : (width - messageInfoSize.width - bubbleEdgeInset - 9.0 + 10.0)), y: 8.0 + messageInfoSize.height), size: replyInfoSize)
if headersOffset != 0.0 {
headersOffset += 6.0
}
let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (displayVideoFrame.minX - replyInfoSize.width) : (displayVideoFrame.maxX)), y: headersOffset + 8.0 + messageInfoSize.height), size: replyInfoSize)
replyBackgroundFrame = replyInfoFrame
let replyInfoNode = replyInfoApply(replyInfoFrame.size, false, animation)
@ -1009,7 +1057,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
strongSelf.replyInfoNode = replyInfoNode
strongSelf.addSubnode(replyInfoNode)
}
animation.animator.updateFrame(layer: replyInfoNode.layer, frame: replyInfoFrame, completion: nil)
replyInfoNode.frame = replyInfoFrame
messageInfoSize = CGSize(width: max(messageInfoSize.width, replyInfoSize.width), height: messageInfoSize.height + replyInfoSize.height)
} else if let replyInfoNode = strongSelf.replyInfoNode {
@ -1017,20 +1065,25 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
strongSelf.replyInfoNode = nil
}
if let replyBackgroundNode = strongSelf.replyBackgroundNode, let replyBackgroundFrame {
let replyBackgroundFrame = replyBackgroundFrame
animation.animator.updateFrame(layer: replyBackgroundNode.layer, frame: replyBackgroundFrame, completion: nil)
let cornerRadius = replyBackgroundNode.frame.height <= 22.0 ? replyBackgroundNode.frame.height / 2.0 : 8.0
replyBackgroundNode.update(size: replyBackgroundNode.bounds.size, cornerRadius: cornerRadius, transition: .immediate)
if let backgroundContent = strongSelf.replyBackgroundContent, let replyBackgroundFrame {
backgroundContent.cornerRadius = 4.0
backgroundContent.frame = replyBackgroundFrame
}
if let backgroundContent = strongSelf.forwardBackgroundContent, let forwardBackgroundFrame {
backgroundContent.cornerRadius = 4.0
backgroundContent.frame = forwardBackgroundFrame
}
let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
if let viaBotNode = strongSelf.viaBotNode {
transition.updateAlpha(node: viaBotNode, alpha: strongSelf.isPlaying ? 0.0 : 1.0)
}
if let replyBackgroundNode = strongSelf.replyBackgroundNode {
transition.updateAlpha(node: replyBackgroundNode, alpha: strongSelf.isPlaying ? 0.0 : 1.0)
if let replyBackgroundContent = strongSelf.replyBackgroundContent {
transition.updateAlpha(node: replyBackgroundContent, alpha: strongSelf.isPlaying ? 0.0 : 1.0)
}
if let forwardBackgroundContent = strongSelf.forwardBackgroundContent {
transition.updateAlpha(node: forwardBackgroundContent, alpha: strongSelf.isPlaying ? 0.0 : 1.0)
}
if let forwardInfoNode = strongSelf.forwardInfoNode {
transition.updateAlpha(node: forwardInfoNode, alpha: strongSelf.isPlaying ? 0.0 : 1.0)
@ -1761,8 +1814,11 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
if let viaBotNode = self.viaBotNode {
viaBotNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration)
}
if let replyBackgroundNode = self.replyBackgroundNode {
replyBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration)
if let replyBackgroundContent = self.replyBackgroundContent {
replyBackgroundContent.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration)
}
if let forwardBackgroundContent = self.replyBackgroundContent {
forwardBackgroundContent.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration)
}
if let forwardInfoNode = self.forwardInfoNode {
forwardInfoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration)
@ -1866,8 +1922,11 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
if let viaBotNode = self.viaBotNode {
viaBotNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration)
}
if let replyBackgroundNode = self.replyBackgroundNode {
replyBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration)
if let replyBackgroundContent = self.replyBackgroundContent {
replyBackgroundContent.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration)
}
if let forwardBackgroundContent = self.forwardBackgroundContent {
forwardBackgroundContent.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration)
}
if let forwardInfoNode = self.forwardInfoNode {
forwardInfoNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration)

View File

@ -1201,7 +1201,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
textSelectionNode.enableQuote = enableQuote
textSelectionNode.enableTranslate = enableOtherActions
textSelectionNode.enableShare = enableOtherActions
textSelectionNode.enableShare = enableOtherActions && enableCopy
textSelectionNode.menuSkipCoordnateConversion = !enableOtherActions
self.textSelectionNode = textSelectionNode
self.containerNode.addSubnode(textSelectionNode)

View File

@ -735,7 +735,7 @@ public final class MessageInlineBlockBackgroundView: UIView {
let itemSize = CGSize(width: placement.size / 3.0, height: placement.size / 3.0)
patternContentLayer.frame = CGRect(origin: CGPoint(x: size.width - placement.position.x / 3.0 - itemSize.width * 0.5, y: placement.position.y / 3.0 - itemSize.height * 0.5), size: itemSize)
var alphaFraction = abs(placement.position.x) / min(500.0, size.width)
var alphaFraction = abs(placement.position.x / 3.0) / min(500.0, size.width)
alphaFraction = min(1.0, max(0.0, alphaFraction))
patternContentLayer.opacity = 0.3 * Float(1.0 - alphaFraction)

View File

@ -70,11 +70,12 @@ public func cacheStillSticker(path: String, width: Int, height: Int, writer: Ani
UIGraphicsPushContext(c)
if let customColor = customColor {
c.clip(to: CGRect(origin: CGPoint(), size: context.size), mask: image.cgImage!)
c.setFillColor(customColor.cgColor)
c.setBlendMode(.sourceIn)
c.fill(CGRect(origin: CGPoint(), size: context.size))
} else {
c.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: context.size))
}
c.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: context.size))
UIGraphicsPopContext()
}
memcpy(surface.argb, context.bytes, surface.height * surface.bytesPerRow)

View File

@ -267,6 +267,31 @@ public class ShareRootControllerImpl {
let accountManager = AccountManager<TelegramAccountManagerTypes>(basePath: rootPath + "/accounts-metadata", isTemporary: true, isReadOnly: false, useCaches: false, removeDatabaseOnError: false)
initializeAccountManagement()
do {
let semaphore = DispatchSemaphore(value: 0)
var loggingSettings = LoggingSettings.defaultSettings
if self.initializationData.appBuildType == .internal {
loggingSettings = LoggingSettings(logToFile: true, logToConsole: false, redactSensitiveData: true)
}
let _ = (accountManager.transaction { transaction -> LoggingSettings? in
if let value = transaction.getSharedData(SharedDataKeys.loggingSettings)?.get(LoggingSettings.self) {
return value
} else {
return nil
}
}).start(next: { value in
if let value {
loggingSettings = value
}
semaphore.signal()
})
semaphore.wait()
Logger.shared.logToFile = loggingSettings.logToFile
Logger.shared.logToConsole = loggingSettings.logToConsole
Logger.shared.redactSensitiveData = loggingSettings.redactSensitiveData
}
var initialPresentationDataAndSettings: InitialPresentationDataAndSettings?
let semaphore = DispatchSemaphore(value: 0)
let systemUserInterfaceStyle: WindowUserInterfaceStyle
@ -306,7 +331,6 @@ public class ShareRootControllerImpl {
|> mapToSignal { sharedContext, loggingSettings -> Signal<(SharedAccountContextImpl, Account, [AccountWithInfo]), ShareAuthorizationError> in
Logger.shared.logToFile = loggingSettings.logToFile
Logger.shared.logToConsole = loggingSettings.logToConsole
Logger.shared.redactSensitiveData = loggingSettings.redactSensitiveData
return combineLatest(sharedContext.activeAccountsWithInfo, accountManager.transaction { transaction -> (Set<AccountRecordId>, PeerId?) in

View File

@ -3338,6 +3338,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}, setupReply: { [weak self] messageId in
self?.interfaceInteraction?.setupReplyMessage(messageId, { _, f in f() })
}, canSetupReply: { [weak self] message in
if message.adAttribute != nil {
return .none
}
if !message.flags.contains(.Incoming) {
if !message.flags.intersection([.Failed, .Sending, .Unsent]).isEmpty {
return .none

View File

@ -772,10 +772,12 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
self.textInputViewInternalInsets = UIEdgeInsets(top: 1.0, left: 13.0, bottom: 1.0, right: 13.0)
var hasSpoilers = true
var hasQuotes = true
if presentationInterfaceState.chatLocation.peerId?.namespace == Namespaces.Peer.SecretChat {
hasSpoilers = false
hasQuotes = false
}
self.inputMenu = TextInputMenu(hasSpoilers: hasSpoilers)
self.inputMenu = TextInputMenu(hasSpoilers: hasSpoilers, hasQuotes: hasQuotes)
self.clippingNode = ASDisplayNode()
self.clippingNode.clipsToBounds = true
@ -3954,18 +3956,17 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
}
public func chatInputTextNodeShouldRespondToAction(action: Selector) -> Bool {
guard let textInputNode = self.textInputNode else {
return true
}
let _ = textInputNode
/*if textInputNode.attributedText == nil || textInputNode.attributedText!.length == 0 || textInputNode.selectedRange.length == 0 {
print("action: \(action)")
}*/
return true
}
public func chatInputTextNodeTargetForAction(action: Selector) -> ChatInputTextNode.TargetForAction? {
if let target = self.editableTextNodeTarget(forAction: action) {
return ChatInputTextNode.TargetForAction(target: target.target)
} else {
return nil
}
}
func chatInputTextNodeShouldPaste() -> Bool {
let pasteboard = UIPasteboard.general

View File

@ -181,6 +181,35 @@ public func generateChatInputTextEntities(_ text: NSAttributedString, maxAnimate
}
}
while true {
var hadReductions = false
scan: for i in 0 ..< entities.count {
if case .BlockQuote = entities[i].type {
inner: for j in 0 ..< entities.count {
if j == i {
continue inner
}
if case .BlockQuote = entities[j].type {
if entities[i].range.upperBound == entities[j].range.lowerBound || entities[i].range.lowerBound == entities[j].range.upperBound {
entities[i].range = min(entities[i].range.lowerBound, entities[j].range.lowerBound) ..< max(entities[i].range.upperBound, entities[j].range.upperBound)
entities.remove(at: j)
hadReductions = true
break scan
}
}
}
break scan
}
}
if !hadReductions {
break
}
}
return entities
}

View File

@ -16,8 +16,10 @@ public final class TextInputMenu {
private var stringStrikethrough: String = "Strikethrough"
private var stringUnderline: String = "Underline"
private var stringSpoiler: String = "Spoiler"
private var stringQuote: String = "Quote"
private let hasSpoilers: Bool
private let hasQuotes: Bool
public private(set) var state: State = .inactive {
didSet {
@ -39,6 +41,9 @@ public final class TextInputMenu {
if self.hasSpoilers {
menuItems.insert(UIMenuItem(title: self.stringSpoiler, action: Selector(("formatAttributesSpoiler:"))), at: 0)
}
if self.hasQuotes {
menuItems.insert(UIMenuItem(title: self.stringQuote, action: Selector(("formatAttributesQuote:"))), at: 0)
}
UIMenuController.shared.menuItems = menuItems
}
@ -48,8 +53,9 @@ public final class TextInputMenu {
private var observer: NSObjectProtocol?
public init(hasSpoilers: Bool = false) {
public init(hasSpoilers: Bool = false, hasQuotes: Bool = false) {
self.hasSpoilers = hasSpoilers
self.hasQuotes = hasQuotes
self.observer = NotificationCenter.default.addObserver(forName: UIMenuController.didHideMenuNotification, object: nil, queue: nil, using: { [weak self] _ in
self?.back()
})
@ -69,6 +75,7 @@ public final class TextInputMenu {
self.stringStrikethrough = strings.TextFormat_Strikethrough
self.stringUnderline = strings.TextFormat_Underline
self.stringSpoiler = strings.TextFormat_Spoiler
self.stringQuote = strings.TextFormat_Quote
}
public func activate() {

View File

@ -589,7 +589,7 @@ public final class TextSelectionNode: ASDisplayNode {
highlightOverlay.innerRadius = 2.0
highlightOverlay.outerRadius = 2.0
highlightOverlay.inset = 1.0
highlightOverlay.useModernPathCalculation = true
highlightOverlay.useModernPathCalculation = false
self.highlightOverlay = highlightOverlay
self.highlightAreaNode.addSubnode(highlightOverlay)
@ -597,8 +597,8 @@ public final class TextSelectionNode: ASDisplayNode {
highlightOverlay.frame = self.bounds
highlightOverlay.updateRects(rects)
if let image = self.leftKnob.image {
self.leftKnob.frame = CGRect(origin: CGPoint(x: floor(startEdge.x - image.size.width / 2.0), y: startEdge.y + 1.0 - 12.0), size: CGSize(width: image.size.width, height: self.theme.knobDiameter + startEdge.height + 2.0))
self.rightKnob.frame = CGRect(origin: CGPoint(x: floor(endEdge.x + 1.0 - image.size.width / 2.0), y: endEdge.y + endEdge.height + 3.0 - (endEdge.height + 2.0)), size: CGSize(width: image.size.width, height: self.theme.knobDiameter + endEdge.height + 2.0))
self.leftKnob.frame = CGRect(origin: CGPoint(x: floor(startEdge.x - image.size.width / 2.0), y: startEdge.y - self.theme.knobDiameter), size: CGSize(width: image.size.width, height: self.theme.knobDiameter + startEdge.height))
self.rightKnob.frame = CGRect(origin: CGPoint(x: floor(endEdge.x - image.size.width / 2.0), y: endEdge.y), size: CGSize(width: image.size.width, height: self.theme.knobDiameter + endEdge.height))
}
if self.leftKnob.alpha.isZero {
highlightOverlay.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)

View File

@ -10,6 +10,11 @@
return nil;
}
UIImage *osImage = [[UIImage alloc] initWithData:imgData scale:1.0];
if (osImage != nil) {
return osImage;
}
int width = 0, height = 0;
if (!WebPGetInfo([imgData bytes], [imgData length], &width, &height)) {
NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];

View File

@ -1,5 +1,5 @@
{
"app": "10.2",
"app": "10.2.1",
"bazel": "6.4.0",
"xcode": "15.0"
}