diff --git a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift index 5af2ccc8a7..d4e5df3f7b 100644 --- a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift +++ b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift @@ -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() } diff --git a/submodules/ComposePollUI/Sources/CreatePollTextInputItem.swift b/submodules/ComposePollUI/Sources/CreatePollTextInputItem.swift index 91d354d021..74c6f78c9f 100644 --- a/submodules/ComposePollUI/Sources/CreatePollTextInputItem.swift +++ b/submodules/ComposePollUI/Sources/CreatePollTextInputItem.swift @@ -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 { diff --git a/submodules/Display/Source/TextNode.swift b/submodules/Display/Source/TextNode.swift index 6d3c386c9f..cfc904d4a3 100644 --- a/submodules/Display/Source/TextNode.swift +++ b/submodules/Display/Source/TextNode.swift @@ -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 diff --git a/submodules/TelegramUI/Components/Chat/ChatInputTextNode/ChatInputTextViewImpl/PublicHeaders/ChatInputTextViewImpl/ChatInputTextViewImpl.h b/submodules/TelegramUI/Components/Chat/ChatInputTextNode/ChatInputTextViewImpl/PublicHeaders/ChatInputTextViewImpl/ChatInputTextViewImpl.h index 528c45c33f..f4a0508002 100755 --- a/submodules/TelegramUI/Components/Chat/ChatInputTextNode/ChatInputTextViewImpl/PublicHeaders/ChatInputTextViewImpl/ChatInputTextViewImpl.h +++ b/submodules/TelegramUI/Components/Chat/ChatInputTextNode/ChatInputTextViewImpl/PublicHeaders/ChatInputTextViewImpl/ChatInputTextViewImpl.h @@ -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)(); diff --git a/submodules/TelegramUI/Components/Chat/ChatInputTextNode/ChatInputTextViewImpl/Sources/ChatInputTextViewImpl.m b/submodules/TelegramUI/Components/Chat/ChatInputTextNode/ChatInputTextViewImpl/Sources/ChatInputTextViewImpl.m index 8e96504a12..eb3e9c739b 100755 --- a/submodules/TelegramUI/Components/Chat/ChatInputTextNode/ChatInputTextViewImpl/Sources/ChatInputTextViewImpl.m +++ b/submodules/TelegramUI/Components/Chat/ChatInputTextNode/ChatInputTextViewImpl/Sources/ChatInputTextViewImpl.m @@ -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]; } diff --git a/submodules/TelegramUI/Components/Chat/ChatInputTextNode/Sources/ChatInputTextNode.swift b/submodules/TelegramUI/Components/Chat/ChatInputTextNode/Sources/ChatInputTextNode.swift index c293c74e2f..cebdc34511 100644 --- a/submodules/TelegramUI/Components/Chat/ChatInputTextNode/Sources/ChatInputTextNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatInputTextNode/Sources/ChatInputTextNode.swift @@ -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() { diff --git a/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift b/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift index 7066c6aff7..2d2654d7a6 100644 --- a/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift +++ b/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift @@ -267,6 +267,31 @@ public class ShareRootControllerImpl { let accountManager = AccountManager(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, PeerId?) in diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 82aa0d7393..d522560103 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -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 diff --git a/submodules/TextInputMenu/Sources/TextInputMenu.swift b/submodules/TextInputMenu/Sources/TextInputMenu.swift index 9e13cccfe9..6046e69d6d 100644 --- a/submodules/TextInputMenu/Sources/TextInputMenu.swift +++ b/submodules/TextInputMenu/Sources/TextInputMenu.swift @@ -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() { diff --git a/submodules/TextSelectionNode/Sources/TextSelectionNode.swift b/submodules/TextSelectionNode/Sources/TextSelectionNode.swift index fd6cf39c1a..b65d15b18f 100644 --- a/submodules/TextSelectionNode/Sources/TextSelectionNode.swift +++ b/submodules/TextSelectionNode/Sources/TextSelectionNode.swift @@ -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)