mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Merge branch 'attach-menu'
This commit is contained in:
commit
ec9373188e
190 changed files with 4755 additions and 5510 deletions
|
|
@ -7256,3 +7256,13 @@ Sorry for the inconvenience.";
|
|||
|
||||
"SharedMedia.CommonGroupCount_1" = "%@ group in common";
|
||||
"SharedMedia.CommonGroupCount_any" = "%@ groups in common";
|
||||
|
||||
"Attachment.Camera" = "Camera";
|
||||
"Attachment.Gallery" = "Gallery";
|
||||
"Attachment.File" = "File";
|
||||
"Attachment.Location" = "Location";
|
||||
"Attachment.Contact" = "Contact";
|
||||
"Attachment.Poll" = "Poll";
|
||||
|
||||
"Attachment.SelectFromGallery" = "Select from Gallery";
|
||||
"Attachment.SelectFromFiles" = "Select from Files";
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ public final class PeerSelectionControllerParams {
|
|||
}
|
||||
}
|
||||
|
||||
public enum PeerSelectionControllerSendMode {
|
||||
public enum AttachmentTextInputPanelSendMode {
|
||||
case generic
|
||||
case silent
|
||||
case schedule
|
||||
|
|
@ -69,7 +69,7 @@ public enum PeerSelectionControllerSendMode {
|
|||
|
||||
public protocol PeerSelectionController: ViewController {
|
||||
var peerSelected: ((Peer) -> Void)? { get set }
|
||||
var multiplePeersSelected: (([Peer], [PeerId: Peer], NSAttributedString, PeerSelectionControllerSendMode, ChatInterfaceForwardOptionsState?) -> Void)? { get set }
|
||||
var multiplePeersSelected: (([Peer], [PeerId: Peer], NSAttributedString, AttachmentTextInputPanelSendMode, ChatInterfaceForwardOptionsState?) -> Void)? { get set }
|
||||
var inProgress: Bool { get set }
|
||||
var customDismiss: (() -> Void)? { get set }
|
||||
}
|
||||
|
|
|
|||
35
submodules/AttachmentTextInputPanelNode/BUILD
Normal file
35
submodules/AttachmentTextInputPanelNode/BUILD
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "AttachmentTextInputPanelNode",
|
||||
module_name = "AttachmentTextInputPanelNode",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/Postbox:Postbox",
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/TextFormat:TextFormat",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/TouchDownGesture:TouchDownGesture",
|
||||
"//submodules/ActivityIndicator:ActivityIndicator",
|
||||
"//submodules/Speak:Speak",
|
||||
"//submodules/LegacyComponents:LegacyComponents",
|
||||
"//submodules/ObjCRuntimeUtils:ObjCRuntimeUtils",
|
||||
"//submodules/InvisibleInkDustNode:InvisibleInkDustNode",
|
||||
"//submodules/TextInputMenu:TextInputMenu",
|
||||
"//submodules/ChatPresentationInterfaceState:ChatPresentationInterfaceState",
|
||||
"//submodules/Pasteboard:Pasteboard",
|
||||
"//submodules/ContextUI:ContextUI",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import ContextUI
|
||||
import ChatPresentationInterfaceState
|
||||
|
||||
final class AttachmentTextInputActionButtonsNode: ASDisplayNode {
|
||||
private let strings: PresentationStrings
|
||||
|
||||
let sendContainerNode: ASDisplayNode
|
||||
let backgroundNode: ASDisplayNode
|
||||
let sendButton: HighlightTrackingButtonNode
|
||||
var sendButtonHasApplyIcon = false
|
||||
var animatingSendButton = false
|
||||
let expandMediaInputButton: HighlightableButtonNode
|
||||
|
||||
var sendButtonLongPressed: ((ASDisplayNode, ContextGesture) -> Void)?
|
||||
|
||||
private var gestureRecognizer: ContextGesture?
|
||||
var sendButtonLongPressEnabled = false {
|
||||
didSet {
|
||||
self.gestureRecognizer?.isEnabled = self.sendButtonLongPressEnabled
|
||||
}
|
||||
}
|
||||
|
||||
private var validLayout: CGSize?
|
||||
|
||||
init(presentationInterfaceState: ChatPresentationInterfaceState, presentController: @escaping (ViewController) -> Void) {
|
||||
let theme = presentationInterfaceState.theme
|
||||
let strings = presentationInterfaceState.strings
|
||||
self.strings = strings
|
||||
|
||||
self.sendContainerNode = ASDisplayNode()
|
||||
self.sendContainerNode.layer.allowsGroupOpacity = true
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.backgroundColor = theme.chat.inputPanel.actionControlFillColor
|
||||
self.backgroundNode.clipsToBounds = true
|
||||
self.sendButton = HighlightTrackingButtonNode(pointerStyle: .lift)
|
||||
|
||||
self.expandMediaInputButton = HighlightableButtonNode(pointerStyle: .default)
|
||||
|
||||
super.init()
|
||||
|
||||
self.isAccessibilityElement = true
|
||||
self.accessibilityTraits = [.button, .notEnabled]
|
||||
|
||||
self.sendButton.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if strongSelf.sendButtonHasApplyIcon || !strongSelf.sendButtonLongPressEnabled {
|
||||
if highlighted {
|
||||
strongSelf.sendContainerNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.sendContainerNode.alpha = 0.4
|
||||
} else {
|
||||
strongSelf.sendContainerNode.alpha = 1.0
|
||||
strongSelf.sendContainerNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
}
|
||||
} else {
|
||||
if highlighted {
|
||||
strongSelf.sendContainerNode.layer.animateScale(from: 1.0, to: 0.75, duration: 0.4, removeOnCompletion: false)
|
||||
} else if let presentationLayer = strongSelf.sendButton.layer.presentation() {
|
||||
strongSelf.sendContainerNode.layer.animateScale(from: CGFloat((presentationLayer.value(forKeyPath: "transform.scale.y") as? NSNumber)?.floatValue ?? 1.0), to: 1.0, duration: 0.25, removeOnCompletion: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.addSubnode(self.sendContainerNode)
|
||||
self.sendContainerNode.addSubnode(self.backgroundNode)
|
||||
self.sendContainerNode.addSubnode(self.sendButton)
|
||||
self.addSubnode(self.expandMediaInputButton)
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
let gestureRecognizer = ContextGesture(target: nil, action: nil)
|
||||
self.gestureRecognizer = gestureRecognizer
|
||||
self.sendButton.view.addGestureRecognizer(gestureRecognizer)
|
||||
gestureRecognizer.activated = { [weak self] recognizer, _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if !strongSelf.sendButtonHasApplyIcon {
|
||||
strongSelf.sendButtonLongPressed?(strongSelf.sendContainerNode, recognizer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateTheme(theme: PresentationTheme, wallpaper: TelegramWallpaper) {
|
||||
self.expandMediaInputButton.setImage(PresentationResourcesChat.chatInputPanelExpandButtonImage(theme), for: [])
|
||||
|
||||
self.backgroundNode.backgroundColor = theme.chat.inputPanel.actionControlFillColor
|
||||
}
|
||||
|
||||
private var absoluteRect: (CGRect, CGSize)?
|
||||
func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
self.absoluteRect = (rect, containerSize)
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) {
|
||||
self.validLayout = size
|
||||
|
||||
transition.updateFrame(layer: self.sendButton.layer, frame: CGRect(origin: CGPoint(), size: size))
|
||||
transition.updateFrame(node: self.sendContainerNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
let backgroundSize = CGSize(width: 33.0, height: 33.0)
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - backgroundSize.width) / 2.0), y: floorToScreenPixels((size.height - backgroundSize.height) / 2.0)), size: backgroundSize))
|
||||
self.backgroundNode.cornerRadius = backgroundSize.width / 2.0
|
||||
|
||||
transition.updateFrame(node: self.expandMediaInputButton, frame: CGRect(origin: CGPoint(), size: size))
|
||||
var expanded = false
|
||||
if case let .media(_, maybeExpanded, _) = interfaceState.inputMode, maybeExpanded != nil {
|
||||
expanded = true
|
||||
}
|
||||
transition.updateSublayerTransformScale(node: self.expandMediaInputButton, scale: CGPoint(x: 1.0, y: expanded ? 1.0 : -1.0))
|
||||
}
|
||||
|
||||
func updateAccessibility() {
|
||||
self.accessibilityTraits = .button
|
||||
self.accessibilityLabel = self.strings.MediaPicker_Send
|
||||
self.accessibilityHint = nil
|
||||
}
|
||||
}
|
||||
|
|
@ -15,9 +15,12 @@ import Speak
|
|||
import ObjCRuntimeUtils
|
||||
import LegacyComponents
|
||||
import InvisibleInkDustNode
|
||||
import TextInputMenu
|
||||
import ChatPresentationInterfaceState
|
||||
import Pasteboard
|
||||
|
||||
private let counterFont = Font.with(size: 14.0, design: .regular, traits: [.monospacedNumbers])
|
||||
private let minInputFontSize = chatTextInputMinFontSize
|
||||
private let minInputFontSize: CGFloat = 5.0
|
||||
|
||||
private func calclulateTextFieldMinHeight(_ presentationInterfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
|
||||
|
|
@ -104,7 +107,7 @@ private func textInputBackgroundImage(backgroundColor: UIColor?, inputBackground
|
|||
}
|
||||
}
|
||||
|
||||
class CaptionEditableTextNode: EditableTextNode {
|
||||
private class CaptionEditableTextNode: EditableTextNode {
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
let previousAlpha = self.alpha
|
||||
self.alpha = 1.0
|
||||
|
|
@ -114,44 +117,47 @@ class CaptionEditableTextNode: EditableTextNode {
|
|||
}
|
||||
}
|
||||
|
||||
class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, ASEditableTextNodeDelegate {
|
||||
private let isCaption: Bool
|
||||
public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, ASEditableTextNodeDelegate {
|
||||
private let context: AccountContext
|
||||
|
||||
var textPlaceholderNode: ImmediateTextNode
|
||||
let textInputContainerBackgroundNode: ASImageNode
|
||||
let textInputContainer: ASDisplayNode
|
||||
var textInputNode: CaptionEditableTextNode?
|
||||
var dustNode: InvisibleInkDustNode?
|
||||
private let isCaption: Bool
|
||||
private let isAttachment: Bool
|
||||
|
||||
private var textPlaceholderNode: ImmediateTextNode
|
||||
private let textInputContainerBackgroundNode: ASImageNode
|
||||
private let textInputContainer: ASDisplayNode
|
||||
public var textInputNode: EditableTextNode?
|
||||
private var dustNode: InvisibleInkDustNode?
|
||||
private var oneLineNode: ImmediateTextNode
|
||||
private var oneLineDustNode: InvisibleInkDustNode?
|
||||
|
||||
let textInputBackgroundNode: ASDisplayNode
|
||||
let textInputBackgroundImageNode: ASImageNode
|
||||
private var transparentTextInputBackgroundImage: UIImage?
|
||||
let actionButtons: ChatTextInputActionButtonsNode
|
||||
private let actionButtons: AttachmentTextInputActionButtonsNode
|
||||
private let counterTextNode: ImmediateTextNode
|
||||
|
||||
private var validLayout: (CGFloat, CGFloat, CGFloat, UIEdgeInsets, CGFloat, LayoutMetrics, Bool)?
|
||||
|
||||
var sendMessage: (PeerSelectionControllerSendMode) -> Void = { _ in }
|
||||
var updateHeight: (Bool) -> Void = { _ in }
|
||||
public var sendMessage: (AttachmentTextInputPanelSendMode) -> Void = { _ in }
|
||||
public var updateHeight: (Bool) -> Void = { _ in }
|
||||
|
||||
private var updatingInputState = false
|
||||
|
||||
private var currentPlaceholder: String?
|
||||
|
||||
var effectivePresentationInterfaceState: (() -> ChatPresentationInterfaceState?)?
|
||||
public var effectivePresentationInterfaceState: (() -> ChatPresentationInterfaceState?)?
|
||||
private var presentationInterfaceState: ChatPresentationInterfaceState?
|
||||
private var initializedPlaceholder = false
|
||||
|
||||
private let inputMenu: ChatTextInputMenu
|
||||
private let inputMenu: TextInputMenu
|
||||
|
||||
private var theme: PresentationTheme?
|
||||
private var strings: PresentationStrings?
|
||||
|
||||
private let hapticFeedback = HapticFeedback()
|
||||
|
||||
var inputTextState: ChatTextInputState {
|
||||
public var inputTextState: ChatTextInputState {
|
||||
if let textInputNode = self.textInputNode {
|
||||
let selectionRange: Range<Int> = textInputNode.selectedRange.location ..< (textInputNode.selectedRange.location + textInputNode.selectedRange.length)
|
||||
return ChatTextInputState(inputText: stateAttributedStringForText(textInputNode.attributedText ?? NSAttributedString()), selectionRange: selectionRange)
|
||||
|
|
@ -177,24 +183,16 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
}
|
||||
}
|
||||
|
||||
override var context: AccountContext? {
|
||||
didSet {
|
||||
self.actionButtons.micButton.account = self.context?.account
|
||||
}
|
||||
}
|
||||
|
||||
var micButton: ChatTextInputMediaRecordingButton? {
|
||||
return self.actionButtons.micButton
|
||||
}
|
||||
public var interfaceInteraction: ChatPanelInterfaceInteraction?
|
||||
|
||||
func updateSendButtonEnabled(_ enabled: Bool, animated: Bool) {
|
||||
public func updateSendButtonEnabled(_ enabled: Bool, animated: Bool) {
|
||||
self.actionButtons.isUserInteractionEnabled = enabled
|
||||
|
||||
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.2, curve: .easeInOut) : .immediate
|
||||
transition.updateAlpha(node: self.actionButtons, alpha: enabled ? 1.0 : 0.3)
|
||||
}
|
||||
|
||||
func updateInputTextState(_ state: ChatTextInputState, animated: Bool) {
|
||||
public func updateInputTextState(_ state: ChatTextInputState, animated: Bool) {
|
||||
if state.inputText.length != 0 && self.textInputNode == nil {
|
||||
self.loadTextInputNode()
|
||||
}
|
||||
|
|
@ -218,7 +216,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
}
|
||||
}
|
||||
|
||||
var text: String {
|
||||
public var text: String {
|
||||
get {
|
||||
return self.textInputNode?.attributedText?.string ?? ""
|
||||
} set(value) {
|
||||
|
|
@ -235,7 +233,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
}
|
||||
}
|
||||
|
||||
func caption() -> NSAttributedString {
|
||||
public func caption() -> NSAttributedString {
|
||||
return self.textInputNode?.attributedText ?? NSAttributedString()
|
||||
}
|
||||
|
||||
|
|
@ -243,15 +241,17 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
|
||||
private var spoilersRevealed = false
|
||||
|
||||
init(presentationInterfaceState: ChatPresentationInterfaceState, isCaption: Bool = false, presentController: @escaping (ViewController) -> Void) {
|
||||
public init(context: AccountContext, presentationInterfaceState: ChatPresentationInterfaceState, isCaption: Bool = false, isAttachment: Bool = false, presentController: @escaping (ViewController) -> Void) {
|
||||
self.context = context
|
||||
self.presentationInterfaceState = presentationInterfaceState
|
||||
self.isCaption = isCaption
|
||||
self.isAttachment = isAttachment
|
||||
|
||||
var hasSpoilers = true
|
||||
if presentationInterfaceState.chatLocation.peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
hasSpoilers = false
|
||||
}
|
||||
self.inputMenu = ChatTextInputMenu(hasSpoilers: hasSpoilers)
|
||||
self.inputMenu = TextInputMenu(hasSpoilers: hasSpoilers)
|
||||
|
||||
self.textInputContainerBackgroundNode = ASImageNode()
|
||||
self.textInputContainerBackgroundNode.isUserInteractionEnabled = false
|
||||
|
|
@ -275,7 +275,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
self.oneLineNode.maximumNumberOfLines = 1
|
||||
self.oneLineNode.isUserInteractionEnabled = false
|
||||
|
||||
self.actionButtons = ChatTextInputActionButtonsNode(presentationInterfaceState: presentationInterfaceState, presentationContext: nil, presentController: presentController)
|
||||
self.actionButtons = AttachmentTextInputActionButtonsNode(presentationInterfaceState: presentationInterfaceState, presentController: presentController)
|
||||
self.counterTextNode = ImmediateTextNode()
|
||||
self.counterTextNode.textAlignment = .center
|
||||
|
||||
|
|
@ -287,7 +287,6 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
|
||||
self.actionButtons.sendButton.addTarget(self, action: #selector(self.sendButtonPressed), forControlEvents: .touchUpInside)
|
||||
self.actionButtons.sendButton.alpha = 1.0
|
||||
self.actionButtons.micButton.alpha = 0.0
|
||||
self.actionButtons.expandMediaInputButton.alpha = 0.0
|
||||
self.actionButtons.updateAccessibility()
|
||||
|
||||
|
|
@ -313,12 +312,12 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
}
|
||||
self.textInputBackgroundNode.view.addGestureRecognizer(recognizer)
|
||||
|
||||
self.updateSendButtonEnabled(isCaption, animated: false)
|
||||
self.updateSendButtonEnabled(isCaption || isAttachment, animated: false)
|
||||
}
|
||||
|
||||
var sendPressed: ((NSAttributedString?) -> Void)?
|
||||
var focusUpdated: ((Bool) -> Void)?
|
||||
var heightUpdated: ((Bool) -> Void)?
|
||||
public var sendPressed: ((NSAttributedString?) -> Void)?
|
||||
public var focusUpdated: ((Bool) -> Void)?
|
||||
public var heightUpdated: ((Bool) -> Void)?
|
||||
|
||||
public func updateLayoutSize(_ size: CGSize, sideInset: CGFloat) -> CGFloat {
|
||||
guard let presentationInterfaceState = self.presentationInterfaceState else {
|
||||
|
|
@ -345,7 +344,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func loadTextInputNodeIfNeeded() {
|
||||
public func loadTextInputNodeIfNeeded() {
|
||||
if self.textInputNode == nil {
|
||||
self.loadTextInputNode()
|
||||
}
|
||||
|
|
@ -459,7 +458,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
return result
|
||||
}
|
||||
|
||||
override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
let textFieldMinHeight = calclulateTextFieldMinHeight(interfaceState, metrics: metrics)
|
||||
var minimalHeight: CGFloat = 14.0 + textFieldMinHeight
|
||||
if case .regular = metrics.widthClass, case .regular = metrics.heightClass {
|
||||
|
|
@ -468,7 +467,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
return minimalHeight
|
||||
}
|
||||
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
public func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
let hadLayout = self.validLayout != nil
|
||||
let previousAdditionalSideInsets = self.validLayout?.3
|
||||
self.validLayout = (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, isSecondary)
|
||||
|
|
@ -550,7 +549,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
if themeUpdated || !self.initializedPlaceholder {
|
||||
self.initializedPlaceholder = true
|
||||
|
||||
let placeholder = self.isCaption ? interfaceState.strings.MediaPicker_AddCaption : interfaceState.strings.Conversation_InputTextPlaceholder
|
||||
let placeholder = self.isCaption || self.isAttachment ? interfaceState.strings.MediaPicker_AddCaption : interfaceState.strings.Conversation_InputTextPlaceholder
|
||||
|
||||
if self.currentPlaceholder != placeholder || themeUpdated {
|
||||
self.currentPlaceholder = placeholder
|
||||
|
|
@ -704,19 +703,11 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
|
||||
self.actionButtons.updateAccessibility()
|
||||
|
||||
if let prevInputPanelNode = self.prevInputPanelNode {
|
||||
prevInputPanelNode.frame = CGRect(origin: .zero, size: prevInputPanelNode.frame.size)
|
||||
}
|
||||
|
||||
return panelHeight
|
||||
}
|
||||
|
||||
override func canHandleTransition(from prevInputPanelNode: ChatInputPanelNode?) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
private var skipUpdate = false
|
||||
@objc func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) {
|
||||
@objc public func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) {
|
||||
if let textInputNode = self.textInputNode, let presentationInterfaceState = self.presentationInterfaceState {
|
||||
let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
|
||||
refreshChatTextInputAttributes(textInputNode, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed)
|
||||
|
|
@ -898,8 +889,8 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
|
||||
private func updateCounterTextNode(transition: ContainedViewLayoutTransition) {
|
||||
let inputTextMaxLength: Int32?
|
||||
if self.isCaption {
|
||||
inputTextMaxLength = self.context?.currentLimitsConfiguration.with { $0 }.maxMediaCaptionLength
|
||||
if self.isCaption || self.isAttachment {
|
||||
inputTextMaxLength = self.context.currentLimitsConfiguration.with { $0 }.maxMediaCaptionLength
|
||||
} else {
|
||||
inputTextMaxLength = nil
|
||||
}
|
||||
|
|
@ -938,11 +929,9 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
inputHasText = true
|
||||
}
|
||||
|
||||
if let _ = self.presentationInterfaceState {
|
||||
self.textPlaceholderNode.isHidden = inputHasText
|
||||
}
|
||||
|
||||
if let presentationInterfaceState = self.presentationInterfaceState {
|
||||
self.textPlaceholderNode.isHidden = inputHasText
|
||||
|
||||
let textColor = presentationInterfaceState.theme.chat.inputPanel.inputTextColor
|
||||
let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
|
||||
let textFont = Font.regular(baseFontSize)
|
||||
|
|
@ -999,7 +988,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
}
|
||||
}
|
||||
|
||||
@objc func editableTextNodeShouldReturn(_ editableTextNode: ASEditableTextNode) -> Bool {
|
||||
@objc public func editableTextNodeShouldReturn(_ editableTextNode: ASEditableTextNode) -> Bool {
|
||||
if self.actionButtons.sendButton.supernode != nil && !self.actionButtons.sendButton.isHidden && !self.actionButtons.sendButton.alpha.isZero {
|
||||
self.sendButtonPressed()
|
||||
}
|
||||
|
|
@ -1025,7 +1014,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
}
|
||||
}
|
||||
|
||||
@objc func editableTextNodeDidChangeSelection(_ editableTextNode: ASEditableTextNode, fromSelectedRange: NSRange, toSelectedRange: NSRange, dueToEditing: Bool) {
|
||||
@objc public func editableTextNodeDidChangeSelection(_ editableTextNode: ASEditableTextNode, fromSelectedRange: NSRange, toSelectedRange: NSRange, dueToEditing: Bool) {
|
||||
if !dueToEditing && !self.updatingInputState {
|
||||
let inputTextState = self.inputTextState
|
||||
self.skipUpdate = true
|
||||
|
|
@ -1045,7 +1034,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
}
|
||||
}
|
||||
|
||||
@objc func editableTextNodeDidBeginEditing(_ editableTextNode: ASEditableTextNode) {
|
||||
@objc public func editableTextNodeDidBeginEditing(_ editableTextNode: ASEditableTextNode) {
|
||||
self.interfaceInteraction?.updateInputModeAndDismissedButtonKeyboardMessageId({ state in
|
||||
return (.text, state.keyboardButtonsMessage?.id)
|
||||
})
|
||||
|
|
@ -1058,7 +1047,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
}
|
||||
}
|
||||
|
||||
func editableTextNodeDidFinishEditing(_ editableTextNode: ASEditableTextNode) {
|
||||
public func editableTextNodeDidFinishEditing(_ editableTextNode: ASEditableTextNode) {
|
||||
self.storedInputLanguage = editableTextNode.textInputMode.primaryLanguage
|
||||
self.inputMenu.deactivate()
|
||||
|
||||
|
|
@ -1069,7 +1058,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
}
|
||||
}
|
||||
|
||||
func editableTextNodeTarget(forAction action: Selector) -> ASEditableTextNodeTargetForAction? {
|
||||
public func editableTextNodeTarget(forAction action: Selector) -> ASEditableTextNodeTargetForAction? {
|
||||
if action == makeSelectorFromString("_accessibilitySpeak:") {
|
||||
if case .format = self.inputMenu.state {
|
||||
return ASEditableTextNodeTargetForAction(target: nil)
|
||||
|
|
@ -1196,7 +1185,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
self.updateSpoilersRevealed(animated: animated)
|
||||
}
|
||||
|
||||
@objc func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
||||
@objc public func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
||||
var cleanText = text
|
||||
let removeSequences: [String] = ["\u{202d}", "\u{202c}"]
|
||||
for sequence in removeSequences {
|
||||
|
|
@ -1229,7 +1218,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
return true
|
||||
}
|
||||
|
||||
@objc func editableTextNodeShouldCopy(_ editableTextNode: ASEditableTextNode) -> Bool {
|
||||
@objc public func editableTextNodeShouldCopy(_ editableTextNode: ASEditableTextNode) -> Bool {
|
||||
self.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in
|
||||
storeInputTextInPasteboard(current.inputText.attributedSubstring(from: NSMakeRange(current.selectionRange.lowerBound, current.selectionRange.count)))
|
||||
return (current, inputMode)
|
||||
|
|
@ -1237,7 +1226,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
return false
|
||||
}
|
||||
|
||||
@objc func editableTextNodeShouldPaste(_ editableTextNode: ASEditableTextNode) -> Bool {
|
||||
@objc public func editableTextNodeShouldPaste(_ editableTextNode: ASEditableTextNode) -> Bool {
|
||||
let pasteboard = UIPasteboard.general
|
||||
|
||||
var attributedString: NSAttributedString?
|
||||
|
|
@ -1264,8 +1253,8 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
|
||||
@objc func sendButtonPressed() {
|
||||
let inputTextMaxLength: Int32?
|
||||
if self.isCaption {
|
||||
inputTextMaxLength = self.context?.currentLimitsConfiguration.with { $0 }.maxMediaCaptionLength
|
||||
if self.isCaption || self.isAttachment {
|
||||
inputTextMaxLength = self.context.currentLimitsConfiguration.with { $0 }.maxMediaCaptionLength
|
||||
} else {
|
||||
inputTextMaxLength = nil
|
||||
}
|
||||
|
|
@ -1294,18 +1283,18 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
}
|
||||
}
|
||||
|
||||
var isFocused: Bool {
|
||||
public var isFocused: Bool {
|
||||
if self.imitateFocus {
|
||||
return true
|
||||
}
|
||||
return self.textInputNode?.isFirstResponder() ?? false
|
||||
}
|
||||
|
||||
func ensureUnfocused() {
|
||||
public func ensureUnfocused() {
|
||||
self.textInputNode?.resignFirstResponder()
|
||||
}
|
||||
|
||||
func ensureFocused() {
|
||||
public func ensureFocused() {
|
||||
self.imitateFocus = false
|
||||
|
||||
if self.textInputNode == nil {
|
||||
|
|
@ -1315,13 +1304,9 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||
self.textInputNode?.becomeFirstResponder()
|
||||
}
|
||||
|
||||
func frameForInputActionButton() -> CGRect? {
|
||||
public func frameForInputActionButton() -> CGRect? {
|
||||
if !self.actionButtons.alpha.isZero {
|
||||
if self.actionButtons.micButton.alpha.isZero {
|
||||
return self.actionButtons.frame.insetBy(dx: 0.0, dy: 6.0).offsetBy(dx: 4.0, dy: 0.0)
|
||||
} else {
|
||||
return self.actionButtons.frame.insetBy(dx: 0.0, dy: 6.0).offsetBy(dx: 2.0, dy: 0.0)
|
||||
}
|
||||
return self.actionButtons.frame.insetBy(dx: 0.0, dy: 6.0).offsetBy(dx: 4.0, dy: 0.0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
34
submodules/AttachmentUI/BUILD
Normal file
34
submodules/AttachmentUI/BUILD
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "AttachmentUI",
|
||||
module_name = "AttachmentUI",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/Postbox:Postbox",
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/LegacyComponents:LegacyComponents",
|
||||
"//submodules/TelegramStringFormatting:TelegramStringFormatting",
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
"//submodules/ComponentFlow:ComponentFlow",
|
||||
"//submodules/UIKitRuntimeUtils:UIKitRuntimeUtils",
|
||||
"//submodules/DirectionalPanGesture:DirectionalPanGesture",
|
||||
"//submodules/AttachmentTextInputPanelNode:AttachmentTextInputPanelNode",
|
||||
"//submodules/ChatSendMessageActionUI:ChatSendMessageActionUI",
|
||||
"//submodules/ChatTextLinkEditUI:ChatTextLinkEditUI",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
511
submodules/AttachmentUI/Sources/AttachmentContainer.swift
Normal file
511
submodules/AttachmentUI/Sources/AttachmentContainer.swift
Normal file
|
|
@ -0,0 +1,511 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import UIKitRuntimeUtils
|
||||
import Display
|
||||
import DirectionalPanGesture
|
||||
import TelegramPresentationData
|
||||
|
||||
final class AttachmentContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
let wrappingNode: ASDisplayNode
|
||||
let clipNode: ASDisplayNode
|
||||
let container: NavigationContainer
|
||||
|
||||
private(set) var isReady: Bool = false
|
||||
private(set) var dismissProgress: CGFloat = 0.0
|
||||
var isReadyUpdated: (() -> Void)?
|
||||
var updateDismissProgress: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
|
||||
var interactivelyDismissed: (() -> Void)?
|
||||
|
||||
var updateModalProgress: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
|
||||
|
||||
private var isUpdatingState = false
|
||||
private var isDismissed = false
|
||||
private var isInteractiveDimissEnabled = true
|
||||
|
||||
public private(set) var isExpanded = false
|
||||
|
||||
private var validLayout: (layout: ContainerViewLayout, controllers: [AttachmentContainable], coveredByModalTransition: CGFloat)?
|
||||
|
||||
var keyboardViewManager: KeyboardViewManager? {
|
||||
didSet {
|
||||
if self.keyboardViewManager !== oldValue {
|
||||
self.container.keyboardViewManager = self.keyboardViewManager
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var canHaveKeyboardFocus: Bool = false {
|
||||
didSet {
|
||||
self.container.canHaveKeyboardFocus = self.canHaveKeyboardFocus
|
||||
}
|
||||
}
|
||||
|
||||
private var panGestureRecognizer: UIPanGestureRecognizer?
|
||||
|
||||
init(presentationData: PresentationData) {
|
||||
self.wrappingNode = ASDisplayNode()
|
||||
self.clipNode = ASDisplayNode()
|
||||
self.clipNode.backgroundColor = presentationData.theme.list.plainBackgroundColor
|
||||
|
||||
self.container = NavigationContainer(controllerRemoved: { _ in })
|
||||
self.container.clipsToBounds = true
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.wrappingNode)
|
||||
self.wrappingNode.addSubnode(self.clipNode)
|
||||
self.clipNode.addSubnode(self.container)
|
||||
|
||||
self.isReady = self.container.isReady
|
||||
self.container.isReadyUpdated = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if !strongSelf.isReady {
|
||||
strongSelf.isReady = true
|
||||
if !strongSelf.isUpdatingState {
|
||||
strongSelf.isReadyUpdated?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
applySmoothRoundedCorners(self.container.layer)
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
let panRecognizer = DirectionalPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
|
||||
panRecognizer.delegate = self
|
||||
panRecognizer.delaysTouchesBegan = false
|
||||
panRecognizer.cancelsTouchesInView = true
|
||||
self.panGestureRecognizer = panRecognizer
|
||||
self.wrappingNode.view.addGestureRecognizer(panRecognizer)
|
||||
}
|
||||
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
if gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer is UIPanGestureRecognizer {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private var panGestureArguments: (topInset: CGFloat, offset: CGFloat, scrollView: UIScrollView?, listNode: ListView?)?
|
||||
|
||||
let defaultTopInset: CGFloat = 210.0
|
||||
@objc func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||
guard let (layout, controllers, coveredByModalTransition) = self.validLayout else {
|
||||
return
|
||||
}
|
||||
|
||||
let isLandscape = layout.orientation == .landscape
|
||||
let edgeTopInset = isLandscape ? 0.0 : defaultTopInset
|
||||
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
let point = recognizer.location(in: self.view)
|
||||
let currentHitView = self.hitTest(point, with: nil)
|
||||
let scrollViewAndListNode = self.findScrollView(view: currentHitView)
|
||||
let scrollView = scrollViewAndListNode?.0
|
||||
let listNode = scrollViewAndListNode?.1
|
||||
|
||||
let topInset: CGFloat
|
||||
if self.isExpanded {
|
||||
topInset = 0.0
|
||||
} else {
|
||||
topInset = edgeTopInset
|
||||
}
|
||||
|
||||
self.panGestureArguments = (topInset, 0.0, scrollView, listNode)
|
||||
case .changed:
|
||||
guard let (topInset, panOffset, scrollView, listNode) = self.panGestureArguments else {
|
||||
return
|
||||
}
|
||||
let visibleContentOffset = listNode?.visibleContentOffset()
|
||||
let contentOffset = scrollView?.contentOffset.y ?? 0.0
|
||||
|
||||
var translation = recognizer.translation(in: self.view).y
|
||||
|
||||
var currentOffset = topInset + translation
|
||||
|
||||
let epsilon = 1.0
|
||||
if case let .known(value) = visibleContentOffset, value <= epsilon {
|
||||
if let scrollView = scrollView {
|
||||
scrollView.bounces = false
|
||||
scrollView.setContentOffset(CGPoint(x: 0.0, y: 0.0), animated: false)
|
||||
}
|
||||
} else if let scrollView = scrollView, contentOffset <= -scrollView.contentInset.top + epsilon {
|
||||
scrollView.bounces = false
|
||||
scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false)
|
||||
} else if let scrollView = scrollView {
|
||||
translation = panOffset
|
||||
currentOffset = topInset + translation
|
||||
if self.isExpanded {
|
||||
recognizer.setTranslation(CGPoint(), in: self.view)
|
||||
} else if currentOffset > 0.0 {
|
||||
scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
self.panGestureArguments = (topInset, translation, scrollView, listNode)
|
||||
|
||||
|
||||
if !self.isExpanded {
|
||||
if currentOffset > 0.0, let scrollView = scrollView {
|
||||
scrollView.panGestureRecognizer.setTranslation(CGPoint(), in: scrollView)
|
||||
}
|
||||
|
||||
var bounds = self.bounds
|
||||
bounds.origin.y = -translation
|
||||
bounds.origin.y = min(0.0, bounds.origin.y)
|
||||
self.bounds = bounds
|
||||
}
|
||||
|
||||
self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: .immediate)
|
||||
case .ended:
|
||||
guard let (currentTopInset, panOffset, scrollView, listNode) = self.panGestureArguments else {
|
||||
return
|
||||
}
|
||||
let visibleContentOffset = listNode?.visibleContentOffset()
|
||||
let contentOffset = scrollView?.contentOffset.y ?? 0.0
|
||||
|
||||
let translation = recognizer.translation(in: self.view).y
|
||||
var velocity = recognizer.velocity(in: self.view)
|
||||
|
||||
if case let .known(value) = visibleContentOffset, value > 0.0 {
|
||||
velocity = CGPoint()
|
||||
} else if case .unknown = visibleContentOffset {
|
||||
velocity = CGPoint()
|
||||
} else if contentOffset > 0.0 {
|
||||
velocity = CGPoint()
|
||||
}
|
||||
|
||||
var bounds = self.bounds
|
||||
bounds.origin.y = -translation
|
||||
bounds.origin.y = min(0.0, bounds.origin.y)
|
||||
|
||||
scrollView?.bounces = true
|
||||
|
||||
let offset = currentTopInset + panOffset
|
||||
let topInset: CGFloat = edgeTopInset
|
||||
if self.isExpanded {
|
||||
self.panGestureArguments = nil
|
||||
if velocity.y > 300.0 || offset > topInset / 2.0 {
|
||||
self.isExpanded = false
|
||||
if let listNode = listNode {
|
||||
listNode.scroller.setContentOffset(CGPoint(), animated: false)
|
||||
} else if let scrollView = scrollView {
|
||||
scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false)
|
||||
}
|
||||
|
||||
let distance = topInset - offset
|
||||
let initialVelocity: CGFloat = distance.isZero ? 0.0 : abs(velocity.y / distance)
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.45, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity))
|
||||
self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: transition)
|
||||
} else {
|
||||
self.isExpanded = true
|
||||
|
||||
self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: .animated(duration: 0.3, curve: .easeInOut))
|
||||
}
|
||||
} else {
|
||||
self.panGestureArguments = nil
|
||||
|
||||
var dismissing = false
|
||||
if bounds.minY < -60 || (bounds.minY < 0.0 && velocity.y > 300.0) {
|
||||
self.interactivelyDismissed?()
|
||||
dismissing = true
|
||||
} else if (velocity.y < -300.0 || offset < topInset / 2.0) {
|
||||
if velocity.y > -2200.0, let listNode = listNode {
|
||||
DispatchQueue.main.async {
|
||||
listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
}
|
||||
}
|
||||
|
||||
let initialVelocity: CGFloat = offset.isZero ? 0.0 : abs(velocity.y / offset)
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.45, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity))
|
||||
self.isExpanded = true
|
||||
|
||||
self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: transition)
|
||||
} else {
|
||||
if let listNode = listNode {
|
||||
listNode.scroller.setContentOffset(CGPoint(), animated: false)
|
||||
} else if let scrollView = scrollView {
|
||||
scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false)
|
||||
}
|
||||
|
||||
self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: .animated(duration: 0.3, curve: .easeInOut))
|
||||
}
|
||||
|
||||
if !dismissing {
|
||||
var bounds = self.bounds
|
||||
let previousBounds = bounds
|
||||
bounds.origin.y = 0.0
|
||||
self.bounds = bounds
|
||||
self.layer.animateBounds(from: previousBounds, to: self.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
||||
}
|
||||
}
|
||||
case .cancelled:
|
||||
self.panGestureArguments = nil
|
||||
|
||||
self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: .animated(duration: 0.3, curve: .easeInOut))
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func checkInteractiveDismissWithControllers() -> Bool {
|
||||
if let controller = self.container.controllers.last {
|
||||
if !controller.attemptNavigation({
|
||||
}) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func update(isExpanded: Bool, transition: ContainedViewLayoutTransition) {
|
||||
guard isExpanded != self.isExpanded else {
|
||||
return
|
||||
}
|
||||
self.isExpanded = isExpanded
|
||||
|
||||
guard let (layout, controllers, coveredByModalTransition) = self.validLayout else {
|
||||
return
|
||||
}
|
||||
self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: transition)
|
||||
}
|
||||
|
||||
func update(layout: ContainerViewLayout, controllers: [AttachmentContainable], coveredByModalTransition: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
if self.isDismissed {
|
||||
return
|
||||
}
|
||||
self.isUpdatingState = true
|
||||
|
||||
self.validLayout = (layout, controllers, coveredByModalTransition)
|
||||
|
||||
self.panGestureRecognizer?.isEnabled = (layout.inputHeight == nil || layout.inputHeight == 0.0)
|
||||
// self.scrollNode.view.isScrollEnabled = (layout.inputHeight == nil || layout.inputHeight == 0.0) && self.isInteractiveDimissEnabled
|
||||
|
||||
let isLandscape = layout.orientation == .landscape
|
||||
let edgeTopInset = isLandscape ? 0.0 : defaultTopInset
|
||||
|
||||
let topInset: CGFloat
|
||||
if let (panInitialTopInset, panOffset, _, _) = self.panGestureArguments {
|
||||
if self.isExpanded {
|
||||
topInset = min(edgeTopInset, panInitialTopInset + max(0.0, panOffset))
|
||||
} else {
|
||||
topInset = max(0.0, panInitialTopInset + min(0.0, panOffset))
|
||||
}
|
||||
} else {
|
||||
topInset = self.isExpanded ? 0.0 : edgeTopInset
|
||||
}
|
||||
transition.updateFrame(node: self.wrappingNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: layout.size))
|
||||
|
||||
let modalProgress = isLandscape ? 0.0 : (1.0 - topInset / defaultTopInset)
|
||||
self.updateModalProgress?(modalProgress, transition)
|
||||
|
||||
let containerLayout: ContainerViewLayout
|
||||
let containerFrame: CGRect
|
||||
let clipFrame: CGRect
|
||||
let containerScale: CGFloat
|
||||
if layout.metrics.widthClass == .compact {
|
||||
self.clipNode.clipsToBounds = true
|
||||
|
||||
if isLandscape {
|
||||
self.clipNode.cornerRadius = 0.0
|
||||
} else {
|
||||
self.clipNode.cornerRadius = 10.0
|
||||
}
|
||||
|
||||
if #available(iOS 11.0, *) {
|
||||
if layout.safeInsets.bottom.isZero {
|
||||
self.wrappingNode.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
} else {
|
||||
self.wrappingNode.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner]
|
||||
}
|
||||
}
|
||||
|
||||
var containerTopInset: CGFloat
|
||||
if isLandscape {
|
||||
containerTopInset = 0.0
|
||||
containerLayout = layout
|
||||
|
||||
let unscaledFrame = CGRect(origin: CGPoint(), size: containerLayout.size)
|
||||
containerScale = 1.0
|
||||
containerFrame = unscaledFrame
|
||||
clipFrame = unscaledFrame
|
||||
} else {
|
||||
containerTopInset = 10.0
|
||||
if let statusBarHeight = layout.statusBarHeight {
|
||||
containerTopInset += statusBarHeight
|
||||
}
|
||||
|
||||
let effectiveStatusBarHeight: CGFloat? = nil
|
||||
|
||||
let inset: CGFloat = 70.0
|
||||
var safeInsets = layout.safeInsets
|
||||
safeInsets.left += inset
|
||||
safeInsets.right += inset
|
||||
|
||||
var intrinsicInsets = layout.intrinsicInsets
|
||||
intrinsicInsets.left += inset
|
||||
intrinsicInsets.right += inset
|
||||
|
||||
containerLayout = ContainerViewLayout(size: CGSize(width: layout.size.width + inset * 2.0, height: layout.size.height - containerTopInset), metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(top: 0.0, left: intrinsicInsets.left, bottom: layout.intrinsicInsets.bottom + 49.0, right: intrinsicInsets.right), safeInsets: UIEdgeInsets(top: 0.0, left: safeInsets.left, bottom: safeInsets.bottom, right: safeInsets.right), additionalInsets: layout.additionalInsets, statusBarHeight: effectiveStatusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver)
|
||||
let unscaledFrame = CGRect(origin: CGPoint(x: 0.0, y: containerTopInset - coveredByModalTransition * 10.0), size: containerLayout.size)
|
||||
let maxScale: CGFloat = (containerLayout.size.width - 16.0 * 2.0) / containerLayout.size.width
|
||||
containerScale = 1.0 * (1.0 - coveredByModalTransition) + maxScale * coveredByModalTransition
|
||||
let maxScaledTopInset: CGFloat = containerTopInset - 10.0
|
||||
let scaledTopInset: CGFloat = containerTopInset * (1.0 - coveredByModalTransition) + maxScaledTopInset * coveredByModalTransition
|
||||
containerFrame = unscaledFrame.offsetBy(dx: -inset, dy: scaledTopInset - (unscaledFrame.midY - containerScale * unscaledFrame.height / 2.0))
|
||||
|
||||
clipFrame = CGRect(x: containerFrame.minX + inset, y: containerFrame.minY, width: containerFrame.width - inset * 2.0, height: containerFrame.height)
|
||||
}
|
||||
} else {
|
||||
self.clipNode.clipsToBounds = true
|
||||
self.clipNode.cornerRadius = 10.0
|
||||
if #available(iOS 11.0, *) {
|
||||
self.clipNode.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner]
|
||||
}
|
||||
|
||||
let verticalInset: CGFloat = 44.0
|
||||
|
||||
let maxSide = max(layout.size.width, layout.size.height)
|
||||
let minSide = min(layout.size.width, layout.size.height)
|
||||
let containerSize = CGSize(width: min(layout.size.width - 20.0, floor(maxSide / 2.0)), height: min(layout.size.height, minSide) - verticalInset * 2.0)
|
||||
containerFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - containerSize.width) / 2.0), y: floor((layout.size.height - containerSize.height) / 2.0)), size: containerSize)
|
||||
containerScale = 1.0
|
||||
clipFrame = containerFrame
|
||||
|
||||
var inputHeight: CGFloat?
|
||||
if let inputHeightValue = layout.inputHeight {
|
||||
inputHeight = max(0.0, inputHeightValue - (layout.size.height - containerFrame.maxY))
|
||||
}
|
||||
|
||||
let effectiveStatusBarHeight: CGFloat? = nil
|
||||
|
||||
containerLayout = ContainerViewLayout(size: containerSize, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), additionalInsets: UIEdgeInsets(), statusBarHeight: effectiveStatusBarHeight, inputHeight: inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver)
|
||||
}
|
||||
transition.updateFrameAsPositionAndBounds(node: self.clipNode, frame: clipFrame)
|
||||
transition.updateFrameAsPositionAndBounds(node: self.container, frame: CGRect(origin: CGPoint(x: containerFrame.minX, y: 0.0), size: containerFrame.size))
|
||||
transition.updateTransformScale(node: self.container, scale: containerScale)
|
||||
self.container.update(layout: containerLayout, canBeClosed: true, controllers: controllers, transition: transition)
|
||||
|
||||
self.isUpdatingState = false
|
||||
}
|
||||
|
||||
func dismiss(transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) -> ContainedViewLayoutTransition {
|
||||
for controller in self.container.controllers {
|
||||
controller.viewWillDisappear(transition.isAnimated)
|
||||
}
|
||||
|
||||
if let firstController = self.container.controllers.first, case .standaloneModal = firstController.navigationPresentation {
|
||||
for controller in self.container.controllers {
|
||||
controller.setIgnoreAppearanceMethodInvocations(true)
|
||||
controller.displayNode.removeFromSupernode()
|
||||
controller.setIgnoreAppearanceMethodInvocations(false)
|
||||
controller.viewDidDisappear(transition.isAnimated)
|
||||
}
|
||||
completion()
|
||||
return transition
|
||||
} else {
|
||||
if transition.isAnimated {
|
||||
let positionTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut)
|
||||
positionTransition.updatePosition(node: self.container, position: CGPoint(x: self.container.position.x, y: self.bounds.height + self.container.bounds.height / 2.0 + self.bounds.height), beginWithCurrentState: true, completion: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
for controller in strongSelf.container.controllers {
|
||||
controller.viewDidDisappear(transition.isAnimated)
|
||||
}
|
||||
completion()
|
||||
})
|
||||
return positionTransition
|
||||
} else {
|
||||
for controller in self.container.controllers {
|
||||
controller.setIgnoreAppearanceMethodInvocations(true)
|
||||
controller.displayNode.removeFromSupernode()
|
||||
controller.setIgnoreAppearanceMethodInvocations(false)
|
||||
controller.viewDidDisappear(transition.isAnimated)
|
||||
}
|
||||
completion()
|
||||
return transition
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func findScrollView(view: UIView?) -> (UIScrollView, ListView?)? {
|
||||
if let view = view {
|
||||
if let view = view as? UIScrollView {
|
||||
return (view, nil)
|
||||
}
|
||||
if let node = view.asyncdisplaykit_node as? ListView {
|
||||
return (node.scroller, node)
|
||||
}
|
||||
return findScrollView(view: view.superview)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
||||
if !self.wrappingNode.frame.contains(point) {
|
||||
return false
|
||||
}
|
||||
return super.point(inside: point, with: event)
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
guard let result = super.hitTest(point, with: event) else {
|
||||
return nil
|
||||
}
|
||||
// var currentParent: UIView? = result
|
||||
// var enableScrolling = true
|
||||
// while true {
|
||||
// if currentParent == nil {
|
||||
// break
|
||||
// }
|
||||
// if currentParent is UIKeyInput {
|
||||
// if currentParent?.disablesInteractiveModalDismiss == true {
|
||||
// enableScrolling = false
|
||||
// break
|
||||
// }
|
||||
// } else if let scrollView = currentParent as? UIScrollView {
|
||||
// if scrollView === self.scrollNode.view {
|
||||
// break
|
||||
// }
|
||||
// if scrollView.disablesInteractiveModalDismiss {
|
||||
// enableScrolling = false
|
||||
// break
|
||||
// } else {
|
||||
// if scrollView.isDecelerating && scrollView.contentOffset.y < -scrollView.contentInset.top {
|
||||
// return self.scrollNode.view
|
||||
// }
|
||||
// }
|
||||
// } else if let listView = currentParent as? ListViewBackingView, let listNode = listView.target {
|
||||
// if listNode.view.disablesInteractiveModalDismiss {
|
||||
// enableScrolling = false
|
||||
// break
|
||||
// } else if listNode.scroller.isDecelerating && listNode.scroller.contentOffset.y < listNode.scroller.contentInset.top {
|
||||
// return self.scrollNode.view
|
||||
// }
|
||||
// }
|
||||
// currentParent = currentParent?.superview
|
||||
// }
|
||||
// if let controller = self.container.controllers.last {
|
||||
// if controller.view.disablesInteractiveModalDismiss {
|
||||
// enableScrolling = false
|
||||
// }
|
||||
// }
|
||||
// self.isInteractiveDimissEnabled = enableScrolling
|
||||
// if let layout = self.validLayout {
|
||||
// if layout.inputHeight != nil && layout.inputHeight != 0.0 {
|
||||
// enableScrolling = false
|
||||
// }
|
||||
// }
|
||||
// self.scrollNode.view.isScrollEnabled = enableScrolling
|
||||
return result
|
||||
}
|
||||
}
|
||||
373
submodules/AttachmentUI/Sources/AttachmentController.swift
Normal file
373
submodules/AttachmentUI/Sources/AttachmentController.swift
Normal file
|
|
@ -0,0 +1,373 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import AccountContext
|
||||
import TelegramStringFormatting
|
||||
import UIKitRuntimeUtils
|
||||
|
||||
public enum AttachmentButtonType: Equatable {
|
||||
case camera
|
||||
case gallery
|
||||
case file
|
||||
case location
|
||||
case contact
|
||||
case poll
|
||||
case app(String)
|
||||
}
|
||||
|
||||
public protocol AttachmentContainable: ViewController {
|
||||
var requestAttachmentMenuExpansion: () -> Void { get set }
|
||||
}
|
||||
|
||||
public protocol AttachmentMediaPickerContext {
|
||||
var selectionCount: Signal<Int, NoError> { get }
|
||||
var caption: Signal<NSAttributedString?, NoError> { get }
|
||||
|
||||
func setCaption(_ caption: NSAttributedString)
|
||||
func send(silently: Bool)
|
||||
func schedule()
|
||||
}
|
||||
|
||||
public class AttachmentController: ViewController {
|
||||
private let context: AccountContext
|
||||
private let buttons: [AttachmentButtonType]
|
||||
|
||||
private final class Node: ASDisplayNode {
|
||||
private weak var controller: AttachmentController?
|
||||
private let dim: ASDisplayNode
|
||||
private let container: AttachmentContainer
|
||||
let panel: AttachmentPanel
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
private var modalProgress: CGFloat = 0.0
|
||||
|
||||
private var currentType: AttachmentButtonType?
|
||||
private var currentController: AttachmentContainable?
|
||||
|
||||
private let captionDisposable = MetaDisposable()
|
||||
|
||||
private let mediaSelectionCountDisposable = MetaDisposable()
|
||||
private var mediaPickerContext: AttachmentMediaPickerContext? {
|
||||
didSet {
|
||||
if let mediaPickerContext = self.mediaPickerContext {
|
||||
self.captionDisposable.set((mediaPickerContext.caption
|
||||
|> deliverOnMainQueue).start(next: { [weak self] caption in
|
||||
if let strongSelf = self {
|
||||
strongSelf.panel.updateCaption(caption ?? NSAttributedString())
|
||||
}
|
||||
}))
|
||||
self.mediaSelectionCountDisposable.set((mediaPickerContext.selectionCount
|
||||
|> deliverOnMainQueue).start(next: { [weak self] count in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateSelectionCount(count)
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
self.updateSelectionCount(0)
|
||||
self.mediaSelectionCountDisposable.set(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(controller: AttachmentController) {
|
||||
self.controller = controller
|
||||
|
||||
self.dim = ASDisplayNode()
|
||||
self.dim.alpha = 0.0
|
||||
self.dim.backgroundColor = UIColor(white: 0.0, alpha: 0.25)
|
||||
|
||||
let presentationData = controller.context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.container = AttachmentContainer(presentationData: presentationData)
|
||||
self.container.canHaveKeyboardFocus = true
|
||||
self.panel = AttachmentPanel(context: controller.context)
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.dim)
|
||||
|
||||
self.container.updateModalProgress = { [weak self] progress, transition in
|
||||
if let strongSelf = self, let layout = strongSelf.validLayout {
|
||||
strongSelf.controller?.updateModalStyleOverlayTransitionFactor(progress, transition: transition)
|
||||
|
||||
strongSelf.modalProgress = progress
|
||||
strongSelf.containerLayoutUpdated(layout, transition: transition)
|
||||
}
|
||||
}
|
||||
self.container.isReadyUpdated = { [weak self] in
|
||||
if let strongSelf = self, let layout = strongSelf.validLayout {
|
||||
strongSelf.containerLayoutUpdated(layout, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
}
|
||||
|
||||
self.container.interactivelyDismissed = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.controller?.dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
self.panel.selectionChanged = { [weak self] type, ascending in
|
||||
if let strongSelf = self {
|
||||
strongSelf.switchToController(type, ascending)
|
||||
}
|
||||
}
|
||||
|
||||
self.panel.beganTextEditing = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.container.update(isExpanded: true, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
}
|
||||
|
||||
self.panel.textUpdated = { [weak self] text in
|
||||
if let strongSelf = self {
|
||||
strongSelf.mediaPickerContext?.setCaption(text)
|
||||
}
|
||||
}
|
||||
|
||||
self.panel.sendMessagePressed = { [weak self] mode in
|
||||
if let strongSelf = self {
|
||||
switch mode {
|
||||
case .generic:
|
||||
strongSelf.mediaPickerContext?.send(silently: false)
|
||||
case .silent:
|
||||
strongSelf.mediaPickerContext?.send(silently: true)
|
||||
case .schedule:
|
||||
strongSelf.mediaPickerContext?.schedule()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.panel.requestLayout = { [weak self] in
|
||||
if let strongSelf = self, let layout = strongSelf.validLayout {
|
||||
strongSelf.containerLayoutUpdated(layout, transition: .animated(duration: 0.2, curve: .easeInOut))
|
||||
}
|
||||
}
|
||||
|
||||
self.panel.present = { [weak self] c in
|
||||
if let strongSelf = self {
|
||||
strongSelf.controller?.present(c, in: .window(.root))
|
||||
}
|
||||
}
|
||||
|
||||
self.panel.presentInGlobalOverlay = { [weak self] c in
|
||||
if let strongSelf = self {
|
||||
strongSelf.controller?.presentInGlobalOverlay(c, with: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.captionDisposable.dispose()
|
||||
self.mediaSelectionCountDisposable.dispose()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.dim.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
|
||||
|
||||
self.switchToController(.gallery, false)
|
||||
}
|
||||
|
||||
private var selectionCount: Int = 0
|
||||
private func updateSelectionCount(_ count: Int) {
|
||||
self.selectionCount = count
|
||||
if let layout = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
}
|
||||
|
||||
@objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
self.controller?.dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if let controller = self.controller, controller.isInteractionDisabled() {
|
||||
return self.view
|
||||
} else {
|
||||
return super.hitTest(point, with: event)
|
||||
}
|
||||
}
|
||||
|
||||
func dismiss(animated: Bool, completion: @escaping () -> Void = {}) {
|
||||
if animated {
|
||||
let positionTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut)
|
||||
positionTransition.updatePosition(node: self.container, position: CGPoint(x: self.container.position.x, y: self.bounds.height + self.container.bounds.height / 2.0 + self.bounds.height), beginWithCurrentState: true, completion: { [weak self] _ in
|
||||
let _ = self?.container.dismiss(transition: .immediate, completion: completion)
|
||||
})
|
||||
let alphaTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut)
|
||||
alphaTransition.updateAlpha(node: self.dim, alpha: 0.0)
|
||||
|
||||
self.controller?.updateModalStyleOverlayTransitionFactor(0.0, transition: positionTransition)
|
||||
} else {
|
||||
self.controller?.dismiss(animated: false, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
func switchToController(_ type: AttachmentButtonType, _ ascending: Bool) {
|
||||
guard self.currentType != type else {
|
||||
return
|
||||
}
|
||||
let previousType = self.currentType
|
||||
self.currentType = type
|
||||
self.controller?.requestController(type, { [weak self] controller, mediaPickerContext in
|
||||
if let strongSelf = self {
|
||||
strongSelf.mediaPickerContext = mediaPickerContext
|
||||
if let controller = controller {
|
||||
controller._presentedInModal = true
|
||||
controller.navigation_setPresenting(strongSelf.controller)
|
||||
controller.requestAttachmentMenuExpansion = { [weak self] in
|
||||
self?.container.update(isExpanded: true, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
|
||||
let animateTransition = previousType != nil
|
||||
strongSelf.currentController = controller
|
||||
|
||||
if animateTransition, let snapshotView = strongSelf.container.container.view.snapshotView(afterScreenUpdates: false) {
|
||||
snapshotView.frame = strongSelf.container.container.frame
|
||||
strongSelf.container.clipNode.view.addSubview(snapshotView)
|
||||
|
||||
let _ = (controller.ready.get()
|
||||
|> filter {
|
||||
$0
|
||||
}
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self, weak snapshotView] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
if ascending {
|
||||
strongSelf.container.container.view.layer.animatePosition(from: CGPoint(x: 70.0, y: 0.0), to: CGPoint(), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
} else {
|
||||
strongSelf.container.container.view.layer.animatePosition(from: CGPoint(x: -70.0, y: 0.0), to: CGPoint(), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
}
|
||||
|
||||
snapshotView?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
||||
snapshotView?.removeFromSuperview()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if let layout = strongSelf.validLayout {
|
||||
strongSelf.switchingController = true
|
||||
strongSelf.containerLayoutUpdated(layout, transition: .animated(duration: 0.4, curve: .spring))
|
||||
strongSelf.switchingController = false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func animateIn(transition: ContainedViewLayoutTransition) {
|
||||
ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear).updateAlpha(node: self.dim, alpha: 1.0)
|
||||
|
||||
transition.animatePositionAdditive(node: self.container, offset: CGPoint(x: 0.0, y: self.bounds.height + self.container.bounds.height / 2.0 - (self.container.position.y - self.bounds.height)))
|
||||
}
|
||||
|
||||
private var isCollapsed: Bool = false
|
||||
private var isUpdatingContainer = false
|
||||
private var switchingController = false
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = layout
|
||||
|
||||
transition.updateFrame(node: self.dim, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
|
||||
let containerTransition: ContainedViewLayoutTransition
|
||||
if self.container.supernode == nil {
|
||||
containerTransition = .immediate
|
||||
} else {
|
||||
containerTransition = transition
|
||||
}
|
||||
|
||||
if !self.isUpdatingContainer {
|
||||
self.isUpdatingContainer = true
|
||||
|
||||
let controllers = self.currentController.flatMap { [$0] } ?? []
|
||||
containerTransition.updateFrame(node: self.container, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
self.container.update(layout: layout, controllers: controllers, coveredByModalTransition: 0.0, transition: self.switchingController ? .immediate : transition)
|
||||
|
||||
if self.container.supernode == nil, !controllers.isEmpty && self.container.isReady {
|
||||
self.addSubnode(self.container)
|
||||
self.container.addSubnode(self.panel)
|
||||
|
||||
self.animateIn(transition: transition)
|
||||
}
|
||||
|
||||
self.isUpdatingContainer = false
|
||||
}
|
||||
|
||||
if self.modalProgress < 0.5 {
|
||||
self.isCollapsed = false
|
||||
} else if self.modalProgress == 1.0 {
|
||||
self.isCollapsed = true
|
||||
}
|
||||
|
||||
let isEffecitvelyCollapsedUpdated = (self.isCollapsed || self.selectionCount > 0) != (self.panel.isCollapsed || self.panel.isSelecting)
|
||||
let panelHeight = self.panel.update(layout: layout, buttons: self.controller?.buttons ?? [], isCollapsed: self.isCollapsed, isSelecting: self.selectionCount > 0, transition: transition)
|
||||
var panelTransition = transition
|
||||
if isEffecitvelyCollapsedUpdated {
|
||||
panelTransition = .animated(duration: 0.25, curve: .easeInOut)
|
||||
}
|
||||
panelTransition.updateFrame(node: self.panel, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - panelHeight), size: CGSize(width: layout.size.width, height: panelHeight)))
|
||||
}
|
||||
}
|
||||
|
||||
public var requestController: (AttachmentButtonType, @escaping (AttachmentContainable?, AttachmentMediaPickerContext?) -> Void) -> Void = { _, completion in
|
||||
completion(nil, nil)
|
||||
}
|
||||
|
||||
public init(context: AccountContext, buttons: [AttachmentButtonType]) {
|
||||
self.context = context
|
||||
self.buttons = buttons
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
self.blocksBackgroundWhenInOverlay = true
|
||||
self.acceptsFocusWhenInOverlay = true
|
||||
}
|
||||
|
||||
public required init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private var node: Node {
|
||||
return self.displayNode as! Node
|
||||
}
|
||||
|
||||
open override func loadDisplayNode() {
|
||||
self.displayNode = Node(controller: self)
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
public override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||
self.view.endEditing(true)
|
||||
if flag {
|
||||
self.node.dismiss(animated: true, completion: {
|
||||
super.dismiss(animated: flag, completion: {})
|
||||
completion?()
|
||||
})
|
||||
} else {
|
||||
super.dismiss(animated: false, completion: {})
|
||||
completion?()
|
||||
}
|
||||
}
|
||||
|
||||
private func isInteractionDisabled() -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
self.node.containerLayoutUpdated(layout, transition: transition)
|
||||
}
|
||||
}
|
||||
926
submodules/AttachmentUI/Sources/AttachmentPanel.swift
Normal file
926
submodules/AttachmentUI/Sources/AttachmentPanel.swift
Normal file
|
|
@ -0,0 +1,926 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
import AttachmentTextInputPanelNode
|
||||
import ChatPresentationInterfaceState
|
||||
import ChatSendMessageActionUI
|
||||
import ChatTextLinkEditUI
|
||||
|
||||
let panelButtonSize = CGSize(width: 80.0, height: 72.0)
|
||||
let smallPanelButtonSize = CGSize(width: 60.0, height: 49.0)
|
||||
|
||||
private let iconSize = CGSize(width: 54.0, height: 42.0)
|
||||
private let normalSideInset: CGFloat = 3.0
|
||||
private let smallSideInset: CGFloat = 0.0
|
||||
|
||||
private enum AttachmentButtonTransition {
|
||||
case transitionIn
|
||||
case selection
|
||||
}
|
||||
|
||||
private func generateBackgroundImage(colors: [UIColor]) -> UIImage? {
|
||||
return generateImage(iconSize, rotatedContext: { size, context in
|
||||
var locations: [CGFloat]
|
||||
if colors.count == 3 {
|
||||
locations = [1.0, 0.5, 0.0]
|
||||
} else {
|
||||
locations = [1.0, 0.0]
|
||||
}
|
||||
let colors: [CGColor] = colors.map { $0.cgColor }
|
||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
|
||||
|
||||
if colors.count == 2 {
|
||||
context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: size.height), options: .drawsAfterEndLocation)
|
||||
} else if colors.count == 3 {
|
||||
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: size.height), end: CGPoint(x: size.width, y: 0.0), options: .drawsAfterEndLocation)
|
||||
}
|
||||
// let center = CGPoint(x: 10.0, y: 10.0)
|
||||
// context.drawRadialGradient(gradient, startCenter: center, startRadius: 0.0, endCenter: center, endRadius: size.width, options: .drawsAfterEndLocation)
|
||||
})
|
||||
}
|
||||
|
||||
private let buttonGlowImage: UIImage? = {
|
||||
let inset: CGFloat = 6.0
|
||||
return generateImage(CGSize(width: iconSize.width + inset * 2.0, height: iconSize.height + inset * 2.0), rotatedContext: { size, context in
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
context.clear(bounds)
|
||||
|
||||
let rect = bounds.insetBy(dx: inset, dy: inset)
|
||||
let path = UIBezierPath(roundedRect: rect, cornerRadius: 21.0).cgPath
|
||||
context.addRect(bounds)
|
||||
context.addPath(path)
|
||||
context.clip(using: .evenOdd)
|
||||
|
||||
context.addPath(path)
|
||||
context.setShadow(offset: CGSize(), blur: 14.0, color: UIColor(rgb: 0xffffff, alpha: 0.8).cgColor)
|
||||
context.setFillColor(UIColor.white.cgColor)
|
||||
context.fillPath()
|
||||
})?.withRenderingMode(.alwaysTemplate)
|
||||
}()
|
||||
|
||||
private let buttonSelectionMaskImage: UIImage? = {
|
||||
let inset: CGFloat = 3.0
|
||||
return generateImage(CGSize(width: iconSize.width + inset * 2.0, height: iconSize.height + inset * 2.0), rotatedContext: { size, context in
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
context.clear(bounds)
|
||||
|
||||
let path = UIBezierPath(roundedRect: bounds, cornerRadius: 23.0).cgPath
|
||||
context.addPath(path)
|
||||
context.setFillColor(UIColor(rgb: 0xffffff).cgColor)
|
||||
context.fillPath()
|
||||
})?.withRenderingMode(.alwaysTemplate)
|
||||
}()
|
||||
|
||||
private final class AttachButtonComponent: CombinedComponent {
|
||||
let context: AccountContext
|
||||
let type: AttachmentButtonType
|
||||
let isSelected: Bool
|
||||
let isCollapsed: Bool
|
||||
let transitionFraction: CGFloat
|
||||
let strings: PresentationStrings
|
||||
let theme: PresentationTheme
|
||||
let action: () -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
type: AttachmentButtonType,
|
||||
isSelected: Bool,
|
||||
isCollapsed: Bool,
|
||||
transitionFraction: CGFloat,
|
||||
strings: PresentationStrings,
|
||||
theme: PresentationTheme,
|
||||
action: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.type = type
|
||||
self.isSelected = isSelected
|
||||
self.isCollapsed = isCollapsed
|
||||
self.transitionFraction = transitionFraction
|
||||
self.strings = strings
|
||||
self.theme = theme
|
||||
self.action = action
|
||||
}
|
||||
|
||||
static func ==(lhs: AttachButtonComponent, rhs: AttachButtonComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.type != rhs.type {
|
||||
return false
|
||||
}
|
||||
if lhs.isSelected != rhs.isSelected {
|
||||
return false
|
||||
}
|
||||
if lhs.isCollapsed != rhs.isCollapsed {
|
||||
return false
|
||||
}
|
||||
if lhs.transitionFraction != rhs.transitionFraction {
|
||||
return false
|
||||
}
|
||||
if lhs.strings !== rhs.strings {
|
||||
return false
|
||||
}
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let icon = Child(AttachButtonIconComponent.self)
|
||||
let title = Child(Text.self)
|
||||
|
||||
return { context in
|
||||
let name: String
|
||||
let animationName: String?
|
||||
let imageName: String?
|
||||
let backgroundColors: [UIColor]
|
||||
let foregroundColor: UIColor = .white
|
||||
|
||||
let isCollapsed = context.component.isCollapsed
|
||||
|
||||
switch context.component.type {
|
||||
case .camera:
|
||||
name = context.component.strings.Attachment_Camera
|
||||
animationName = "anim_camera"
|
||||
imageName = "Chat/Attach Menu/Camera"
|
||||
backgroundColors = [UIColor(rgb: 0xba4aae), UIColor(rgb: 0xdd4e6f), UIColor(rgb: 0xf4b76c)]
|
||||
case .gallery:
|
||||
name = context.component.strings.Attachment_Gallery
|
||||
animationName = "anim_gallery"
|
||||
imageName = "Chat/Attach Menu/Gallery"
|
||||
backgroundColors = [UIColor(rgb: 0x2071f1), UIColor(rgb: 0x1bc9fa)]
|
||||
case .file:
|
||||
name = context.component.strings.Attachment_File
|
||||
animationName = "anim_file"
|
||||
imageName = "Chat/Attach Menu/File"
|
||||
backgroundColors = [UIColor(rgb: 0xed705d), UIColor(rgb: 0xffa14c)]
|
||||
case .location:
|
||||
name = context.component.strings.Attachment_Location
|
||||
animationName = "anim_location"
|
||||
imageName = "Chat/Attach Menu/Location"
|
||||
backgroundColors = [UIColor(rgb: 0x5fb84f), UIColor(rgb: 0x99de6f)]
|
||||
case .contact:
|
||||
name = context.component.strings.Attachment_Contact
|
||||
animationName = "anim_contact"
|
||||
imageName = "Chat/Attach Menu/Contact"
|
||||
backgroundColors = [UIColor(rgb: 0xaa47d6), UIColor(rgb: 0xd67cf4)]
|
||||
case .poll:
|
||||
name = context.component.strings.Attachment_Poll
|
||||
animationName = "anim_poll"
|
||||
imageName = "Chat/Attach Menu/Poll"
|
||||
backgroundColors = [UIColor(rgb: 0xe9484f), UIColor(rgb: 0xee707e)]
|
||||
case let .app(appName):
|
||||
name = appName
|
||||
animationName = nil
|
||||
imageName = nil
|
||||
backgroundColors = [UIColor(rgb: 0x000000), UIColor(rgb: 0x000000)]
|
||||
}
|
||||
|
||||
let icon = icon.update(
|
||||
component: AttachButtonIconComponent(
|
||||
animationName: animationName,
|
||||
imageName: imageName,
|
||||
isSelected: context.component.isSelected,
|
||||
backgroundColors: backgroundColors,
|
||||
foregroundColor: foregroundColor,
|
||||
theme: context.component.theme,
|
||||
context: context.component.context,
|
||||
action: context.component.action
|
||||
),
|
||||
availableSize: iconSize,
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let title = title.update(
|
||||
component: Text(
|
||||
text: name,
|
||||
font: Font.regular(11.0),
|
||||
color: context.component.theme.actionSheet.primaryTextColor
|
||||
),
|
||||
availableSize: context.availableSize,
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
let topInset: CGFloat = 8.0
|
||||
let spacing: CGFloat = 3.0 + UIScreenPixel
|
||||
|
||||
let normalIconScale = isCollapsed ? 0.7 : 1.0
|
||||
let smallIconScale = isCollapsed ? 0.5 : 0.6
|
||||
|
||||
let iconScale = normalIconScale - (normalIconScale - smallIconScale) * abs(context.component.transitionFraction)
|
||||
let iconOffset: CGFloat = (isCollapsed ? 10.0 : 20.0) * context.component.transitionFraction
|
||||
let iconFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((context.availableSize.width - icon.size.width) / 2.0) + iconOffset, y: isCollapsed ? 3.0 : topInset), size: icon.size)
|
||||
var titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((context.availableSize.width - title.size.width) / 2.0) + iconOffset, y: iconFrame.midY + (iconFrame.height * 0.5 * iconScale) + spacing), size: title.size)
|
||||
if isCollapsed {
|
||||
titleFrame.origin.y = floorToScreenPixels(iconFrame.midY - title.size.height / 2.0)
|
||||
}
|
||||
|
||||
context.add(title
|
||||
.position(CGPoint(x: titleFrame.midX, y: titleFrame.midY))
|
||||
.opacity(isCollapsed ? 0.0 : 1.0 - abs(context.component.transitionFraction))
|
||||
)
|
||||
|
||||
context.add(icon
|
||||
.position(CGPoint(x: iconFrame.midX, y: iconFrame.midY))
|
||||
.scale(iconScale)
|
||||
)
|
||||
|
||||
return context.availableSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class AttachButtonIconComponent: Component {
|
||||
let animationName: String?
|
||||
let imageName: String?
|
||||
let isSelected: Bool
|
||||
let backgroundColors: [UIColor]
|
||||
let foregroundColor: UIColor
|
||||
let theme: PresentationTheme
|
||||
let context: AccountContext
|
||||
|
||||
let action: () -> Void
|
||||
|
||||
init(
|
||||
animationName: String?,
|
||||
imageName: String?,
|
||||
isSelected: Bool,
|
||||
backgroundColors: [UIColor],
|
||||
foregroundColor: UIColor,
|
||||
theme: PresentationTheme,
|
||||
context: AccountContext,
|
||||
action: @escaping () -> Void
|
||||
) {
|
||||
self.animationName = animationName
|
||||
self.imageName = imageName
|
||||
self.isSelected = isSelected
|
||||
self.backgroundColors = backgroundColors
|
||||
self.foregroundColor = foregroundColor
|
||||
self.theme = theme
|
||||
self.context = context
|
||||
self.action = action
|
||||
}
|
||||
|
||||
static func ==(lhs: AttachButtonIconComponent, rhs: AttachButtonIconComponent) -> Bool {
|
||||
if lhs.animationName != rhs.animationName {
|
||||
return false
|
||||
}
|
||||
if lhs.imageName != rhs.imageName {
|
||||
return false
|
||||
}
|
||||
if lhs.isSelected != rhs.isSelected {
|
||||
return false
|
||||
}
|
||||
if lhs.backgroundColors != rhs.backgroundColors {
|
||||
return false
|
||||
}
|
||||
if lhs.foregroundColor != rhs.foregroundColor {
|
||||
return false
|
||||
}
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: HighlightTrackingButton {
|
||||
private let containerView: UIView
|
||||
private let glowView: UIImageView
|
||||
private let selectionView: UIImageView
|
||||
private let backgroundView: UIView
|
||||
private let iconView: UIImageView
|
||||
private let highlightView: UIView
|
||||
|
||||
private var action: (() -> Void)?
|
||||
|
||||
private var currentColors: [UIColor] = []
|
||||
private var currentImageName: String?
|
||||
private var currentIsSelected: Bool?
|
||||
|
||||
private let hapticFeedback = HapticFeedback()
|
||||
|
||||
init() {
|
||||
self.containerView = UIView()
|
||||
self.containerView.isUserInteractionEnabled = false
|
||||
|
||||
self.glowView = UIImageView()
|
||||
self.glowView.image = buttonGlowImage
|
||||
self.glowView.isUserInteractionEnabled = false
|
||||
|
||||
self.selectionView = UIImageView()
|
||||
self.selectionView.image = buttonSelectionMaskImage
|
||||
self.selectionView.isUserInteractionEnabled = false
|
||||
|
||||
self.backgroundView = UIView()
|
||||
self.backgroundView.clipsToBounds = true
|
||||
self.backgroundView.isUserInteractionEnabled = false
|
||||
self.backgroundView.layer.cornerRadius = 21.0
|
||||
|
||||
self.iconView = UIImageView()
|
||||
|
||||
self.highlightView = UIView()
|
||||
self.highlightView.alpha = 0.0
|
||||
self.highlightView.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.1)
|
||||
self.highlightView.isUserInteractionEnabled = false
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.addSubview(self.containerView)
|
||||
self.containerView.addSubview(self.glowView)
|
||||
self.containerView.addSubview(self.selectionView)
|
||||
self.containerView.addSubview(self.backgroundView)
|
||||
self.backgroundView.addSubview(self.iconView)
|
||||
self.backgroundView.addSubview(self.highlightView)
|
||||
|
||||
self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
||||
self.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
strongSelf.containerView.layer.animateScale(from: 1.0, to: 0.9, duration: 0.3, removeOnCompletion: false)
|
||||
|
||||
strongSelf.highlightView.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.highlightView.alpha = 1.0
|
||||
|
||||
strongSelf.hapticFeedback.impact(.click05)
|
||||
} else {
|
||||
if let presentationLayer = strongSelf.containerView.layer.presentation() {
|
||||
strongSelf.containerView.layer.animateScale(from: CGFloat((presentationLayer.value(forKeyPath: "transform.scale.y") as? NSNumber)?.floatValue ?? 1.0), to: 1.0, duration: 0.2, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
strongSelf.highlightView.alpha = 0.0
|
||||
strongSelf.highlightView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
|
||||
|
||||
strongSelf.hapticFeedback.impact(.click06)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
@objc private func pressed() {
|
||||
self.action?()
|
||||
}
|
||||
|
||||
func update(component: AttachButtonIconComponent, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
self.action = component.action
|
||||
|
||||
if self.currentColors != component.backgroundColors {
|
||||
self.currentColors = component.backgroundColors
|
||||
self.backgroundView.layer.contents = generateBackgroundImage(colors: component.backgroundColors)?.cgImage
|
||||
|
||||
if let color = component.backgroundColors.last {
|
||||
self.glowView.tintColor = color
|
||||
self.selectionView.tintColor = color.withAlphaComponent(0.2)
|
||||
}
|
||||
}
|
||||
|
||||
if self.currentImageName != component.imageName {
|
||||
self.currentImageName = component.imageName
|
||||
if let imageName = component.imageName, let image = UIImage(bundleImageName: imageName) {
|
||||
self.iconView.image = image
|
||||
|
||||
let scale: CGFloat = 0.875
|
||||
let iconSize = CGSize(width: floorToScreenPixels(image.size.width * scale), height: floorToScreenPixels(image.size.height * scale))
|
||||
self.iconView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - iconSize.width) / 2.0), y: floorToScreenPixels((availableSize.height - iconSize.height) / 2.0)), size: iconSize)
|
||||
}
|
||||
}
|
||||
|
||||
if self.currentIsSelected != component.isSelected {
|
||||
self.currentIsSelected = component.isSelected
|
||||
|
||||
transition.setScale(view: self.selectionView, scale: component.isSelected ? 1.0 : 0.8)
|
||||
}
|
||||
|
||||
let contentFrame = CGRect(origin: CGPoint(), size: availableSize)
|
||||
self.containerView.frame = contentFrame
|
||||
self.backgroundView.frame = contentFrame
|
||||
self.highlightView.frame = contentFrame
|
||||
|
||||
self.glowView.bounds = CGRect(origin: CGPoint(), size: CGSize(width: contentFrame.width + 12.0, height: contentFrame.height + 12.0))
|
||||
self.glowView.center = CGPoint(x: contentFrame.midX, y: contentFrame.midY)
|
||||
|
||||
self.selectionView.bounds = CGRect(origin: CGPoint(), size: CGSize(width: contentFrame.width + 6.0, height: contentFrame.height + 6.0))
|
||||
self.selectionView.center = CGPoint(x: contentFrame.midX, y: contentFrame.midY)
|
||||
|
||||
return availableSize
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View()
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
||||
private let context: AccountContext
|
||||
private var presentationData: PresentationData
|
||||
|
||||
private var presentationInterfaceState: ChatPresentationInterfaceState
|
||||
private var interfaceInteraction: ChatPanelInterfaceInteraction?
|
||||
|
||||
private let containerNode: ASDisplayNode
|
||||
private var effectView: UIVisualEffectView?
|
||||
private let scrollNode: ASScrollNode
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let separatorNode: ASDisplayNode
|
||||
private var buttonViews: [Int: ComponentHostView<Empty>] = [:]
|
||||
|
||||
private var textInputPanelNode: AttachmentTextInputPanelNode?
|
||||
|
||||
private var buttons: [AttachmentButtonType] = []
|
||||
private var selectedIndex: Int = 1
|
||||
private(set) var isCollapsed: Bool = false
|
||||
private(set) var isSelecting: Bool = false
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
private var scrollLayout: (width: CGFloat, contentSize: CGSize)?
|
||||
|
||||
var selectionChanged: (AttachmentButtonType, Bool) -> Void = { _, _ in }
|
||||
var beganTextEditing: () -> Void = {}
|
||||
var textUpdated: (NSAttributedString) -> Void = { _ in }
|
||||
var sendMessagePressed: (AttachmentTextInputPanelSendMode) -> Void = { _ in }
|
||||
var requestLayout: () -> Void = {}
|
||||
var present: (ViewController) -> Void = { _ in }
|
||||
var presentInGlobalOverlay: (ViewController) -> Void = { _ in }
|
||||
|
||||
init(context: AccountContext) {
|
||||
self.context = context
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: .peer(PeerId(0)), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil)
|
||||
|
||||
self.containerNode = ASDisplayNode()
|
||||
self.containerNode.clipsToBounds = true
|
||||
|
||||
self.scrollNode = ASScrollNode()
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.backgroundColor = self.presentationData.theme.actionSheet.itemBackgroundColor
|
||||
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.backgroundColor = self.presentationData.theme.rootController.navigationBar.separatorColor
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.containerNode)
|
||||
self.containerNode.addSubnode(self.backgroundNode)
|
||||
self.containerNode.addSubnode(self.separatorNode)
|
||||
self.containerNode.addSubnode(self.scrollNode)
|
||||
|
||||
self.interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _, _ in
|
||||
}, setupEditMessage: { _, _ in
|
||||
}, beginMessageSelection: { _, _ in
|
||||
}, deleteSelectedMessages: {
|
||||
}, reportSelectedMessages: {
|
||||
}, reportMessages: { _, _ in
|
||||
}, blockMessageAuthor: { _, _ in
|
||||
}, deleteMessages: { _, _, f in
|
||||
f(.default)
|
||||
}, forwardSelectedMessages: {
|
||||
}, forwardCurrentForwardMessages: {
|
||||
}, forwardMessages: { _ in
|
||||
}, updateForwardOptionsState: { [weak self] value in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, { $0.updatedInterfaceState({ $0.withUpdatedForwardOptionsState($0.forwardOptionsState) }) })
|
||||
}
|
||||
}, presentForwardOptions: { _ in
|
||||
}, shareSelectedMessages: {
|
||||
}, updateTextInputStateAndMode: { [weak self] f in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, { state in
|
||||
let (updatedState, updatedMode) = f(state.interfaceState.effectiveInputState, state.inputMode)
|
||||
return state.updatedInterfaceState { interfaceState in
|
||||
return interfaceState.withUpdatedEffectiveInputState(updatedState)
|
||||
}.updatedInputMode({ _ in updatedMode })
|
||||
})
|
||||
}
|
||||
}, updateInputModeAndDismissedButtonKeyboardMessageId: { [weak self] f in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, {
|
||||
let (updatedInputMode, updatedClosedButtonKeyboardMessageId) = f($0)
|
||||
return $0.updatedInputMode({ _ in return updatedInputMode }).updatedInterfaceState({
|
||||
$0.withUpdatedMessageActionsState({ value in
|
||||
var value = value
|
||||
value.closedButtonKeyboardMessageId = updatedClosedButtonKeyboardMessageId
|
||||
return value
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}, openStickers: {
|
||||
}, editMessage: {
|
||||
}, beginMessageSearch: { _, _ in
|
||||
}, dismissMessageSearch: {
|
||||
}, updateMessageSearch: { _ in
|
||||
}, openSearchResults: {
|
||||
}, navigateMessageSearch: { _ in
|
||||
}, openCalendarSearch: {
|
||||
}, toggleMembersSearch: { _ in
|
||||
}, navigateToMessage: { _, _, _, _ in
|
||||
}, navigateToChat: { _ in
|
||||
}, navigateToProfile: { _ in
|
||||
}, openPeerInfo: {
|
||||
}, togglePeerNotifications: {
|
||||
}, sendContextResult: { _, _, _, _ in
|
||||
return false
|
||||
}, sendBotCommand: { _, _ in
|
||||
}, sendBotStart: { _ in
|
||||
}, botSwitchChatWithPayload: { _, _ in
|
||||
}, beginMediaRecording: { _ in
|
||||
}, finishMediaRecording: { _ in
|
||||
}, stopMediaRecording: {
|
||||
}, lockMediaRecording: {
|
||||
}, deleteRecordedMedia: {
|
||||
}, sendRecordedMedia: { _ in
|
||||
}, displayRestrictedInfo: { _, _ in
|
||||
}, displayVideoUnmuteTip: { _ in
|
||||
}, switchMediaRecordingMode: {
|
||||
}, setupMessageAutoremoveTimeout: {
|
||||
}, sendSticker: { _, _, _, _ in
|
||||
return false
|
||||
}, unblockPeer: {
|
||||
}, pinMessage: { _, _ in
|
||||
}, unpinMessage: { _, _, _ in
|
||||
}, unpinAllMessages: {
|
||||
}, openPinnedList: { _ in
|
||||
}, shareAccountContact: {
|
||||
}, reportPeer: {
|
||||
}, presentPeerContact: {
|
||||
}, dismissReportPeer: {
|
||||
}, deleteChat: {
|
||||
}, beginCall: { _ in
|
||||
}, toggleMessageStickerStarred: { _ in
|
||||
}, presentController: { _, _ in
|
||||
}, getNavigationController: {
|
||||
return nil
|
||||
}, presentGlobalOverlayController: { _, _ in
|
||||
}, navigateFeed: {
|
||||
}, openGrouping: {
|
||||
}, toggleSilentPost: {
|
||||
}, requestUnvoteInMessage: { _ in
|
||||
}, requestStopPollInMessage: { _ in
|
||||
}, updateInputLanguage: { _ in
|
||||
}, unarchiveChat: {
|
||||
}, openLinkEditing: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
var selectionRange: Range<Int>?
|
||||
var text: String?
|
||||
var inputMode: ChatInputMode?
|
||||
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, { state in
|
||||
selectionRange = state.interfaceState.effectiveInputState.selectionRange
|
||||
if let selectionRange = selectionRange {
|
||||
text = state.interfaceState.effectiveInputState.inputText.attributedSubstring(from: NSRange(location: selectionRange.startIndex, length: selectionRange.count)).string
|
||||
}
|
||||
inputMode = state.inputMode
|
||||
return state
|
||||
})
|
||||
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = chatTextLinkEditController(sharedContext: strongSelf.context.sharedContext, updatedPresentationData: (presentationData, .never()), account: strongSelf.context.account, text: text ?? "", link: nil, apply: { [weak self] link in
|
||||
if let strongSelf = self, let inputMode = inputMode, let selectionRange = selectionRange {
|
||||
if let link = link {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, { state in
|
||||
return state.updatedInterfaceState({
|
||||
$0.withUpdatedEffectiveInputState(chatTextInputAddLinkAttribute($0.effectiveInputState, selectionRange: selectionRange, url: link))
|
||||
})
|
||||
})
|
||||
}
|
||||
if let textInputPanelNode = strongSelf.textInputPanelNode {
|
||||
textInputPanelNode.ensureFocused()
|
||||
}
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, { state in
|
||||
return state.updatedInputMode({ _ in return inputMode }).updatedInterfaceState({
|
||||
$0.withUpdatedEffectiveInputState(ChatTextInputState(inputText: $0.effectiveInputState.inputText, selectionRange: selectionRange.endIndex ..< selectionRange.endIndex))
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
strongSelf.present(controller)
|
||||
}
|
||||
}, reportPeerIrrelevantGeoLocation: {
|
||||
}, displaySlowmodeTooltip: { _, _ in
|
||||
}, displaySendMessageOptions: { [weak self] node, gesture in
|
||||
guard let strongSelf = self, let textInputPanelNode = strongSelf.textInputPanelNode else {
|
||||
return
|
||||
}
|
||||
textInputPanelNode.loadTextInputNodeIfNeeded()
|
||||
guard let textInputNode = textInputPanelNode.textInputNode else {
|
||||
return
|
||||
}
|
||||
let controller = ChatSendMessageActionSheetController(context: strongSelf.context, interfaceState: strongSelf.presentationInterfaceState, gesture: gesture, sourceSendButton: node, textInputNode: textInputNode, completion: {
|
||||
}, sendMessage: { [weak textInputPanelNode] silently in
|
||||
textInputPanelNode?.sendMessage(silently ? .silent : .generic)
|
||||
}, schedule: { [weak textInputPanelNode] in
|
||||
textInputPanelNode?.sendMessage(.schedule)
|
||||
})
|
||||
strongSelf.presentInGlobalOverlay(controller)
|
||||
}, openScheduledMessages: {
|
||||
}, openPeersNearby: {
|
||||
}, displaySearchResultsTooltip: { _, _ in
|
||||
}, unarchivePeer: {
|
||||
}, scrollToTop: {
|
||||
}, viewReplies: { _, _ in
|
||||
}, activatePinnedListPreview: { _, _ in
|
||||
}, joinGroupCall: { _ in
|
||||
}, presentInviteMembers: {
|
||||
}, presentGigagroupHelp: {
|
||||
}, editMessageMedia: { _, _ in
|
||||
}, updateShowCommands: { _ in
|
||||
}, updateShowSendAsPeers: { _ in
|
||||
}, openInviteRequests: {
|
||||
}, openSendAsPeer: { _, _ in
|
||||
}, presentChatRequestAdminInfo: {
|
||||
}, displayCopyProtectionTip: { _, _ in
|
||||
}, statuses: nil)
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
if #available(iOS 13.0, *) {
|
||||
self.containerNode.layer.cornerCurve = .continuous
|
||||
}
|
||||
|
||||
self.scrollNode.view.delegate = self
|
||||
self.scrollNode.view.showsHorizontalScrollIndicator = false
|
||||
self.scrollNode.view.showsVerticalScrollIndicator = false
|
||||
|
||||
let effect: UIVisualEffect
|
||||
switch self.presentationData.theme.actionSheet.backgroundType {
|
||||
case .light:
|
||||
effect = UIBlurEffect(style: .light)
|
||||
case .dark:
|
||||
effect = UIBlurEffect(style: .dark)
|
||||
}
|
||||
let effectView = UIVisualEffectView(effect: effect)
|
||||
self.effectView = effectView
|
||||
self.containerNode.view.insertSubview(effectView, at: 0)
|
||||
}
|
||||
|
||||
func updateCaption(_ caption: NSAttributedString) {
|
||||
if !caption.string.isEmpty {
|
||||
self.loadTextNodeIfNeeded()
|
||||
}
|
||||
self.updateChatPresentationInterfaceState(animated: false, { $0.updatedInterfaceState { $0.withUpdatedComposeInputState(ChatTextInputState(inputText: caption))} })
|
||||
}
|
||||
|
||||
private func updateChatPresentationInterfaceState(animated: Bool = true, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState, completion: @escaping (ContainedViewLayoutTransition) -> Void = { _ in }) {
|
||||
self.updateChatPresentationInterfaceState(transition: animated ? .animated(duration: 0.4, curve: .spring) : .immediate, f, completion: completion)
|
||||
}
|
||||
|
||||
private func updateChatPresentationInterfaceState(transition: ContainedViewLayoutTransition, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState, completion externalCompletion: @escaping (ContainedViewLayoutTransition) -> Void = { _ in }) {
|
||||
let presentationInterfaceState = f(self.presentationInterfaceState)
|
||||
let updateInputTextState = self.presentationInterfaceState.interfaceState.effectiveInputState != presentationInterfaceState.interfaceState.effectiveInputState
|
||||
|
||||
self.presentationInterfaceState = presentationInterfaceState
|
||||
|
||||
if let textInputPanelNode = self.textInputPanelNode, updateInputTextState {
|
||||
textInputPanelNode.updateInputTextState(presentationInterfaceState.interfaceState.effectiveInputState, animated: transition.isAnimated)
|
||||
|
||||
self.textUpdated(presentationInterfaceState.interfaceState.effectiveInputState.inputText)
|
||||
}
|
||||
}
|
||||
|
||||
func updateViews(transition: Transition) {
|
||||
guard let layout = self.validLayout else {
|
||||
return
|
||||
}
|
||||
|
||||
let visibleRect = self.scrollNode.bounds.insetBy(dx: -180.0, dy: 0.0)
|
||||
let actualVisibleRect = self.scrollNode.bounds
|
||||
var validButtons = Set<Int>()
|
||||
|
||||
let buttonSize = self.isCollapsed ? smallPanelButtonSize : panelButtonSize
|
||||
var sideInset = self.isCollapsed ? smallSideInset : normalSideInset
|
||||
|
||||
let buttonsWidth = sideInset * 2.0 + buttonSize.width * CGFloat(self.buttons.count)
|
||||
if buttonsWidth < layout.size.width {
|
||||
sideInset = floorToScreenPixels((layout.size.width - buttonsWidth) / 2.0)
|
||||
}
|
||||
|
||||
for i in 0 ..< self.buttons.count {
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: sideInset + buttonSize.width * CGFloat(i), y: 0.0), size: buttonSize)
|
||||
if !visibleRect.intersects(buttonFrame) {
|
||||
continue
|
||||
}
|
||||
validButtons.insert(i)
|
||||
|
||||
let edge = buttonSize.width * 0.75
|
||||
let leftEdge = max(-edge, min(0.0, buttonFrame.minX - actualVisibleRect.minX)) / -edge
|
||||
let rightEdge = min(edge, max(0.0, buttonFrame.maxX - actualVisibleRect.maxX)) / edge
|
||||
|
||||
let transitionFraction: CGFloat
|
||||
if leftEdge > rightEdge {
|
||||
transitionFraction = leftEdge
|
||||
} else {
|
||||
transitionFraction = -rightEdge
|
||||
}
|
||||
|
||||
var buttonTransition = transition
|
||||
let buttonView: ComponentHostView<Empty>
|
||||
if let current = self.buttonViews[i] {
|
||||
buttonView = current
|
||||
} else {
|
||||
buttonTransition = .immediate
|
||||
buttonView = ComponentHostView<Empty>()
|
||||
self.buttonViews[i] = buttonView
|
||||
self.scrollNode.view.addSubview(buttonView)
|
||||
}
|
||||
|
||||
let type = self.buttons[i]
|
||||
let _ = buttonView.update(
|
||||
transition: buttonTransition,
|
||||
component: AnyComponent(AttachButtonComponent(
|
||||
context: self.context,
|
||||
type: type,
|
||||
isSelected: i == self.selectedIndex,
|
||||
isCollapsed: self.isCollapsed,
|
||||
transitionFraction: transitionFraction,
|
||||
strings: self.presentationData.strings,
|
||||
theme: self.presentationData.theme,
|
||||
action: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
let ascending = i > strongSelf.selectedIndex
|
||||
strongSelf.selectedIndex = i
|
||||
strongSelf.selectionChanged(type, ascending)
|
||||
strongSelf.updateViews(transition: .init(animation: .curve(duration: 0.2, curve: .spring)))
|
||||
}
|
||||
})
|
||||
),
|
||||
environment: {},
|
||||
containerSize: buttonSize
|
||||
)
|
||||
buttonTransition.setFrame(view: buttonView, frame: buttonFrame)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateScrollLayoutIfNeeded(force: Bool, transition: ContainedViewLayoutTransition) -> Bool {
|
||||
guard let layout = self.validLayout else {
|
||||
return false
|
||||
}
|
||||
if self.scrollLayout?.width == layout.size.width && !force {
|
||||
return false
|
||||
}
|
||||
|
||||
let buttonSize = self.isCollapsed ? smallPanelButtonSize : panelButtonSize
|
||||
let contentSize = CGSize(width: (self.isCollapsed ? smallSideInset : normalSideInset) * 2.0 + CGFloat(self.buttons.count) * buttonSize.width, height: buttonSize.height)
|
||||
self.scrollLayout = (layout.size.width, contentSize)
|
||||
|
||||
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(x: 0.0, y: self.isSelecting ? -panelButtonSize.height : 0.0), size: CGSize(width: layout.size.width, height: panelButtonSize.height)))
|
||||
self.scrollNode.view.contentSize = contentSize
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private func loadTextNodeIfNeeded() {
|
||||
if let _ = self.textInputPanelNode {
|
||||
} else {
|
||||
let textInputPanelNode = AttachmentTextInputPanelNode(context: self.context, presentationInterfaceState: self.presentationInterfaceState, isAttachment: true, presentController: { [weak self] c in
|
||||
if let strongSelf = self {
|
||||
strongSelf.present(c)
|
||||
}
|
||||
})
|
||||
textInputPanelNode.interfaceInteraction = self.interfaceInteraction
|
||||
textInputPanelNode.sendMessage = { [weak self] mode in
|
||||
if let strongSelf = self {
|
||||
strongSelf.sendMessagePressed(mode)
|
||||
}
|
||||
}
|
||||
textInputPanelNode.focusUpdated = { [weak self] focus in
|
||||
if let strongSelf = self, focus {
|
||||
strongSelf.beganTextEditing()
|
||||
}
|
||||
}
|
||||
textInputPanelNode.updateHeight = { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.requestLayout()
|
||||
}
|
||||
}
|
||||
self.addSubnode(textInputPanelNode)
|
||||
self.textInputPanelNode = textInputPanelNode
|
||||
|
||||
textInputPanelNode.alpha = self.isSelecting ? 1.0 : 0.0
|
||||
textInputPanelNode.isUserInteractionEnabled = self.isSelecting
|
||||
}
|
||||
}
|
||||
|
||||
func update(layout: ContainerViewLayout, buttons: [AttachmentButtonType], isCollapsed: Bool, isSelecting: Bool, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
self.validLayout = layout
|
||||
self.buttons = buttons
|
||||
|
||||
let isCollapsedUpdated = self.isCollapsed != isCollapsed
|
||||
self.isCollapsed = isCollapsed
|
||||
|
||||
let isSelectingUpdated = self.isSelecting != isSelecting
|
||||
self.isSelecting = isSelecting
|
||||
|
||||
self.scrollNode.isUserInteractionEnabled = !isSelecting
|
||||
|
||||
var insets = layout.insets(options: [])
|
||||
if let inputHeight = layout.inputHeight, inputHeight > 0.0 && isSelecting {
|
||||
insets.bottom = inputHeight
|
||||
} else if layout.intrinsicInsets.bottom > 0.0 {
|
||||
insets.bottom = layout.intrinsicInsets.bottom
|
||||
}
|
||||
|
||||
if isSelecting {
|
||||
self.loadTextNodeIfNeeded()
|
||||
} else {
|
||||
self.textInputPanelNode?.ensureUnfocused()
|
||||
}
|
||||
var textPanelHeight: CGFloat = 0.0
|
||||
if let textInputPanelNode = self.textInputPanelNode {
|
||||
textInputPanelNode.isUserInteractionEnabled = isSelecting
|
||||
|
||||
var panelTransition = transition
|
||||
if textInputPanelNode.frame.width.isZero {
|
||||
panelTransition = .immediate
|
||||
}
|
||||
let panelHeight = textInputPanelNode.updateLayout(width: layout.size.width, leftInset: insets.left, rightInset: insets.right, additionalSideInsets: UIEdgeInsets(), maxHeight: layout.size.height / 2.0, isSecondary: false, transition: panelTransition, interfaceState: self.presentationInterfaceState, metrics: layout.metrics)
|
||||
let panelFrame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: panelHeight)
|
||||
if textInputPanelNode.frame.width.isZero {
|
||||
textInputPanelNode.frame = panelFrame
|
||||
}
|
||||
transition.updateFrame(node: textInputPanelNode, frame: panelFrame)
|
||||
if panelFrame.height > 0.0 {
|
||||
textPanelHeight = panelFrame.height
|
||||
} else {
|
||||
textPanelHeight = 45.0
|
||||
}
|
||||
}
|
||||
|
||||
let bounds = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: panelButtonSize.height + insets.bottom))
|
||||
let containerTransition: ContainedViewLayoutTransition
|
||||
let containerFrame: CGRect
|
||||
if isSelecting {
|
||||
containerFrame = CGRect(origin: CGPoint(), size: CGSize(width: bounds.width, height: textPanelHeight + insets.bottom))
|
||||
} else if isCollapsed {
|
||||
containerFrame = CGRect(origin: CGPoint(), size: CGSize(width: bounds.width, height: smallPanelButtonSize.height + insets.bottom))
|
||||
} else {
|
||||
containerFrame = bounds
|
||||
}
|
||||
let containerBounds = CGRect(origin: CGPoint(), size: containerFrame.size)
|
||||
if isCollapsedUpdated || isSelectingUpdated {
|
||||
containerTransition = .animated(duration: 0.25, curve: .easeInOut)
|
||||
} else {
|
||||
containerTransition = transition
|
||||
}
|
||||
containerTransition.updateAlpha(node: self.scrollNode, alpha: isSelecting ? 0.0 : 1.0)
|
||||
|
||||
if isSelectingUpdated {
|
||||
if isSelecting {
|
||||
self.loadTextNodeIfNeeded()
|
||||
if let textInputPanelNode = self.textInputPanelNode {
|
||||
textInputPanelNode.alpha = 1.0
|
||||
textInputPanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||
textInputPanelNode.layer.animatePosition(from: CGPoint(x: 0.0, y: 44.0), to: CGPoint(), duration: 0.25, additive: true)
|
||||
}
|
||||
} else {
|
||||
if let textInputPanelNode = self.textInputPanelNode {
|
||||
textInputPanelNode.alpha = 0.0
|
||||
textInputPanelNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25)
|
||||
textInputPanelNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: 44.0), duration: 0.25, additive: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
containerTransition.updateFrame(node: self.containerNode, frame: containerFrame)
|
||||
containerTransition.updateFrame(node: self.backgroundNode, frame: containerBounds)
|
||||
containerTransition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: bounds.width, height: UIScreenPixel)))
|
||||
if let effectView = self.effectView {
|
||||
containerTransition.updateFrame(view: effectView, frame: bounds)
|
||||
}
|
||||
|
||||
let _ = self.updateScrollLayoutIfNeeded(force: isCollapsedUpdated || isSelectingUpdated, transition: containerTransition)
|
||||
|
||||
var buttonTransition: Transition = .immediate
|
||||
if isCollapsedUpdated {
|
||||
buttonTransition = .easeInOut(duration: 0.25)
|
||||
}
|
||||
self.updateViews(transition: buttonTransition)
|
||||
|
||||
return containerFrame.height
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
self.updateViews(transition: .immediate)
|
||||
}
|
||||
}
|
||||
26
submodules/ChatPresentationInterfaceState/BUILD
Normal file
26
submodules/ChatPresentationInterfaceState/BUILD
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "ChatPresentationInterfaceState",
|
||||
module_name = "ChatPresentationInterfaceState",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/ContextUI:ContextUI",
|
||||
"//submodules/ChatInterfaceState:ChatInterfaceState",
|
||||
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
|
|
@ -25,16 +25,16 @@ private func setBits(data: UnsafeMutableRawPointer, bitOffset: Int, numBits: Int
|
|||
normalizedData.assumingMemoryBound(to: Int32.self).pointee |= value << Int32(normalizedBitOffset)
|
||||
}
|
||||
|
||||
final class AudioWaveform: Equatable {
|
||||
let samples: Data
|
||||
let peak: Int32
|
||||
public final class AudioWaveform: Equatable {
|
||||
public let samples: Data
|
||||
public let peak: Int32
|
||||
|
||||
init(samples: Data, peak: Int32) {
|
||||
public init(samples: Data, peak: Int32) {
|
||||
self.samples = samples
|
||||
self.peak = peak
|
||||
}
|
||||
|
||||
convenience init(bitstream: Data, bitsPerSample: Int) {
|
||||
public convenience init(bitstream: Data, bitsPerSample: Int) {
|
||||
let numSamples = Int(Float(bitstream.count * 8) / Float(bitsPerSample))
|
||||
var result = Data()
|
||||
result.count = numSamples * 2
|
||||
|
|
@ -51,7 +51,7 @@ final class AudioWaveform: Equatable {
|
|||
self.init(samples: result, peak: 31)
|
||||
}
|
||||
|
||||
func makeBitstream() -> Data {
|
||||
public func makeBitstream() -> Data {
|
||||
let numSamples = self.samples.count / 2
|
||||
let bitstreamLength = (numSamples * 5) / 8 + (((numSamples * 5) % 8) == 0 ? 0 : 1)
|
||||
var result = Data()
|
||||
|
|
@ -80,7 +80,7 @@ final class AudioWaveform: Equatable {
|
|||
return result
|
||||
}
|
||||
|
||||
static func ==(lhs: AudioWaveform, rhs: AudioWaveform) -> Bool {
|
||||
public static func ==(lhs: AudioWaveform, rhs: AudioWaveform) -> Bool {
|
||||
return lhs.peak == rhs.peak && lhs.samples == rhs.samples
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
|
||||
public struct MessageMediaEditingOptions: OptionSet {
|
||||
public var rawValue: Int32
|
||||
|
||||
public init(rawValue: Int32) {
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
|
||||
public static let imageOrVideo = MessageMediaEditingOptions(rawValue: 1 << 0)
|
||||
public static let file = MessageMediaEditingOptions(rawValue: 1 << 1)
|
||||
}
|
||||
|
||||
public enum ChatEditInterfaceMessageStateContent: Equatable {
|
||||
case plaintext
|
||||
case media(mediaOptions: MessageMediaEditingOptions)
|
||||
}
|
||||
|
||||
public final class ChatEditInterfaceMessageState: Equatable {
|
||||
public let content: ChatEditInterfaceMessageStateContent
|
||||
public let mediaReference: AnyMediaReference?
|
||||
|
||||
public init(content: ChatEditInterfaceMessageStateContent, mediaReference: AnyMediaReference?) {
|
||||
self.content = content
|
||||
self.mediaReference = mediaReference
|
||||
}
|
||||
|
||||
public static func ==(lhs: ChatEditInterfaceMessageState, rhs: ChatEditInterfaceMessageState) -> Bool {
|
||||
if lhs.content != rhs.content {
|
||||
return false
|
||||
}
|
||||
if let lhsMedia = lhs.mediaReference, let rhsMedia = rhs.mediaReference {
|
||||
if !lhsMedia.media.isEqual(to: rhsMedia.media) {
|
||||
return false
|
||||
}
|
||||
} else if (lhs.mediaReference != nil) != (rhs.mediaReference != nil) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -8,21 +8,26 @@ import Display
|
|||
import AccountContext
|
||||
import ContextUI
|
||||
|
||||
public enum ChatLoadingMessageSubject {
|
||||
case generic
|
||||
case pinnedMessage
|
||||
}
|
||||
|
||||
public enum ChatFinishMediaRecordingAction {
|
||||
case dismiss
|
||||
case preview
|
||||
case send
|
||||
}
|
||||
|
||||
final class ChatPanelInterfaceInteractionStatuses {
|
||||
let editingMessage: Signal<Float?, NoError>
|
||||
let startingBot: Signal<Bool, NoError>
|
||||
let unblockingPeer: Signal<Bool, NoError>
|
||||
let searching: Signal<Bool, NoError>
|
||||
let loadingMessage: Signal<ChatLoadingMessageSubject?, NoError>
|
||||
let inlineSearch: Signal<Bool, NoError>
|
||||
public final class ChatPanelInterfaceInteractionStatuses {
|
||||
public let editingMessage: Signal<Float?, NoError>
|
||||
public let startingBot: Signal<Bool, NoError>
|
||||
public let unblockingPeer: Signal<Bool, NoError>
|
||||
public let searching: Signal<Bool, NoError>
|
||||
public let loadingMessage: Signal<ChatLoadingMessageSubject?, NoError>
|
||||
public let inlineSearch: Signal<Bool, NoError>
|
||||
|
||||
init(editingMessage: Signal<Float?, NoError>, startingBot: Signal<Bool, NoError>, unblockingPeer: Signal<Bool, NoError>, searching: Signal<Bool, NoError>, loadingMessage: Signal<ChatLoadingMessageSubject?, NoError>, inlineSearch: Signal<Bool, NoError>) {
|
||||
public init(editingMessage: Signal<Float?, NoError>, startingBot: Signal<Bool, NoError>, unblockingPeer: Signal<Bool, NoError>, searching: Signal<Bool, NoError>, loadingMessage: Signal<ChatLoadingMessageSubject?, NoError>, inlineSearch: Signal<Bool, NoError>) {
|
||||
self.editingMessage = editingMessage
|
||||
self.startingBot = startingBot
|
||||
self.unblockingPeer = unblockingPeer
|
||||
|
|
@ -32,114 +37,114 @@ final class ChatPanelInterfaceInteractionStatuses {
|
|||
}
|
||||
}
|
||||
|
||||
enum ChatPanelSearchNavigationAction {
|
||||
public enum ChatPanelSearchNavigationAction {
|
||||
case earlier
|
||||
case later
|
||||
case index(Int)
|
||||
}
|
||||
|
||||
enum ChatPanelRestrictionInfoSubject {
|
||||
public enum ChatPanelRestrictionInfoSubject {
|
||||
case mediaRecording
|
||||
case stickers
|
||||
}
|
||||
|
||||
enum ChatPanelRestrictionInfoDisplayType {
|
||||
public enum ChatPanelRestrictionInfoDisplayType {
|
||||
case tooltip
|
||||
case alert
|
||||
}
|
||||
|
||||
final class ChatPanelInterfaceInteraction {
|
||||
let setupReplyMessage: (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void
|
||||
let setupEditMessage: (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void
|
||||
let beginMessageSelection: ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void
|
||||
let deleteSelectedMessages: () -> Void
|
||||
let reportSelectedMessages: () -> Void
|
||||
let reportMessages: ([Message], ContextControllerProtocol?) -> Void
|
||||
let blockMessageAuthor: (Message, ContextControllerProtocol?) -> Void
|
||||
let deleteMessages: ([Message], ContextControllerProtocol?, @escaping (ContextMenuActionResult) -> Void) -> Void
|
||||
let forwardSelectedMessages: () -> Void
|
||||
let forwardCurrentForwardMessages: () -> Void
|
||||
let forwardMessages: ([Message]) -> Void
|
||||
let updateForwardOptionsState: ((ChatInterfaceForwardOptionsState) -> ChatInterfaceForwardOptionsState) -> Void
|
||||
let presentForwardOptions: (ASDisplayNode) -> Void
|
||||
let shareSelectedMessages: () -> Void
|
||||
let updateTextInputStateAndMode: (@escaping (ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void
|
||||
let updateInputModeAndDismissedButtonKeyboardMessageId: ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void
|
||||
let openStickers: () -> Void
|
||||
let editMessage: () -> Void
|
||||
let beginMessageSearch: (ChatSearchDomain, String) -> Void
|
||||
let dismissMessageSearch: () -> Void
|
||||
let updateMessageSearch: (String) -> Void
|
||||
let navigateMessageSearch: (ChatPanelSearchNavigationAction) -> Void
|
||||
let openSearchResults: () -> Void
|
||||
let openCalendarSearch: () -> Void
|
||||
let toggleMembersSearch: (Bool) -> Void
|
||||
let navigateToMessage: (MessageId, Bool, Bool, ChatLoadingMessageSubject) -> Void
|
||||
let navigateToChat: (PeerId) -> Void
|
||||
let navigateToProfile: (PeerId) -> Void
|
||||
let openPeerInfo: () -> Void
|
||||
let togglePeerNotifications: () -> Void
|
||||
let sendContextResult: (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool
|
||||
let sendBotCommand: (Peer, String) -> Void
|
||||
let sendBotStart: (String?) -> Void
|
||||
let botSwitchChatWithPayload: (PeerId, String) -> Void
|
||||
let beginMediaRecording: (Bool) -> Void
|
||||
let finishMediaRecording: (ChatFinishMediaRecordingAction) -> Void
|
||||
let stopMediaRecording: () -> Void
|
||||
let lockMediaRecording: () -> Void
|
||||
let deleteRecordedMedia: () -> Void
|
||||
let sendRecordedMedia: (Bool) -> Void
|
||||
let displayRestrictedInfo: (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void
|
||||
let displayVideoUnmuteTip: (CGPoint?) -> Void
|
||||
let switchMediaRecordingMode: () -> Void
|
||||
let setupMessageAutoremoveTimeout: () -> Void
|
||||
let sendSticker: (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool
|
||||
let unblockPeer: () -> Void
|
||||
let pinMessage: (MessageId, ContextControllerProtocol?) -> Void
|
||||
let unpinMessage: (MessageId, Bool, ContextControllerProtocol?) -> Void
|
||||
let unpinAllMessages: () -> Void
|
||||
let openPinnedList: (MessageId) -> Void
|
||||
let shareAccountContact: () -> Void
|
||||
let reportPeer: () -> Void
|
||||
let presentPeerContact: () -> Void
|
||||
let dismissReportPeer: () -> Void
|
||||
let deleteChat: () -> Void
|
||||
let beginCall: (Bool) -> Void
|
||||
let toggleMessageStickerStarred: (MessageId) -> Void
|
||||
let presentController: (ViewController, Any?) -> Void
|
||||
let getNavigationController: () -> NavigationController?
|
||||
let presentGlobalOverlayController: (ViewController, Any?) -> Void
|
||||
let navigateFeed: () -> Void
|
||||
let openGrouping: () -> Void
|
||||
let toggleSilentPost: () -> Void
|
||||
let requestUnvoteInMessage: (MessageId) -> Void
|
||||
let requestStopPollInMessage: (MessageId) -> Void
|
||||
let updateInputLanguage: (@escaping (String?) -> String?) -> Void
|
||||
let unarchiveChat: () -> Void
|
||||
let openLinkEditing: () -> Void
|
||||
let reportPeerIrrelevantGeoLocation: () -> Void
|
||||
let displaySlowmodeTooltip: (ASDisplayNode, CGRect) -> Void
|
||||
let displaySendMessageOptions: (ASDisplayNode, ContextGesture) -> Void
|
||||
let openScheduledMessages: () -> Void
|
||||
let displaySearchResultsTooltip: (ASDisplayNode, CGRect) -> Void
|
||||
let openPeersNearby: () -> Void
|
||||
let unarchivePeer: () -> Void
|
||||
let scrollToTop: () -> Void
|
||||
let viewReplies: (MessageId?, ChatReplyThreadMessage) -> Void
|
||||
let activatePinnedListPreview: (ASDisplayNode, ContextGesture) -> Void
|
||||
let editMessageMedia: (MessageId, Bool) -> Void
|
||||
let joinGroupCall: (CachedChannelData.ActiveCall) -> Void
|
||||
let presentInviteMembers: () -> Void
|
||||
let presentGigagroupHelp: () -> Void
|
||||
let updateShowCommands: ((Bool) -> Bool) -> Void
|
||||
let updateShowSendAsPeers: ((Bool) -> Bool) -> Void
|
||||
let openInviteRequests: () -> Void
|
||||
let openSendAsPeer: (ASDisplayNode, ContextGesture?) -> Void
|
||||
let presentChatRequestAdminInfo: () -> Void
|
||||
let displayCopyProtectionTip: (ASDisplayNode, Bool) -> Void
|
||||
let statuses: ChatPanelInterfaceInteractionStatuses?
|
||||
public final class ChatPanelInterfaceInteraction {
|
||||
public let setupReplyMessage: (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void
|
||||
public let setupEditMessage: (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void
|
||||
public let beginMessageSelection: ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void
|
||||
public let deleteSelectedMessages: () -> Void
|
||||
public let reportSelectedMessages: () -> Void
|
||||
public let reportMessages: ([Message], ContextControllerProtocol?) -> Void
|
||||
public let blockMessageAuthor: (Message, ContextControllerProtocol?) -> Void
|
||||
public let deleteMessages: ([Message], ContextControllerProtocol?, @escaping (ContextMenuActionResult) -> Void) -> Void
|
||||
public let forwardSelectedMessages: () -> Void
|
||||
public let forwardCurrentForwardMessages: () -> Void
|
||||
public let forwardMessages: ([Message]) -> Void
|
||||
public let updateForwardOptionsState: ((ChatInterfaceForwardOptionsState) -> ChatInterfaceForwardOptionsState) -> Void
|
||||
public let presentForwardOptions: (ASDisplayNode) -> Void
|
||||
public let shareSelectedMessages: () -> Void
|
||||
public let updateTextInputStateAndMode: (@escaping (ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void
|
||||
public let updateInputModeAndDismissedButtonKeyboardMessageId: ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void
|
||||
public let openStickers: () -> Void
|
||||
public let editMessage: () -> Void
|
||||
public let beginMessageSearch: (ChatSearchDomain, String) -> Void
|
||||
public let dismissMessageSearch: () -> Void
|
||||
public let updateMessageSearch: (String) -> Void
|
||||
public let navigateMessageSearch: (ChatPanelSearchNavigationAction) -> Void
|
||||
public let openSearchResults: () -> Void
|
||||
public let openCalendarSearch: () -> Void
|
||||
public let toggleMembersSearch: (Bool) -> Void
|
||||
public let navigateToMessage: (MessageId, Bool, Bool, ChatLoadingMessageSubject) -> Void
|
||||
public let navigateToChat: (PeerId) -> Void
|
||||
public let navigateToProfile: (PeerId) -> Void
|
||||
public let openPeerInfo: () -> Void
|
||||
public let togglePeerNotifications: () -> Void
|
||||
public let sendContextResult: (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool
|
||||
public let sendBotCommand: (Peer, String) -> Void
|
||||
public let sendBotStart: (String?) -> Void
|
||||
public let botSwitchChatWithPayload: (PeerId, String) -> Void
|
||||
public let beginMediaRecording: (Bool) -> Void
|
||||
public let finishMediaRecording: (ChatFinishMediaRecordingAction) -> Void
|
||||
public let stopMediaRecording: () -> Void
|
||||
public let lockMediaRecording: () -> Void
|
||||
public let deleteRecordedMedia: () -> Void
|
||||
public let sendRecordedMedia: (Bool) -> Void
|
||||
public let displayRestrictedInfo: (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void
|
||||
public let displayVideoUnmuteTip: (CGPoint?) -> Void
|
||||
public let switchMediaRecordingMode: () -> Void
|
||||
public let setupMessageAutoremoveTimeout: () -> Void
|
||||
public let sendSticker: (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool
|
||||
public let unblockPeer: () -> Void
|
||||
public let pinMessage: (MessageId, ContextControllerProtocol?) -> Void
|
||||
public let unpinMessage: (MessageId, Bool, ContextControllerProtocol?) -> Void
|
||||
public let unpinAllMessages: () -> Void
|
||||
public let openPinnedList: (MessageId) -> Void
|
||||
public let shareAccountContact: () -> Void
|
||||
public let reportPeer: () -> Void
|
||||
public let presentPeerContact: () -> Void
|
||||
public let dismissReportPeer: () -> Void
|
||||
public let deleteChat: () -> Void
|
||||
public let beginCall: (Bool) -> Void
|
||||
public let toggleMessageStickerStarred: (MessageId) -> Void
|
||||
public let presentController: (ViewController, Any?) -> Void
|
||||
public let getNavigationController: () -> NavigationController?
|
||||
public let presentGlobalOverlayController: (ViewController, Any?) -> Void
|
||||
public let navigateFeed: () -> Void
|
||||
public let openGrouping: () -> Void
|
||||
public let toggleSilentPost: () -> Void
|
||||
public let requestUnvoteInMessage: (MessageId) -> Void
|
||||
public let requestStopPollInMessage: (MessageId) -> Void
|
||||
public let updateInputLanguage: (@escaping (String?) -> String?) -> Void
|
||||
public let unarchiveChat: () -> Void
|
||||
public let openLinkEditing: () -> Void
|
||||
public let reportPeerIrrelevantGeoLocation: () -> Void
|
||||
public let displaySlowmodeTooltip: (ASDisplayNode, CGRect) -> Void
|
||||
public let displaySendMessageOptions: (ASDisplayNode, ContextGesture) -> Void
|
||||
public let openScheduledMessages: () -> Void
|
||||
public let displaySearchResultsTooltip: (ASDisplayNode, CGRect) -> Void
|
||||
public let openPeersNearby: () -> Void
|
||||
public let unarchivePeer: () -> Void
|
||||
public let scrollToTop: () -> Void
|
||||
public let viewReplies: (MessageId?, ChatReplyThreadMessage) -> Void
|
||||
public let activatePinnedListPreview: (ASDisplayNode, ContextGesture) -> Void
|
||||
public let editMessageMedia: (MessageId, Bool) -> Void
|
||||
public let joinGroupCall: (CachedChannelData.ActiveCall) -> Void
|
||||
public let presentInviteMembers: () -> Void
|
||||
public let presentGigagroupHelp: () -> Void
|
||||
public let updateShowCommands: ((Bool) -> Bool) -> Void
|
||||
public let updateShowSendAsPeers: ((Bool) -> Bool) -> Void
|
||||
public let openInviteRequests: () -> Void
|
||||
public let openSendAsPeer: (ASDisplayNode, ContextGesture?) -> Void
|
||||
public let presentChatRequestAdminInfo: () -> Void
|
||||
public let displayCopyProtectionTip: (ASDisplayNode, Bool) -> Void
|
||||
public let statuses: ChatPanelInterfaceInteractionStatuses?
|
||||
|
||||
init(
|
||||
public init(
|
||||
setupReplyMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void,
|
||||
setupEditMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void,
|
||||
beginMessageSelection: @escaping ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void,
|
||||
|
|
@ -321,7 +326,7 @@ final class ChatPanelInterfaceInteraction {
|
|||
self.statuses = statuses
|
||||
}
|
||||
|
||||
convenience init(
|
||||
public convenience init(
|
||||
updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void,
|
||||
updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void,
|
||||
openLinkEditing: @escaping () -> Void
|
||||
|
|
@ -7,7 +7,18 @@ import TelegramUIPreferences
|
|||
import AccountContext
|
||||
import ChatInterfaceState
|
||||
|
||||
enum ChatPresentationInputQueryKind: Int32 {
|
||||
public extension ChatLocation {
|
||||
var peerId: PeerId {
|
||||
switch self {
|
||||
case let .peer(peerId):
|
||||
return peerId
|
||||
case let .replyThread(replyThreadMessage):
|
||||
return replyThreadMessage.messageId.peerId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum ChatPresentationInputQueryKind: Int32 {
|
||||
case emoji
|
||||
case hashtag
|
||||
case mention
|
||||
|
|
@ -16,19 +27,19 @@ enum ChatPresentationInputQueryKind: Int32 {
|
|||
case emojiSearch
|
||||
}
|
||||
|
||||
struct ChatInputQueryMentionTypes: OptionSet, Hashable {
|
||||
var rawValue: Int32
|
||||
public struct ChatInputQueryMentionTypes: OptionSet, Hashable {
|
||||
public var rawValue: Int32
|
||||
|
||||
init(rawValue: Int32) {
|
||||
public init(rawValue: Int32) {
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
|
||||
static let contextBots = ChatInputQueryMentionTypes(rawValue: 1 << 0)
|
||||
static let members = ChatInputQueryMentionTypes(rawValue: 1 << 1)
|
||||
static let accountPeer = ChatInputQueryMentionTypes(rawValue: 1 << 2)
|
||||
public static let contextBots = ChatInputQueryMentionTypes(rawValue: 1 << 0)
|
||||
public static let members = ChatInputQueryMentionTypes(rawValue: 1 << 1)
|
||||
public static let accountPeer = ChatInputQueryMentionTypes(rawValue: 1 << 2)
|
||||
}
|
||||
|
||||
enum ChatPresentationInputQuery: Hashable, Equatable {
|
||||
public enum ChatPresentationInputQuery: Hashable, Equatable {
|
||||
case emoji(String)
|
||||
case hashtag(String)
|
||||
case mention(query: String, types: ChatInputQueryMentionTypes)
|
||||
|
|
@ -36,7 +47,7 @@ enum ChatPresentationInputQuery: Hashable, Equatable {
|
|||
case emojiSearch(query: String, languageCode: String, range: NSRange)
|
||||
case contextRequest(addressName: String, query: String)
|
||||
|
||||
var kind: ChatPresentationInputQueryKind {
|
||||
public var kind: ChatPresentationInputQueryKind {
|
||||
switch self {
|
||||
case .emoji:
|
||||
return .emoji
|
||||
|
|
@ -54,30 +65,30 @@ enum ChatPresentationInputQuery: Hashable, Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
enum ChatMediaInputMode {
|
||||
public enum ChatMediaInputMode {
|
||||
case gif
|
||||
case other
|
||||
}
|
||||
|
||||
enum ChatMediaInputSearchMode {
|
||||
public enum ChatMediaInputSearchMode {
|
||||
case gif
|
||||
case sticker
|
||||
case trending
|
||||
}
|
||||
|
||||
enum ChatMediaInputExpanded: Equatable {
|
||||
public enum ChatMediaInputExpanded: Equatable {
|
||||
case content
|
||||
case search(ChatMediaInputSearchMode)
|
||||
}
|
||||
|
||||
enum ChatInputMode: Equatable {
|
||||
public enum ChatInputMode: Equatable {
|
||||
case none
|
||||
case text
|
||||
case media(mode: ChatMediaInputMode, expanded: ChatMediaInputExpanded?, focused: Bool)
|
||||
case inputButtons
|
||||
}
|
||||
|
||||
enum ChatTitlePanelContext: Equatable, Comparable {
|
||||
public enum ChatTitlePanelContext: Equatable, Comparable {
|
||||
case pinnedMessage
|
||||
case chatInfo
|
||||
case requestInProgress
|
||||
|
|
@ -99,38 +110,46 @@ enum ChatTitlePanelContext: Equatable, Comparable {
|
|||
}
|
||||
}
|
||||
|
||||
static func <(lhs: ChatTitlePanelContext, rhs: ChatTitlePanelContext) -> Bool {
|
||||
public static func <(lhs: ChatTitlePanelContext, rhs: ChatTitlePanelContext) -> Bool {
|
||||
return lhs.index < rhs.index
|
||||
}
|
||||
}
|
||||
|
||||
struct ChatSearchResultsState: Equatable {
|
||||
let messageIndices: [MessageIndex]
|
||||
let currentId: MessageId?
|
||||
let state: SearchMessagesState
|
||||
let totalCount: Int32
|
||||
let completed: Bool
|
||||
public struct ChatSearchResultsState: Equatable {
|
||||
public let messageIndices: [MessageIndex]
|
||||
public let currentId: MessageId?
|
||||
public let state: SearchMessagesState
|
||||
public let totalCount: Int32
|
||||
public let completed: Bool
|
||||
|
||||
public init(messageIndices: [MessageIndex], currentId: MessageId?, state: SearchMessagesState, totalCount: Int32, completed: Bool) {
|
||||
self.messageIndices = messageIndices
|
||||
self.currentId = currentId
|
||||
self.state = state
|
||||
self.totalCount = totalCount
|
||||
self.completed = completed
|
||||
}
|
||||
}
|
||||
|
||||
enum ChatSearchDomainSuggestionContext: Equatable {
|
||||
public enum ChatSearchDomainSuggestionContext: Equatable {
|
||||
case none
|
||||
case members(String)
|
||||
}
|
||||
|
||||
struct ChatSearchData: Equatable {
|
||||
let query: String
|
||||
let domain: ChatSearchDomain
|
||||
let domainSuggestionContext: ChatSearchDomainSuggestionContext
|
||||
let resultsState: ChatSearchResultsState?
|
||||
public struct ChatSearchData: Equatable {
|
||||
public let query: String
|
||||
public let domain: ChatSearchDomain
|
||||
public let domainSuggestionContext: ChatSearchDomainSuggestionContext
|
||||
public let resultsState: ChatSearchResultsState?
|
||||
|
||||
init(query: String = "", domain: ChatSearchDomain = .everything, domainSuggestionContext: ChatSearchDomainSuggestionContext = .none, resultsState: ChatSearchResultsState? = nil) {
|
||||
public init(query: String = "", domain: ChatSearchDomain = .everything, domainSuggestionContext: ChatSearchDomainSuggestionContext = .none, resultsState: ChatSearchResultsState? = nil) {
|
||||
self.query = query
|
||||
self.domain = domain
|
||||
self.domainSuggestionContext = domainSuggestionContext
|
||||
self.resultsState = resultsState
|
||||
}
|
||||
|
||||
static func ==(lhs: ChatSearchData, rhs: ChatSearchData) -> Bool {
|
||||
public static func ==(lhs: ChatSearchData, rhs: ChatSearchData) -> Bool {
|
||||
if lhs.query != rhs.query {
|
||||
return false
|
||||
}
|
||||
|
|
@ -146,37 +165,37 @@ struct ChatSearchData: Equatable {
|
|||
return true
|
||||
}
|
||||
|
||||
func withUpdatedQuery(_ query: String) -> ChatSearchData {
|
||||
public func withUpdatedQuery(_ query: String) -> ChatSearchData {
|
||||
return ChatSearchData(query: query, domain: self.domain, domainSuggestionContext: self.domainSuggestionContext, resultsState: self.resultsState)
|
||||
}
|
||||
|
||||
func withUpdatedDomain(_ domain: ChatSearchDomain) -> ChatSearchData {
|
||||
public func withUpdatedDomain(_ domain: ChatSearchDomain) -> ChatSearchData {
|
||||
return ChatSearchData(query: self.query, domain: domain, domainSuggestionContext: self.domainSuggestionContext, resultsState: self.resultsState)
|
||||
}
|
||||
|
||||
func withUpdatedDomainSuggestionContext(_ domain: ChatSearchDomainSuggestionContext) -> ChatSearchData {
|
||||
public func withUpdatedDomainSuggestionContext(_ domain: ChatSearchDomainSuggestionContext) -> ChatSearchData {
|
||||
return ChatSearchData(query: self.query, domain: self.domain, domainSuggestionContext: domainSuggestionContext, resultsState: self.resultsState)
|
||||
}
|
||||
|
||||
func withUpdatedResultsState(_ resultsState: ChatSearchResultsState?) -> ChatSearchData {
|
||||
public func withUpdatedResultsState(_ resultsState: ChatSearchResultsState?) -> ChatSearchData {
|
||||
return ChatSearchData(query: self.query, domain: self.domain, domainSuggestionContext: self.domainSuggestionContext, resultsState: resultsState)
|
||||
}
|
||||
}
|
||||
|
||||
final class ChatRecordedMediaPreview: Equatable {
|
||||
let resource: TelegramMediaResource
|
||||
let fileSize: Int32
|
||||
let duration: Int32
|
||||
let waveform: AudioWaveform
|
||||
public final class ChatRecordedMediaPreview: Equatable {
|
||||
public let resource: TelegramMediaResource
|
||||
public let fileSize: Int32
|
||||
public let duration: Int32
|
||||
public let waveform: AudioWaveform
|
||||
|
||||
init(resource: TelegramMediaResource, duration: Int32, fileSize: Int32, waveform: AudioWaveform) {
|
||||
public init(resource: TelegramMediaResource, duration: Int32, fileSize: Int32, waveform: AudioWaveform) {
|
||||
self.resource = resource
|
||||
self.duration = duration
|
||||
self.fileSize = fileSize
|
||||
self.waveform = waveform
|
||||
}
|
||||
|
||||
static func ==(lhs: ChatRecordedMediaPreview, rhs: ChatRecordedMediaPreview) -> Bool {
|
||||
public static func ==(lhs: ChatRecordedMediaPreview, rhs: ChatRecordedMediaPreview) -> Bool {
|
||||
if !lhs.resource.isEqual(to: rhs.resource) {
|
||||
return false
|
||||
}
|
||||
|
|
@ -193,13 +212,20 @@ final class ChatRecordedMediaPreview: Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
struct ChatContactStatus: Equatable {
|
||||
var canAddContact: Bool
|
||||
var canReportIrrelevantLocation: Bool
|
||||
var peerStatusSettings: PeerStatusSettings?
|
||||
var invitedBy: Peer?
|
||||
public struct ChatContactStatus: Equatable {
|
||||
public var canAddContact: Bool
|
||||
public var canReportIrrelevantLocation: Bool
|
||||
public var peerStatusSettings: PeerStatusSettings?
|
||||
public var invitedBy: Peer?
|
||||
|
||||
var isEmpty: Bool {
|
||||
public init(canAddContact: Bool, canReportIrrelevantLocation: Bool, peerStatusSettings: PeerStatusSettings?, invitedBy: Peer?) {
|
||||
self.canAddContact = canAddContact
|
||||
self.canReportIrrelevantLocation = canReportIrrelevantLocation
|
||||
self.peerStatusSettings = peerStatusSettings
|
||||
self.invitedBy = invitedBy
|
||||
}
|
||||
|
||||
public var isEmpty: Bool {
|
||||
guard var peerStatusSettings = self.peerStatusSettings else {
|
||||
return false
|
||||
}
|
||||
|
|
@ -212,7 +238,7 @@ struct ChatContactStatus: Equatable {
|
|||
return peerStatusSettings.flags.isEmpty
|
||||
}
|
||||
|
||||
static func ==(lhs: ChatContactStatus, rhs: ChatContactStatus) -> Bool {
|
||||
public static func ==(lhs: ChatContactStatus, rhs: ChatContactStatus) -> Bool {
|
||||
if lhs.canAddContact != rhs.canAddContact {
|
||||
return false
|
||||
}
|
||||
|
|
@ -229,30 +255,35 @@ struct ChatContactStatus: Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
enum ChatSlowmodeVariant: Equatable {
|
||||
public enum ChatSlowmodeVariant: Equatable {
|
||||
case timestamp(Int32)
|
||||
case pendingMessages
|
||||
}
|
||||
|
||||
struct ChatSlowmodeState: Equatable {
|
||||
var timeout: Int32
|
||||
var variant: ChatSlowmodeVariant
|
||||
public struct ChatSlowmodeState: Equatable {
|
||||
public var timeout: Int32
|
||||
public var variant: ChatSlowmodeVariant
|
||||
|
||||
public init(timeout: Int32, variant: ChatSlowmodeVariant) {
|
||||
self.timeout = timeout
|
||||
self.variant = variant
|
||||
}
|
||||
}
|
||||
|
||||
final class ChatPinnedMessage: Equatable {
|
||||
let message: Message
|
||||
let index: Int
|
||||
let totalCount: Int
|
||||
let topMessageId: MessageId
|
||||
public final class ChatPinnedMessage: Equatable {
|
||||
public let message: Message
|
||||
public let index: Int
|
||||
public let totalCount: Int
|
||||
public let topMessageId: MessageId
|
||||
|
||||
init(message: Message, index: Int, totalCount: Int, topMessageId: MessageId) {
|
||||
public init(message: Message, index: Int, totalCount: Int, topMessageId: MessageId) {
|
||||
self.message = message
|
||||
self.index = index
|
||||
self.totalCount = totalCount
|
||||
self.topMessageId = topMessageId
|
||||
}
|
||||
|
||||
static func ==(lhs: ChatPinnedMessage, rhs: ChatPinnedMessage) -> Bool {
|
||||
public static func ==(lhs: ChatPinnedMessage, rhs: ChatPinnedMessage) -> Bool {
|
||||
if lhs === rhs {
|
||||
return true
|
||||
}
|
||||
|
|
@ -275,74 +306,87 @@ final class ChatPinnedMessage: Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
struct ChatActiveGroupCallInfo: Equatable {
|
||||
var activeCall: CachedChannelData.ActiveCall
|
||||
}
|
||||
|
||||
struct ChatPresentationImportState: Equatable {
|
||||
var progress: Float
|
||||
}
|
||||
|
||||
final class ChatPresentationInterfaceState: Equatable {
|
||||
let interfaceState: ChatInterfaceState
|
||||
let chatLocation: ChatLocation
|
||||
let renderedPeer: RenderedPeer?
|
||||
let isNotAccessible: Bool
|
||||
let explicitelyCanPinMessages: Bool
|
||||
let contactStatus: ChatContactStatus?
|
||||
let hasBots: Bool
|
||||
let isArchived: Bool
|
||||
let inputTextPanelState: ChatTextInputPanelState
|
||||
let editMessageState: ChatEditInterfaceMessageState?
|
||||
let recordedMediaPreview: ChatRecordedMediaPreview?
|
||||
let inputQueryResults: [ChatPresentationInputQueryKind: ChatPresentationInputQueryResult]
|
||||
let inputMode: ChatInputMode
|
||||
let titlePanelContexts: [ChatTitlePanelContext]
|
||||
let keyboardButtonsMessage: Message?
|
||||
let pinnedMessageId: MessageId?
|
||||
let pinnedMessage: ChatPinnedMessage?
|
||||
let peerIsBlocked: Bool
|
||||
let peerIsMuted: Bool
|
||||
let peerDiscussionId: PeerId?
|
||||
let peerGeoLocation: PeerGeoLocation?
|
||||
let callsAvailable: Bool
|
||||
let callsPrivate: Bool
|
||||
let slowmodeState: ChatSlowmodeState?
|
||||
let chatHistoryState: ChatHistoryNodeHistoryState?
|
||||
let botStartPayload: String?
|
||||
let urlPreview: (String, TelegramMediaWebpage)?
|
||||
let editingUrlPreview: (String, TelegramMediaWebpage)?
|
||||
let search: ChatSearchData?
|
||||
let searchQuerySuggestionResult: ChatPresentationInputQueryResult?
|
||||
let presentationReady: Bool
|
||||
let chatWallpaper: TelegramWallpaper
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let dateTimeFormat: PresentationDateTimeFormat
|
||||
let nameDisplayOrder: PresentationPersonNameOrder
|
||||
let limitsConfiguration: LimitsConfiguration
|
||||
let fontSize: PresentationFontSize
|
||||
let bubbleCorners: PresentationChatBubbleCorners
|
||||
let accountPeerId: PeerId
|
||||
let mode: ChatControllerPresentationMode
|
||||
let hasScheduledMessages: Bool
|
||||
let autoremoveTimeout: Int32?
|
||||
let subject: ChatControllerSubject?
|
||||
let peerNearbyData: ChatPeerNearbyData?
|
||||
let greetingData: ChatGreetingData?
|
||||
let pendingUnpinnedAllMessages: Bool
|
||||
let activeGroupCallInfo: ChatActiveGroupCallInfo?
|
||||
let hasActiveGroupCall: Bool
|
||||
let importState: ChatPresentationImportState?
|
||||
let reportReason: ReportReason?
|
||||
let showCommands: Bool
|
||||
let hasBotCommands: Bool
|
||||
let showSendAsPeers: Bool
|
||||
let sendAsPeers: [FoundPeer]?
|
||||
let currentSendAsPeerId: PeerId?
|
||||
let copyProtectionEnabled: Bool
|
||||
public struct ChatActiveGroupCallInfo: Equatable {
|
||||
public var activeCall: CachedChannelData.ActiveCall
|
||||
|
||||
init(chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, limitsConfiguration: LimitsConfiguration, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, accountPeerId: PeerId, mode: ChatControllerPresentationMode, chatLocation: ChatLocation, subject: ChatControllerSubject?, peerNearbyData: ChatPeerNearbyData?, greetingData: ChatGreetingData?, pendingUnpinnedAllMessages: Bool, activeGroupCallInfo: ChatActiveGroupCallInfo?, hasActiveGroupCall: Bool, importState: ChatPresentationImportState?) {
|
||||
public init(activeCall: CachedChannelData.ActiveCall) {
|
||||
self.activeCall = activeCall
|
||||
}
|
||||
}
|
||||
|
||||
public struct ChatPresentationImportState: Equatable {
|
||||
public var progress: Float
|
||||
|
||||
public init(progress: Float) {
|
||||
self.progress = progress
|
||||
}
|
||||
}
|
||||
|
||||
public enum ChatHistoryNodeHistoryState: Equatable {
|
||||
case loading
|
||||
case loaded(isEmpty: Bool)
|
||||
}
|
||||
|
||||
public final class ChatPresentationInterfaceState: Equatable {
|
||||
public let interfaceState: ChatInterfaceState
|
||||
public let chatLocation: ChatLocation
|
||||
public let renderedPeer: RenderedPeer?
|
||||
public let isNotAccessible: Bool
|
||||
public let explicitelyCanPinMessages: Bool
|
||||
public let contactStatus: ChatContactStatus?
|
||||
public let hasBots: Bool
|
||||
public let isArchived: Bool
|
||||
public let inputTextPanelState: ChatTextInputPanelState
|
||||
public let editMessageState: ChatEditInterfaceMessageState?
|
||||
public let recordedMediaPreview: ChatRecordedMediaPreview?
|
||||
public let inputQueryResults: [ChatPresentationInputQueryKind: ChatPresentationInputQueryResult]
|
||||
public let inputMode: ChatInputMode
|
||||
public let titlePanelContexts: [ChatTitlePanelContext]
|
||||
public let keyboardButtonsMessage: Message?
|
||||
public let pinnedMessageId: MessageId?
|
||||
public let pinnedMessage: ChatPinnedMessage?
|
||||
public let peerIsBlocked: Bool
|
||||
public let peerIsMuted: Bool
|
||||
public let peerDiscussionId: PeerId?
|
||||
public let peerGeoLocation: PeerGeoLocation?
|
||||
public let callsAvailable: Bool
|
||||
public let callsPrivate: Bool
|
||||
public let slowmodeState: ChatSlowmodeState?
|
||||
public let chatHistoryState: ChatHistoryNodeHistoryState?
|
||||
public let botStartPayload: String?
|
||||
public let urlPreview: (String, TelegramMediaWebpage)?
|
||||
public let editingUrlPreview: (String, TelegramMediaWebpage)?
|
||||
public let search: ChatSearchData?
|
||||
public let searchQuerySuggestionResult: ChatPresentationInputQueryResult?
|
||||
public let presentationReady: Bool
|
||||
public let chatWallpaper: TelegramWallpaper
|
||||
public let theme: PresentationTheme
|
||||
public let strings: PresentationStrings
|
||||
public let dateTimeFormat: PresentationDateTimeFormat
|
||||
public let nameDisplayOrder: PresentationPersonNameOrder
|
||||
public let limitsConfiguration: LimitsConfiguration
|
||||
public let fontSize: PresentationFontSize
|
||||
public let bubbleCorners: PresentationChatBubbleCorners
|
||||
public let accountPeerId: PeerId
|
||||
public let mode: ChatControllerPresentationMode
|
||||
public let hasScheduledMessages: Bool
|
||||
public let autoremoveTimeout: Int32?
|
||||
public let subject: ChatControllerSubject?
|
||||
public let peerNearbyData: ChatPeerNearbyData?
|
||||
public let greetingData: ChatGreetingData?
|
||||
public let pendingUnpinnedAllMessages: Bool
|
||||
public let activeGroupCallInfo: ChatActiveGroupCallInfo?
|
||||
public let hasActiveGroupCall: Bool
|
||||
public let importState: ChatPresentationImportState?
|
||||
public let reportReason: ReportReason?
|
||||
public let showCommands: Bool
|
||||
public let hasBotCommands: Bool
|
||||
public let showSendAsPeers: Bool
|
||||
public let sendAsPeers: [FoundPeer]?
|
||||
public let currentSendAsPeerId: PeerId?
|
||||
public let copyProtectionEnabled: Bool
|
||||
|
||||
public init(chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, limitsConfiguration: LimitsConfiguration, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, accountPeerId: PeerId, mode: ChatControllerPresentationMode, chatLocation: ChatLocation, subject: ChatControllerSubject?, peerNearbyData: ChatPeerNearbyData?, greetingData: ChatGreetingData?, pendingUnpinnedAllMessages: Bool, activeGroupCallInfo: ChatActiveGroupCallInfo?, hasActiveGroupCall: Bool, importState: ChatPresentationImportState?) {
|
||||
self.interfaceState = ChatInterfaceState()
|
||||
self.inputTextPanelState = ChatTextInputPanelState()
|
||||
self.editMessageState = nil
|
||||
|
|
@ -402,7 +446,7 @@ final class ChatPresentationInterfaceState: Equatable {
|
|||
self.copyProtectionEnabled = false
|
||||
}
|
||||
|
||||
init(interfaceState: ChatInterfaceState, chatLocation: ChatLocation, renderedPeer: RenderedPeer?, isNotAccessible: Bool, explicitelyCanPinMessages: Bool, contactStatus: ChatContactStatus?, hasBots: Bool, isArchived: Bool, inputTextPanelState: ChatTextInputPanelState, editMessageState: ChatEditInterfaceMessageState?, recordedMediaPreview: ChatRecordedMediaPreview?, inputQueryResults: [ChatPresentationInputQueryKind: ChatPresentationInputQueryResult], inputMode: ChatInputMode, titlePanelContexts: [ChatTitlePanelContext], keyboardButtonsMessage: Message?, pinnedMessageId: MessageId?, pinnedMessage: ChatPinnedMessage?, peerIsBlocked: Bool, peerIsMuted: Bool, peerDiscussionId: PeerId?, peerGeoLocation: PeerGeoLocation?, callsAvailable: Bool, callsPrivate: Bool, slowmodeState: ChatSlowmodeState?, chatHistoryState: ChatHistoryNodeHistoryState?, botStartPayload: String?, urlPreview: (String, TelegramMediaWebpage)?, editingUrlPreview: (String, TelegramMediaWebpage)?, search: ChatSearchData?, searchQuerySuggestionResult: ChatPresentationInputQueryResult?, presentationReady: Bool, chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, limitsConfiguration: LimitsConfiguration, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, accountPeerId: PeerId, mode: ChatControllerPresentationMode, hasScheduledMessages: Bool, autoremoveTimeout: Int32?, subject: ChatControllerSubject?, peerNearbyData: ChatPeerNearbyData?, greetingData: ChatGreetingData?, pendingUnpinnedAllMessages: Bool, activeGroupCallInfo: ChatActiveGroupCallInfo?, hasActiveGroupCall: Bool, importState: ChatPresentationImportState?, reportReason: ReportReason?, showCommands: Bool, hasBotCommands: Bool, showSendAsPeers: Bool, sendAsPeers: [FoundPeer]?, currentSendAsPeerId: PeerId?, copyProtectionEnabled: Bool) {
|
||||
public init(interfaceState: ChatInterfaceState, chatLocation: ChatLocation, renderedPeer: RenderedPeer?, isNotAccessible: Bool, explicitelyCanPinMessages: Bool, contactStatus: ChatContactStatus?, hasBots: Bool, isArchived: Bool, inputTextPanelState: ChatTextInputPanelState, editMessageState: ChatEditInterfaceMessageState?, recordedMediaPreview: ChatRecordedMediaPreview?, inputQueryResults: [ChatPresentationInputQueryKind: ChatPresentationInputQueryResult], inputMode: ChatInputMode, titlePanelContexts: [ChatTitlePanelContext], keyboardButtonsMessage: Message?, pinnedMessageId: MessageId?, pinnedMessage: ChatPinnedMessage?, peerIsBlocked: Bool, peerIsMuted: Bool, peerDiscussionId: PeerId?, peerGeoLocation: PeerGeoLocation?, callsAvailable: Bool, callsPrivate: Bool, slowmodeState: ChatSlowmodeState?, chatHistoryState: ChatHistoryNodeHistoryState?, botStartPayload: String?, urlPreview: (String, TelegramMediaWebpage)?, editingUrlPreview: (String, TelegramMediaWebpage)?, search: ChatSearchData?, searchQuerySuggestionResult: ChatPresentationInputQueryResult?, presentationReady: Bool, chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, limitsConfiguration: LimitsConfiguration, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, accountPeerId: PeerId, mode: ChatControllerPresentationMode, hasScheduledMessages: Bool, autoremoveTimeout: Int32?, subject: ChatControllerSubject?, peerNearbyData: ChatPeerNearbyData?, greetingData: ChatGreetingData?, pendingUnpinnedAllMessages: Bool, activeGroupCallInfo: ChatActiveGroupCallInfo?, hasActiveGroupCall: Bool, importState: ChatPresentationImportState?, reportReason: ReportReason?, showCommands: Bool, hasBotCommands: Bool, showSendAsPeers: Bool, sendAsPeers: [FoundPeer]?, currentSendAsPeerId: PeerId?, copyProtectionEnabled: Bool) {
|
||||
self.interfaceState = interfaceState
|
||||
self.chatLocation = chatLocation
|
||||
self.renderedPeer = renderedPeer
|
||||
|
|
@ -462,7 +506,7 @@ final class ChatPresentationInterfaceState: Equatable {
|
|||
self.copyProtectionEnabled = copyProtectionEnabled
|
||||
}
|
||||
|
||||
static func ==(lhs: ChatPresentationInterfaceState, rhs: ChatPresentationInterfaceState) -> Bool {
|
||||
public static func ==(lhs: ChatPresentationInterfaceState, rhs: ChatPresentationInterfaceState) -> Bool {
|
||||
if lhs.interfaceState != rhs.interfaceState {
|
||||
return false
|
||||
}
|
||||
|
|
@ -649,35 +693,35 @@ final class ChatPresentationInterfaceState: Equatable {
|
|||
return true
|
||||
}
|
||||
|
||||
func updatedInterfaceState(_ f: (ChatInterfaceState) -> ChatInterfaceState) -> ChatPresentationInterfaceState {
|
||||
public func updatedInterfaceState(_ f: (ChatInterfaceState) -> ChatInterfaceState) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: f(self.interfaceState), chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedPeer(_ f: (RenderedPeer?) -> RenderedPeer?) -> ChatPresentationInterfaceState {
|
||||
public func updatedPeer(_ f: (RenderedPeer?) -> RenderedPeer?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: f(self.renderedPeer), isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedIsNotAccessible(_ isNotAccessible: Bool) -> ChatPresentationInterfaceState {
|
||||
public func updatedIsNotAccessible(_ isNotAccessible: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedExplicitelyCanPinMessages(_ explicitelyCanPinMessages: Bool) -> ChatPresentationInterfaceState {
|
||||
public func updatedExplicitelyCanPinMessages(_ explicitelyCanPinMessages: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedContactStatus(_ contactStatus: ChatContactStatus?) -> ChatPresentationInterfaceState {
|
||||
public func updatedContactStatus(_ contactStatus: ChatContactStatus?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedHasBots(_ hasBots: Bool) -> ChatPresentationInterfaceState {
|
||||
public func updatedHasBots(_ hasBots: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedIsArchived(_ isArchived: Bool) -> ChatPresentationInterfaceState {
|
||||
public func updatedIsArchived(_ isArchived: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedInputQueryResult(queryKind: ChatPresentationInputQueryKind, _ f: (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?) -> ChatPresentationInterfaceState {
|
||||
public func updatedInputQueryResult(queryKind: ChatPresentationInputQueryKind, _ f: (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?) -> ChatPresentationInterfaceState {
|
||||
var inputQueryResults = self.inputQueryResults
|
||||
let updated = f(inputQueryResults[queryKind])
|
||||
if let updated = updated {
|
||||
|
|
@ -689,172 +733,172 @@ final class ChatPresentationInterfaceState: Equatable {
|
|||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedInputTextPanelState(_ f: (ChatTextInputPanelState) -> ChatTextInputPanelState) -> ChatPresentationInterfaceState {
|
||||
public func updatedInputTextPanelState(_ f: (ChatTextInputPanelState) -> ChatTextInputPanelState) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: f(self.inputTextPanelState), editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedEditMessageState(_ editMessageState: ChatEditInterfaceMessageState?) -> ChatPresentationInterfaceState {
|
||||
public func updatedEditMessageState(_ editMessageState: ChatEditInterfaceMessageState?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedRecordedMediaPreview(_ recordedMediaPreview: ChatRecordedMediaPreview?) -> ChatPresentationInterfaceState {
|
||||
public func updatedRecordedMediaPreview(_ recordedMediaPreview: ChatRecordedMediaPreview?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedInputMode(_ f: (ChatInputMode) -> ChatInputMode) -> ChatPresentationInterfaceState {
|
||||
public func updatedInputMode(_ f: (ChatInputMode) -> ChatInputMode) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: f(self.inputMode), titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedTitlePanelContext(_ f: ([ChatTitlePanelContext]) -> [ChatTitlePanelContext]) -> ChatPresentationInterfaceState {
|
||||
public func updatedTitlePanelContext(_ f: ([ChatTitlePanelContext]) -> [ChatTitlePanelContext]) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: f(self.titlePanelContexts), keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedKeyboardButtonsMessage(_ message: Message?) -> ChatPresentationInterfaceState {
|
||||
public func updatedKeyboardButtonsMessage(_ message: Message?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: message, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> ChatPresentationInterfaceState {
|
||||
public func updatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedPinnedMessage(_ pinnedMessage: ChatPinnedMessage?) -> ChatPresentationInterfaceState {
|
||||
public func updatedPinnedMessage(_ pinnedMessage: ChatPinnedMessage?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedPeerIsBlocked(_ peerIsBlocked: Bool) -> ChatPresentationInterfaceState {
|
||||
public func updatedPeerIsBlocked(_ peerIsBlocked: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedPeerIsMuted(_ peerIsMuted: Bool) -> ChatPresentationInterfaceState {
|
||||
public func updatedPeerIsMuted(_ peerIsMuted: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedPeerDiscussionId(_ peerDiscussionId: PeerId?) -> ChatPresentationInterfaceState {
|
||||
public func updatedPeerDiscussionId(_ peerDiscussionId: PeerId?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedPeerGeoLocation(_ peerGeoLocation: PeerGeoLocation?) -> ChatPresentationInterfaceState {
|
||||
public func updatedPeerGeoLocation(_ peerGeoLocation: PeerGeoLocation?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedCallsAvailable(_ callsAvailable: Bool) -> ChatPresentationInterfaceState {
|
||||
public func updatedCallsAvailable(_ callsAvailable: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedCallsPrivate(_ callsPrivate: Bool) -> ChatPresentationInterfaceState {
|
||||
public func updatedCallsPrivate(_ callsPrivate: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedSlowmodeState(_ slowmodeState: ChatSlowmodeState?) -> ChatPresentationInterfaceState {
|
||||
public func updatedSlowmodeState(_ slowmodeState: ChatSlowmodeState?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedBotStartPayload(_ botStartPayload: String?) -> ChatPresentationInterfaceState {
|
||||
public func updatedBotStartPayload(_ botStartPayload: String?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedChatHistoryState(_ chatHistoryState: ChatHistoryNodeHistoryState?) -> ChatPresentationInterfaceState {
|
||||
public func updatedChatHistoryState(_ chatHistoryState: ChatHistoryNodeHistoryState?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedUrlPreview(_ urlPreview: (String, TelegramMediaWebpage)?) -> ChatPresentationInterfaceState {
|
||||
public func updatedUrlPreview(_ urlPreview: (String, TelegramMediaWebpage)?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedEditingUrlPreview(_ editingUrlPreview: (String, TelegramMediaWebpage)?) -> ChatPresentationInterfaceState {
|
||||
public func updatedEditingUrlPreview(_ editingUrlPreview: (String, TelegramMediaWebpage)?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedSearch(_ search: ChatSearchData?) -> ChatPresentationInterfaceState {
|
||||
public func updatedSearch(_ search: ChatSearchData?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedSearchQuerySuggestionResult(_ f: (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?) -> ChatPresentationInterfaceState {
|
||||
public func updatedSearchQuerySuggestionResult(_ f: (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: f(self.searchQuerySuggestionResult), presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedMode(_ mode: ChatControllerPresentationMode) -> ChatPresentationInterfaceState {
|
||||
public func updatedMode(_ mode: ChatControllerPresentationMode) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedPresentationReady(_ presentationReady: Bool) -> ChatPresentationInterfaceState {
|
||||
public func updatedPresentationReady(_ presentationReady: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedTheme(_ theme: PresentationTheme) -> ChatPresentationInterfaceState {
|
||||
public func updatedTheme(_ theme: PresentationTheme) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedStrings(_ strings: PresentationStrings) -> ChatPresentationInterfaceState {
|
||||
public func updatedStrings(_ strings: PresentationStrings) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedDateTimeFormat(_ dateTimeFormat: PresentationDateTimeFormat) -> ChatPresentationInterfaceState {
|
||||
public func updatedDateTimeFormat(_ dateTimeFormat: PresentationDateTimeFormat) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedChatWallpaper(_ chatWallpaper: TelegramWallpaper) -> ChatPresentationInterfaceState {
|
||||
public func updatedChatWallpaper(_ chatWallpaper: TelegramWallpaper) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedBubbleCorners(_ bubbleCorners: PresentationChatBubbleCorners) -> ChatPresentationInterfaceState {
|
||||
public func updatedBubbleCorners(_ bubbleCorners: PresentationChatBubbleCorners) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> ChatPresentationInterfaceState {
|
||||
public func updatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedAutoremoveTimeout(_ autoremoveTimeout: Int32?) -> ChatPresentationInterfaceState {
|
||||
public func updatedAutoremoveTimeout(_ autoremoveTimeout: Int32?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedPendingUnpinnedAllMessages(_ pendingUnpinnedAllMessages: Bool) -> ChatPresentationInterfaceState {
|
||||
public func updatedPendingUnpinnedAllMessages(_ pendingUnpinnedAllMessages: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedActiveGroupCallInfo(_ activeGroupCallInfo: ChatActiveGroupCallInfo?) -> ChatPresentationInterfaceState {
|
||||
public func updatedActiveGroupCallInfo(_ activeGroupCallInfo: ChatActiveGroupCallInfo?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedHasActiveGroupCall(_ hasActiveGroupCall: Bool) -> ChatPresentationInterfaceState {
|
||||
public func updatedHasActiveGroupCall(_ hasActiveGroupCall: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedImportState(_ importState: ChatPresentationImportState?) -> ChatPresentationInterfaceState {
|
||||
public func updatedImportState(_ importState: ChatPresentationImportState?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedReportReason(_ reportReason: ReportReason?) -> ChatPresentationInterfaceState {
|
||||
public func updatedReportReason(_ reportReason: ReportReason?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedShowCommands(_ showCommands: Bool) -> ChatPresentationInterfaceState {
|
||||
public func updatedShowCommands(_ showCommands: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedHasBotCommands(_ hasBotCommands: Bool) -> ChatPresentationInterfaceState {
|
||||
public func updatedHasBotCommands(_ hasBotCommands: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedShowSendAsPeers(_ showSendAsPeers: Bool) -> ChatPresentationInterfaceState {
|
||||
public func updatedShowSendAsPeers(_ showSendAsPeers: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedSendAsPeers(_ sendAsPeers: [FoundPeer]?) -> ChatPresentationInterfaceState {
|
||||
public func updatedSendAsPeers(_ sendAsPeers: [FoundPeer]?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedCurrentSendAsPeerId(_ currentSendAsPeerId: PeerId?) -> ChatPresentationInterfaceState {
|
||||
public func updatedCurrentSendAsPeerId(_ currentSendAsPeerId: PeerId?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled)
|
||||
}
|
||||
|
||||
func updatedCopyProtectionEnabled(_ copyProtectionEnabled: Bool) -> ChatPresentationInterfaceState {
|
||||
public func updatedCopyProtectionEnabled(_ copyProtectionEnabled: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: copyProtectionEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
func canSendMessagesToChat(_ state: ChatPresentationInterfaceState) -> Bool {
|
||||
public func canSendMessagesToChat(_ state: ChatPresentationInterfaceState) -> Bool {
|
||||
if let peer = state.renderedPeer?.peer {
|
||||
if canSendMessagesToPeer(peer) {
|
||||
return true
|
||||
|
|
@ -4,7 +4,7 @@ import Postbox
|
|||
import TelegramCore
|
||||
import AccountContext
|
||||
|
||||
func chatTextInputAddFormattingAttribute(_ state: ChatTextInputState, attribute: NSAttributedString.Key) -> ChatTextInputState {
|
||||
public func chatTextInputAddFormattingAttribute(_ state: ChatTextInputState, attribute: NSAttributedString.Key) -> ChatTextInputState {
|
||||
if !state.selectionRange.isEmpty {
|
||||
let nsRange = NSRange(location: state.selectionRange.lowerBound, length: state.selectionRange.count)
|
||||
var addAttribute = true
|
||||
|
|
@ -31,7 +31,7 @@ func chatTextInputAddFormattingAttribute(_ state: ChatTextInputState, attribute:
|
|||
}
|
||||
}
|
||||
|
||||
func chatTextInputClearFormattingAttributes(_ state: ChatTextInputState) -> ChatTextInputState {
|
||||
public func chatTextInputClearFormattingAttributes(_ state: ChatTextInputState) -> ChatTextInputState {
|
||||
if !state.selectionRange.isEmpty {
|
||||
let nsRange = NSRange(location: state.selectionRange.lowerBound, length: state.selectionRange.count)
|
||||
var attributesToRemove: [NSAttributedString.Key] = []
|
||||
|
|
@ -51,7 +51,7 @@ func chatTextInputClearFormattingAttributes(_ state: ChatTextInputState) -> Chat
|
|||
}
|
||||
}
|
||||
|
||||
func chatTextInputAddLinkAttribute(_ state: ChatTextInputState, selectionRange: Range<Int>, url: String) -> ChatTextInputState {
|
||||
public func chatTextInputAddLinkAttribute(_ state: ChatTextInputState, selectionRange: Range<Int>, url: String) -> ChatTextInputState {
|
||||
if !selectionRange.isEmpty {
|
||||
let nsRange = NSRange(location: selectionRange.lowerBound, length: selectionRange.count)
|
||||
var linkRange = nsRange
|
||||
|
|
@ -78,7 +78,7 @@ func chatTextInputAddLinkAttribute(_ state: ChatTextInputState, selectionRange:
|
|||
}
|
||||
}
|
||||
|
||||
func chatTextInputAddMentionAttribute(_ state: ChatTextInputState, peer: Peer) -> ChatTextInputState {
|
||||
public func chatTextInputAddMentionAttribute(_ state: ChatTextInputState, peer: Peer) -> ChatTextInputState {
|
||||
let inputText = NSMutableAttributedString(attributedString: state.inputText)
|
||||
|
||||
let range = NSMakeRange(state.selectionRange.startIndex, state.selectionRange.endIndex - state.selectionRange.startIndex)
|
||||
|
|
@ -1,28 +1,49 @@
|
|||
import Foundation
|
||||
import AccountContext
|
||||
import SwiftSignalKit
|
||||
|
||||
struct ChatTextInputPanelState: Equatable {
|
||||
let accessoryItems: [ChatTextInputAccessoryItem]
|
||||
let contextPlaceholder: NSAttributedString?
|
||||
let mediaRecordingState: ChatTextInputPanelMediaRecordingState?
|
||||
public enum ChatTextInputAccessoryItem: Equatable {
|
||||
case keyboard
|
||||
case stickers(Bool)
|
||||
case inputButtons
|
||||
case commands
|
||||
case silentPost(Bool)
|
||||
case messageAutoremoveTimeout(Int32?)
|
||||
case scheduledMessages
|
||||
}
|
||||
|
||||
public final class InstantVideoControllerRecordingStatus {
|
||||
public let micLevel: Signal<Float, NoError>
|
||||
public let duration: Signal<TimeInterval, NoError>
|
||||
|
||||
init(accessoryItems: [ChatTextInputAccessoryItem], contextPlaceholder: NSAttributedString?, mediaRecordingState: ChatTextInputPanelMediaRecordingState?) {
|
||||
public init(micLevel: Signal<Float, NoError>, duration: Signal<TimeInterval, NoError>) {
|
||||
self.micLevel = micLevel
|
||||
self.duration = duration
|
||||
}
|
||||
}
|
||||
|
||||
public struct ChatTextInputPanelState: Equatable {
|
||||
public let accessoryItems: [ChatTextInputAccessoryItem]
|
||||
public let contextPlaceholder: NSAttributedString?
|
||||
public let mediaRecordingState: ChatTextInputPanelMediaRecordingState?
|
||||
|
||||
public init(accessoryItems: [ChatTextInputAccessoryItem], contextPlaceholder: NSAttributedString?, mediaRecordingState: ChatTextInputPanelMediaRecordingState?) {
|
||||
self.accessoryItems = accessoryItems
|
||||
self.contextPlaceholder = contextPlaceholder
|
||||
self.mediaRecordingState = mediaRecordingState
|
||||
}
|
||||
|
||||
init() {
|
||||
public init() {
|
||||
self.accessoryItems = []
|
||||
self.contextPlaceholder = nil
|
||||
self.mediaRecordingState = nil
|
||||
}
|
||||
|
||||
func withUpdatedMediaRecordingState(_ mediaRecordingState: ChatTextInputPanelMediaRecordingState?) -> ChatTextInputPanelState {
|
||||
public func withUpdatedMediaRecordingState(_ mediaRecordingState: ChatTextInputPanelMediaRecordingState?) -> ChatTextInputPanelState {
|
||||
return ChatTextInputPanelState(accessoryItems: self.accessoryItems, contextPlaceholder: self.contextPlaceholder, mediaRecordingState: mediaRecordingState)
|
||||
}
|
||||
|
||||
static func ==(lhs: ChatTextInputPanelState, rhs: ChatTextInputPanelState) -> Bool {
|
||||
public static func ==(lhs: ChatTextInputPanelState, rhs: ChatTextInputPanelState) -> Bool {
|
||||
if lhs.accessoryItems != rhs.accessoryItems {
|
||||
return false
|
||||
}
|
||||
|
|
@ -38,11 +59,11 @@ struct ChatTextInputPanelState: Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
enum ChatVideoRecordingStatus: Equatable {
|
||||
public enum ChatVideoRecordingStatus: Equatable {
|
||||
case recording(InstantVideoControllerRecordingStatus)
|
||||
case editing
|
||||
|
||||
static func ==(lhs: ChatVideoRecordingStatus, rhs: ChatVideoRecordingStatus) -> Bool {
|
||||
public static func ==(lhs: ChatVideoRecordingStatus, rhs: ChatVideoRecordingStatus) -> Bool {
|
||||
switch lhs {
|
||||
case let .recording(lhsStatus):
|
||||
if case let .recording(rhsStatus) = rhs, lhsStatus === rhsStatus {
|
||||
|
|
@ -60,12 +81,12 @@ enum ChatVideoRecordingStatus: Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
enum ChatTextInputPanelMediaRecordingState: Equatable {
|
||||
public enum ChatTextInputPanelMediaRecordingState: Equatable {
|
||||
case audio(recorder: ManagedAudioRecorder, isLocked: Bool)
|
||||
case video(status: ChatVideoRecordingStatus, isLocked: Bool)
|
||||
case waitingForPreview
|
||||
|
||||
var isLocked: Bool {
|
||||
public var isLocked: Bool {
|
||||
switch self {
|
||||
case let .audio(_, isLocked):
|
||||
return isLocked
|
||||
|
|
@ -76,7 +97,7 @@ enum ChatTextInputPanelMediaRecordingState: Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
func withLocked(_ isLocked: Bool) -> ChatTextInputPanelMediaRecordingState {
|
||||
public func withLocked(_ isLocked: Bool) -> ChatTextInputPanelMediaRecordingState {
|
||||
switch self {
|
||||
case let .audio(recorder, _):
|
||||
return .audio(recorder: recorder, isLocked: isLocked)
|
||||
|
|
@ -87,7 +108,7 @@ enum ChatTextInputPanelMediaRecordingState: Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
static func ==(lhs: ChatTextInputPanelMediaRecordingState, rhs: ChatTextInputPanelMediaRecordingState) -> Bool {
|
||||
public static func ==(lhs: ChatTextInputPanelMediaRecordingState, rhs: ChatTextInputPanelMediaRecordingState) -> Bool {
|
||||
switch lhs {
|
||||
case let .audio(lhsRecorder, lhsIsLocked):
|
||||
if case let .audio(rhsRecorder, rhsIsLocked) = rhs, lhsRecorder === rhsRecorder, lhsIsLocked == rhsIsLocked {
|
||||
26
submodules/ChatSendMessageActionUI/BUILD
Normal file
26
submodules/ChatSendMessageActionUI/BUILD
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "ChatSendMessageActionUI",
|
||||
module_name = "ChatSendMessageActionUI",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/ChatPresentationInterfaceState:ChatPresentationInterfaceState",
|
||||
"//submodules/ContextUI:ContextUI",
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
|
|
@ -7,9 +7,10 @@ import TelegramPresentationData
|
|||
import AccountContext
|
||||
import ContextUI
|
||||
import TelegramCore
|
||||
import ChatPresentationInterfaceState
|
||||
|
||||
final class ChatSendMessageActionSheetController: ViewController {
|
||||
var controllerNode: ChatSendMessageActionSheetControllerNode {
|
||||
public final class ChatSendMessageActionSheetController: ViewController {
|
||||
private var controllerNode: ChatSendMessageActionSheetControllerNode {
|
||||
return self.displayNode as! ChatSendMessageActionSheetControllerNode
|
||||
}
|
||||
|
||||
|
|
@ -31,7 +32,7 @@ final class ChatSendMessageActionSheetController: ViewController {
|
|||
|
||||
private let hapticFeedback = HapticFeedback()
|
||||
|
||||
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, interfaceState: ChatPresentationInterfaceState, gesture: ContextGesture, sourceSendButton: ASDisplayNode, textInputNode: EditableTextNode, completion: @escaping () -> Void, sendMessage: @escaping (Bool) -> Void, schedule: @escaping () -> Void) {
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, interfaceState: ChatPresentationInterfaceState, gesture: ContextGesture, sourceSendButton: ASDisplayNode, textInputNode: EditableTextNode, completion: @escaping () -> Void, sendMessage: @escaping (Bool) -> Void, schedule: @escaping () -> Void) {
|
||||
self.context = context
|
||||
self.interfaceState = interfaceState
|
||||
self.gesture = gesture
|
||||
|
|
@ -67,7 +68,7 @@ final class ChatSendMessageActionSheetController: ViewController {
|
|||
self.presentationDataDisposable?.dispose()
|
||||
}
|
||||
|
||||
override func loadDisplayNode() {
|
||||
override public func loadDisplayNode() {
|
||||
var forwardedCount = 0
|
||||
if let forwardMessageIds = self.interfaceState.interfaceState.forwardMessageIds {
|
||||
forwardedCount = forwardMessageIds.count
|
||||
|
|
@ -156,7 +156,6 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
|||
private let sourceSendButton: ASDisplayNode
|
||||
private let textFieldFrame: CGRect
|
||||
private let textInputNode: EditableTextNode
|
||||
private let accessoryPanelNode: AccessoryPanelNode?
|
||||
private let forwardedCount: Int?
|
||||
|
||||
private let send: (() -> Void)?
|
||||
|
|
@ -187,7 +186,6 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
|||
self.sourceSendButton = sourceSendButton
|
||||
self.textFieldFrame = textInputNode.convert(textInputNode.bounds, to: nil)
|
||||
self.textInputNode = textInputNode
|
||||
self.accessoryPanelNode = nil
|
||||
self.forwardedCount = forwardedCount
|
||||
|
||||
self.send = send
|
||||
|
|
@ -280,10 +278,6 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
|||
self.messageClipNode.addSubnode(self.messageBackgroundNode)
|
||||
self.messageClipNode.addSubnode(self.fromMessageTextNode)
|
||||
self.messageClipNode.addSubnode(self.toMessageTextNode)
|
||||
|
||||
if let accessoryPanelNode = self.accessoryPanelNode {
|
||||
self.addSubnode(accessoryPanelNode)
|
||||
}
|
||||
|
||||
self.contentNodes.forEach(self.contentContainerNode.addSubnode)
|
||||
|
||||
|
|
@ -652,11 +646,6 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
|||
|
||||
self.fromMessageTextNode.frame = textFrame
|
||||
self.toMessageTextNode.frame = textFrame
|
||||
|
||||
if let accessoryPanelNode = self.accessoryPanelNode {
|
||||
let size = accessoryPanelNode.calculateSizeThatFits(CGSize(width: messageFrame.width, height: 45.0))
|
||||
accessoryPanelNode.frame = CGRect(origin: CGPoint(x: 0.0, y: self.textFieldFrame.minY - size.height - 7.0), size: size)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func dimTapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
25
submodules/ChatTextLinkEditUI/BUILD
Normal file
25
submodules/ChatTextLinkEditUI/BUILD
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "ChatTextLinkEditUI",
|
||||
module_name = "ChatTextLinkEditUI",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/Postbox:Postbox",
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/UrlEscaping:UrlEscaping",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
|
|
@ -385,7 +385,7 @@ private final class ChatTextLinkEditAlertContentNode: AlertContentNode {
|
|||
}
|
||||
}
|
||||
|
||||
func chatTextLinkEditController(sharedContext: SharedAccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, account: Account, text: String, link: String?, apply: @escaping (String?) -> Void) -> AlertController {
|
||||
public func chatTextLinkEditController(sharedContext: SharedAccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, account: Account, text: String, link: String?, apply: @escaping (String?) -> Void) -> AlertController {
|
||||
let presentationData = updatedPresentationData?.initial ?? sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
var dismissImpl: ((Bool) -> Void)?
|
||||
|
|
@ -67,8 +67,14 @@ public extension Transition.DisappearWithGuide {
|
|||
public extension Transition.Update {
|
||||
static let `default` = Transition.Update { component, view, transition in
|
||||
let frame = component.size.centered(around: component._position ?? CGPoint())
|
||||
if view.frame != frame {
|
||||
transition.setFrame(view: view, frame: frame)
|
||||
if let scale = component._scale {
|
||||
transition.setBounds(view: view, bounds: CGRect(origin: CGPoint(), size: frame.size))
|
||||
transition.setPosition(view: view, position: frame.center)
|
||||
transition.setScale(view: view, scale: scale)
|
||||
} else {
|
||||
if view.frame != frame {
|
||||
transition.setFrame(view: view, frame: frame)
|
||||
}
|
||||
}
|
||||
let opacity = component._opacity ?? 1.0
|
||||
if view.alpha != opacity {
|
||||
|
|
|
|||
|
|
@ -175,6 +175,7 @@ public final class _UpdatedChildComponent {
|
|||
|
||||
var _removed: Bool = false
|
||||
var _position: CGPoint?
|
||||
var _scale: CGFloat?
|
||||
var _opacity: CGFloat?
|
||||
var _cornerRadius: CGFloat?
|
||||
var _clipsToBounds: Bool?
|
||||
|
|
@ -239,6 +240,11 @@ public final class _UpdatedChildComponent {
|
|||
return self
|
||||
}
|
||||
|
||||
@discardableResult public func scale(_ scale: CGFloat) -> _UpdatedChildComponent {
|
||||
self._scale = scale
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult public func opacity(_ opacity: CGFloat) -> _UpdatedChildComponent {
|
||||
self._opacity = opacity
|
||||
return self
|
||||
|
|
@ -683,7 +689,13 @@ public extension CombinedComponent {
|
|||
|
||||
view.insertSubview(updatedChild.view, at: index)
|
||||
|
||||
updatedChild.view.frame = updatedChild.size.centered(around: updatedChild._position ?? CGPoint())
|
||||
if let scale = updatedChild._scale {
|
||||
updatedChild.view.bounds = CGRect(origin: CGPoint(), size: updatedChild.size)
|
||||
updatedChild.view.center = updatedChild._position ?? CGPoint()
|
||||
updatedChild.view.transform = CGAffineTransform(scaleX: scale, y: scale)
|
||||
} else {
|
||||
updatedChild.view.frame = updatedChild.size.centered(around: updatedChild._position ?? CGPoint())
|
||||
}
|
||||
updatedChild.view.alpha = updatedChild._opacity ?? 1.0
|
||||
updatedChild.view.clipsToBounds = updatedChild._clipsToBounds ?? false
|
||||
updatedChild.view.layer.cornerRadius = updatedChild._cornerRadius ?? 0.0
|
||||
|
|
|
|||
|
|
@ -176,6 +176,40 @@ public struct Transition {
|
|||
}
|
||||
}
|
||||
|
||||
public func setBounds(view: UIView, bounds: CGRect, completion: ((Bool) -> Void)? = nil) {
|
||||
if view.bounds == bounds {
|
||||
completion?(true)
|
||||
return
|
||||
}
|
||||
switch self.animation {
|
||||
case .none:
|
||||
view.bounds = bounds
|
||||
completion?(true)
|
||||
case .curve:
|
||||
let previousBounds = view.bounds
|
||||
view.bounds = bounds
|
||||
|
||||
self.animateBounds(view: view, from: previousBounds, to: view.bounds, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
public func setPosition(view: UIView, position: CGPoint, completion: ((Bool) -> Void)? = nil) {
|
||||
if view.center == position {
|
||||
completion?(true)
|
||||
return
|
||||
}
|
||||
switch self.animation {
|
||||
case .none:
|
||||
view.center = position
|
||||
completion?(true)
|
||||
case .curve:
|
||||
let previousPosition = view.center
|
||||
view.center = position
|
||||
|
||||
self.animatePosition(view: view, from: previousPosition, to: view.center, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
public func setAlpha(view: UIView, alpha: CGFloat, completion: ((Bool) -> Void)? = nil) {
|
||||
if view.alpha == alpha {
|
||||
completion?(true)
|
||||
|
|
@ -191,7 +225,35 @@ public struct Transition {
|
|||
self.animateAlpha(view: view, from: previousAlpha, to: alpha, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func setScale(view: UIView, scale: CGFloat, completion: ((Bool) -> Void)? = nil) {
|
||||
let t = view.layer.presentation()?.transform ?? view.layer.transform
|
||||
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
|
||||
if currentScale == scale {
|
||||
completion?(true)
|
||||
return
|
||||
}
|
||||
switch self.animation {
|
||||
case .none:
|
||||
view.layer.transform = CATransform3DMakeScale(scale, scale, 1.0)
|
||||
completion?(true)
|
||||
case let .curve(duration, curve):
|
||||
let previousScale = currentScale
|
||||
view.layer.transform = CATransform3DMakeScale(scale, scale, 1.0)
|
||||
view.layer.animate(
|
||||
from: previousScale as NSNumber,
|
||||
to: scale as NSNumber,
|
||||
keyPath: "transform.scale",
|
||||
duration: duration,
|
||||
delay: 0.0,
|
||||
curve: curve,
|
||||
removeOnCompletion: true,
|
||||
additive: false,
|
||||
completion: completion
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public func setSublayerTransform(view: UIView, transform: CATransform3D, completion: ((Bool) -> Void)? = nil) {
|
||||
switch self.animation {
|
||||
case .none:
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ swift_library(
|
|||
"//submodules/PresentationDataUtils:PresentationDataUtils",
|
||||
"//submodules/TextFormat:TextFormat",
|
||||
"//submodules/ObjCRuntimeUtils:ObjCRuntimeUtils",
|
||||
"//submodules/AttachmentUI:AttachmentUI",
|
||||
"//submodules/TextInputMenu:TextInputMenu",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import AccountContext
|
|||
import AlertUI
|
||||
import PresentationDataUtils
|
||||
import TextFormat
|
||||
import AttachmentUI
|
||||
|
||||
private struct OrderedLinkedListItemOrderingId: RawRepresentable, Hashable {
|
||||
var rawValue: Int
|
||||
|
|
@ -160,8 +161,9 @@ private final class CreatePollControllerArguments {
|
|||
let updateQuiz: (Bool) -> Void
|
||||
let updateSolutionText: (NSAttributedString) -> Void
|
||||
let solutionTextFocused: (Bool) -> Void
|
||||
let questionTextFocused: (Bool) -> Void
|
||||
|
||||
init(updatePollText: @escaping (String) -> Void, updateOptionText: @escaping (Int, String, Bool) -> Void, moveToNextOption: @escaping (Int) -> Void, moveToPreviousOption: @escaping (Int) -> Void, removeOption: @escaping (Int, Bool) -> Void, optionFocused: @escaping (Int, Bool) -> Void, setItemIdWithRevealedOptions: @escaping (Int?, Int?) -> Void, toggleOptionSelected: @escaping (Int) -> Void, updateAnonymous: @escaping (Bool) -> Void, updateMultipleChoice: @escaping (Bool) -> Void, displayMultipleChoiceDisabled: @escaping () -> Void, updateQuiz: @escaping (Bool) -> Void, updateSolutionText: @escaping (NSAttributedString) -> Void, solutionTextFocused: @escaping (Bool) -> Void) {
|
||||
init(updatePollText: @escaping (String) -> Void, updateOptionText: @escaping (Int, String, Bool) -> Void, moveToNextOption: @escaping (Int) -> Void, moveToPreviousOption: @escaping (Int) -> Void, removeOption: @escaping (Int, Bool) -> Void, optionFocused: @escaping (Int, Bool) -> Void, setItemIdWithRevealedOptions: @escaping (Int?, Int?) -> Void, toggleOptionSelected: @escaping (Int) -> Void, updateAnonymous: @escaping (Bool) -> Void, updateMultipleChoice: @escaping (Bool) -> Void, displayMultipleChoiceDisabled: @escaping () -> Void, updateQuiz: @escaping (Bool) -> Void, updateSolutionText: @escaping (NSAttributedString) -> Void, solutionTextFocused: @escaping (Bool) -> Void, questionTextFocused: @escaping (Bool) -> Void) {
|
||||
self.updatePollText = updatePollText
|
||||
self.updateOptionText = updateOptionText
|
||||
self.moveToNextOption = moveToNextOption
|
||||
|
|
@ -176,6 +178,7 @@ private final class CreatePollControllerArguments {
|
|||
self.updateQuiz = updateQuiz
|
||||
self.updateSolutionText = updateSolutionText
|
||||
self.solutionTextFocused = solutionTextFocused
|
||||
self.questionTextFocused = questionTextFocused
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -348,6 +351,8 @@ private enum CreatePollEntry: ItemListNodeEntry {
|
|||
case let .text(placeholder, text, maxLength):
|
||||
return ItemListMultilineInputItem(presentationData: presentationData, text: text, placeholder: placeholder, maxLength: ItemListMultilineInputItemTextLimit(value: maxLength, display: false), sectionId: self.section, style: .blocks, textUpdated: { value in
|
||||
arguments.updatePollText(value)
|
||||
}, updatedFocus: { value in
|
||||
arguments.questionTextFocused(value)
|
||||
}, tag: CreatePollEntryTag.text)
|
||||
case let .optionsHeader(text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
|
|
@ -511,7 +516,11 @@ public final class ComposedPoll {
|
|||
}
|
||||
}
|
||||
|
||||
public func createPollController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer, isQuiz: Bool? = nil, completion: @escaping (ComposedPoll) -> Void) -> ViewController {
|
||||
private class CreatePollControllerImpl: ItemListController, AttachmentContainable {
|
||||
public var requestAttachmentMenuExpansion: () -> Void = {}
|
||||
}
|
||||
|
||||
public func createPollController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer, isQuiz: Bool? = nil, completion: @escaping (ComposedPoll) -> Void) -> AttachmentContainable {
|
||||
var initialState = CreatePollControllerState()
|
||||
if let isQuiz = isQuiz {
|
||||
initialState.isQuiz = isQuiz
|
||||
|
|
@ -528,6 +537,7 @@ public func createPollController(context: AccountContext, updatedPresentationDat
|
|||
var ensureTextVisibleImpl: (() -> Void)?
|
||||
var ensureOptionVisibleImpl: ((Int) -> Void)?
|
||||
var ensureSolutionVisibleImpl: (() -> Void)?
|
||||
var ensureQuestionVisibleImpl: (() -> Void)?
|
||||
var displayQuizTooltipImpl: ((Bool) -> Void)?
|
||||
var attemptNavigationImpl: (() -> Bool)?
|
||||
|
||||
|
|
@ -767,6 +777,10 @@ public func createPollController(context: AccountContext, updatedPresentationDat
|
|||
if isFocused {
|
||||
ensureSolutionVisibleImpl?()
|
||||
}
|
||||
}, questionTextFocused: { isFocused in
|
||||
if isFocused {
|
||||
ensureQuestionVisibleImpl?()
|
||||
}
|
||||
})
|
||||
|
||||
let previousOptionIds = Atomic<[Int]?>(value: nil)
|
||||
|
|
@ -905,7 +919,7 @@ public func createPollController(context: AccountContext, updatedPresentationDat
|
|||
}
|
||||
|
||||
weak var currentTooltipController: TooltipController?
|
||||
let controller = ItemListController(context: context, state: signal)
|
||||
let controller = CreatePollControllerImpl(context: context, state: signal)
|
||||
controller.navigationPresentation = .modal
|
||||
presentControllerImpl = { [weak controller] c, a in
|
||||
controller?.present(c, in: .window(.root), with: a)
|
||||
|
|
@ -940,6 +954,8 @@ public func createPollController(context: AccountContext, updatedPresentationDat
|
|||
return
|
||||
}
|
||||
|
||||
controller.requestAttachmentMenuExpansion()
|
||||
|
||||
var resultItemNode: ListViewItemNode?
|
||||
let _ = controller.frameForItemNode({ itemNode in
|
||||
if let itemNode = itemNode as? ItemListItemNode {
|
||||
|
|
@ -955,12 +971,22 @@ public func createPollController(context: AccountContext, updatedPresentationDat
|
|||
}
|
||||
})
|
||||
}
|
||||
ensureQuestionVisibleImpl = { [weak controller] in
|
||||
controller?.afterLayout({
|
||||
guard let controller = controller else {
|
||||
return
|
||||
}
|
||||
controller.requestAttachmentMenuExpansion()
|
||||
})
|
||||
}
|
||||
ensureOptionVisibleImpl = { [weak controller] id in
|
||||
controller?.afterLayout({
|
||||
guard let controller = controller else {
|
||||
return
|
||||
}
|
||||
|
||||
controller.requestAttachmentMenuExpansion()
|
||||
|
||||
var resultItemNode: ListViewItemNode?
|
||||
let state = stateValue.with({ $0 })
|
||||
var isLast = false
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import TelegramPresentationData
|
|||
import ItemListUI
|
||||
import TextFormat
|
||||
import ObjCRuntimeUtils
|
||||
import TextInputMenu
|
||||
|
||||
public enum CreatePollTextInputItemTextLimitMode {
|
||||
case characters
|
||||
|
|
@ -108,95 +109,6 @@ public class CreatePollTextInputItem: ListViewItem, ItemListItem {
|
|||
}
|
||||
}
|
||||
|
||||
private enum ChatTextInputMenuState {
|
||||
case inactive
|
||||
case general
|
||||
case format
|
||||
}
|
||||
|
||||
private final class ChatTextInputMenu {
|
||||
private var stringBold: String = "Bold"
|
||||
private var stringItalic: String = "Italic"
|
||||
private var stringMonospace: String = "Monospace"
|
||||
private var stringLink: String = "Link"
|
||||
private var stringStrikethrough: String = "Strikethrough"
|
||||
private var stringUnderline: String = "Underline"
|
||||
|
||||
private(set) var state: ChatTextInputMenuState = .inactive {
|
||||
didSet {
|
||||
if self.state != oldValue {
|
||||
switch self.state {
|
||||
case .inactive:
|
||||
UIMenuController.shared.menuItems = []
|
||||
case .general:
|
||||
UIMenuController.shared.menuItems = []
|
||||
case .format:
|
||||
UIMenuController.shared.menuItems = [
|
||||
UIMenuItem(title: self.stringBold, action: Selector(("formatAttributesBold:"))),
|
||||
UIMenuItem(title: self.stringItalic, action: Selector(("formatAttributesItalic:"))),
|
||||
UIMenuItem(title: self.stringMonospace, action: Selector(("formatAttributesMonospace:"))),
|
||||
UIMenuItem(title: self.stringStrikethrough, action: Selector(("formatAttributesStrikethrough:"))),
|
||||
UIMenuItem(title: self.stringUnderline, action: Selector(("formatAttributesUnderline:")))
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var observer: NSObjectProtocol?
|
||||
|
||||
init() {
|
||||
self.observer = NotificationCenter.default.addObserver(forName: UIMenuController.didHideMenuNotification, object: nil, queue: nil, using: { [weak self] _ in
|
||||
self?.back()
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let observer = self.observer {
|
||||
NotificationCenter.default.removeObserver(observer)
|
||||
}
|
||||
}
|
||||
|
||||
func updateStrings(_ strings: PresentationStrings) {
|
||||
self.stringBold = strings.TextFormat_Bold
|
||||
self.stringItalic = strings.TextFormat_Italic
|
||||
self.stringMonospace = strings.TextFormat_Monospace
|
||||
self.stringLink = strings.TextFormat_Link
|
||||
self.stringStrikethrough = strings.TextFormat_Strikethrough
|
||||
self.stringUnderline = strings.TextFormat_Underline
|
||||
}
|
||||
|
||||
func activate() {
|
||||
if self.state == .inactive {
|
||||
self.state = .general
|
||||
}
|
||||
}
|
||||
|
||||
func deactivate() {
|
||||
self.state = .inactive
|
||||
}
|
||||
|
||||
func format(view: UIView, rect: CGRect) {
|
||||
if self.state == .general {
|
||||
self.state = .format
|
||||
if #available(iOS 13.0, *) {
|
||||
UIMenuController.shared.showMenu(from: view, rect: rect)
|
||||
} else {
|
||||
UIMenuController.shared.isMenuVisible = true
|
||||
UIMenuController.shared.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func back() {
|
||||
if self.state == .format {
|
||||
self.state = .general
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class CreatePollTextInputItemNode: ListViewItemNode, ASEditableTextNodeDelegate, ItemListItemNode, ItemListItemFocusableNode {
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let topStripeNode: ASDisplayNode
|
||||
|
|
@ -213,7 +125,7 @@ public class CreatePollTextInputItemNode: ListViewItemNode, ASEditableTextNodeDe
|
|||
private var item: CreatePollTextInputItem?
|
||||
private var layoutParams: ListViewItemLayoutParams?
|
||||
|
||||
private let inputMenu = ChatTextInputMenu()
|
||||
private let inputMenu = TextInputMenu()
|
||||
|
||||
public var tag: ItemListItemTag? {
|
||||
return self.item?.tag
|
||||
|
|
|
|||
|
|
@ -980,8 +980,8 @@ public final class ContactListNode: ASDisplayNode {
|
|||
}
|
||||
|
||||
var insets = layout.0.insets(options: [.input])
|
||||
insets.left += layout.0.safeInsets.left
|
||||
insets.right += layout.0.safeInsets.right
|
||||
insets.left = layout.0.safeInsets.left
|
||||
insets.right = layout.0.safeInsets.right
|
||||
|
||||
var headerInsets = layout.1
|
||||
if headerInsets.top == insets.top {
|
||||
|
|
@ -1469,8 +1469,8 @@ public final class ContactListNode: ASDisplayNode {
|
|||
self.validLayout = (layout, headerInsets)
|
||||
|
||||
var insets = layout.insets(options: [.input])
|
||||
insets.left += layout.safeInsets.left
|
||||
insets.right += layout.safeInsets.right
|
||||
insets.left = layout.safeInsets.left
|
||||
insets.right = layout.safeInsets.right
|
||||
|
||||
var headerInsets = headerInsets
|
||||
if !hadValidLayout {
|
||||
|
|
@ -1488,8 +1488,8 @@ public final class ContactListNode: ASDisplayNode {
|
|||
if let inputHeight = layout.inputHeight {
|
||||
insets.bottom -= inputHeight
|
||||
}
|
||||
insets.left += layout.safeInsets.left
|
||||
insets.right += layout.safeInsets.right
|
||||
insets.left = layout.safeInsets.left
|
||||
insets.right = layout.safeInsets.right
|
||||
|
||||
let indexNodeFrame = CGRect(origin: CGPoint(x: layout.size.width - insets.right - 20.0, y: insets.top), size: CGSize(width: 20.0, height: layout.size.height - insets.top - insets.bottom))
|
||||
transition.updateFrame(node: indexNode, frame: indexNodeFrame)
|
||||
|
|
@ -1534,8 +1534,8 @@ public final class ContactListNode: ASDisplayNode {
|
|||
self.indexSections = transition.indexSections
|
||||
|
||||
var insets = layout.insets(options: [.input])
|
||||
insets.left += layout.safeInsets.left
|
||||
insets.right += layout.safeInsets.right
|
||||
insets.left = layout.safeInsets.left
|
||||
insets.right = layout.safeInsets.right
|
||||
|
||||
if let inputHeight = layout.inputHeight {
|
||||
insets.bottom -= inputHeight
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ public enum ImpactHapticFeedbackStyle: Hashable {
|
|||
case soft
|
||||
case rigid
|
||||
case veryLight
|
||||
case click05
|
||||
case click06
|
||||
}
|
||||
|
||||
@available(iOSApplicationExtension 10.0, iOS 10.0, *)
|
||||
|
|
@ -21,7 +23,9 @@ private final class HapticFeedbackImpl {
|
|||
.heavy: UIImpactFeedbackGenerator(style: .heavy),
|
||||
.soft: UIImpactFeedbackGenerator(style: .soft),
|
||||
.rigid: UIImpactFeedbackGenerator(style: .rigid),
|
||||
.veryLight: UIImpactFeedbackGenerator()]
|
||||
.veryLight: UIImpactFeedbackGenerator(),
|
||||
.click05: UIImpactFeedbackGenerator(),
|
||||
.click06: UIImpactFeedbackGenerator()]
|
||||
} else {
|
||||
return [.light: UIImpactFeedbackGenerator(style: .light),
|
||||
.medium: UIImpactFeedbackGenerator(style: .medium),
|
||||
|
|
@ -78,8 +82,17 @@ private final class HapticFeedbackImpl {
|
|||
|
||||
func impact(_ style: ImpactHapticFeedbackStyle) {
|
||||
if let impactGenerator = self.impactGenerator[style] {
|
||||
if #available(iOSApplicationExtension 13.0, iOS 13.0, *), case .veryLight = style {
|
||||
impactGenerator.impactOccurred(intensity: 0.3)
|
||||
if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
|
||||
switch style {
|
||||
case .click05:
|
||||
impactGenerator.impactOccurred(intensity: 0.3)
|
||||
case .click06:
|
||||
impactGenerator.impactOccurred(intensity: 0.4)
|
||||
case .veryLight:
|
||||
impactGenerator.impactOccurred(intensity: 0.3)
|
||||
default:
|
||||
impactGenerator.impactOccurred()
|
||||
}
|
||||
} else {
|
||||
impactGenerator.impactOccurred()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ private func endAnimations(view: UIView) {
|
|||
}
|
||||
}
|
||||
|
||||
func viewTreeContainsFirstResponder(view: UIView) -> Bool {
|
||||
public func viewTreeContainsFirstResponder(view: UIView) -> Bool {
|
||||
if view.isFirstResponder {
|
||||
return true
|
||||
} else {
|
||||
|
|
@ -147,14 +147,14 @@ func viewTreeContainsFirstResponder(view: UIView) -> Bool {
|
|||
}
|
||||
}
|
||||
|
||||
final class KeyboardViewManager {
|
||||
public final class KeyboardViewManager {
|
||||
private let host: StatusBarHost
|
||||
|
||||
init(host: StatusBarHost) {
|
||||
self.host = host
|
||||
}
|
||||
|
||||
func dismissEditingWithoutAnimation(view: UIView) {
|
||||
public func dismissEditingWithoutAnimation(view: UIView) {
|
||||
if viewTreeContainsFirstResponder(view: view) {
|
||||
view.endEditing(true)
|
||||
if let keyboardWindow = self.host.keyboardWindow {
|
||||
|
|
@ -165,7 +165,7 @@ final class KeyboardViewManager {
|
|||
}
|
||||
}
|
||||
|
||||
func update(leftEdge: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
public func update(leftEdge: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
guard let keyboardWindow = self.host.keyboardWindow else {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import UIKit
|
|||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
|
||||
final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
public final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
private final class Child {
|
||||
let value: ViewController
|
||||
var layout: ContainerViewLayout
|
||||
|
|
@ -73,19 +73,20 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
var pending: PendingChild?
|
||||
}
|
||||
|
||||
private(set) var controllers: [ViewController] = []
|
||||
public private(set) var controllers: [ViewController] = []
|
||||
private var state: State = State(layout: nil, canBeClosed: nil, top: nil, transition: nil, pending: nil)
|
||||
|
||||
private var ignoreInputHeight: Bool = false
|
||||
|
||||
private(set) var isReady: Bool = false
|
||||
var isReadyUpdated: (() -> Void)?
|
||||
var controllerRemoved: (ViewController) -> Void
|
||||
var keyboardViewManager: KeyboardViewManager? {
|
||||
public private(set) var isReady: Bool = false
|
||||
public var isReadyUpdated: (() -> Void)?
|
||||
public var controllerRemoved: (ViewController) -> Void
|
||||
public var keyboardViewManager: KeyboardViewManager? {
|
||||
didSet {
|
||||
}
|
||||
}
|
||||
var canHaveKeyboardFocus: Bool = false {
|
||||
|
||||
public var canHaveKeyboardFocus: Bool = false {
|
||||
didSet {
|
||||
if self.canHaveKeyboardFocus != oldValue {
|
||||
if !self.canHaveKeyboardFocus {
|
||||
|
|
@ -96,14 +97,15 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
var isInFocus: Bool = false {
|
||||
public var isInFocus: Bool = false {
|
||||
didSet {
|
||||
if self.isInFocus != oldValue {
|
||||
self.inFocusUpdated(isInFocus: self.isInFocus)
|
||||
}
|
||||
}
|
||||
}
|
||||
func inFocusUpdated(isInFocus: Bool) {
|
||||
|
||||
public func inFocusUpdated(isInFocus: Bool) {
|
||||
self.state.top?.value.isInFocus = isInFocus
|
||||
}
|
||||
|
||||
|
|
@ -113,13 +115,13 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
var statusBarStyle: StatusBarStyle = .Ignore
|
||||
var statusBarStyleUpdated: ((ContainedViewLayoutTransition) -> Void)?
|
||||
|
||||
init(controllerRemoved: @escaping (ViewController) -> Void) {
|
||||
public init(controllerRemoved: @escaping (ViewController) -> Void) {
|
||||
self.controllerRemoved = controllerRemoved
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
public override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), allowedDirections: { [weak self] _ in
|
||||
|
|
@ -263,7 +265,7 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
func update(layout: ContainerViewLayout, canBeClosed: Bool, controllers: [ViewController], transition: ContainedViewLayoutTransition) {
|
||||
public func update(layout: ContainerViewLayout, canBeClosed: Bool, controllers: [ViewController], transition: ContainedViewLayoutTransition) {
|
||||
self.state.layout = layout
|
||||
self.state.canBeClosed = canBeClosed
|
||||
|
||||
|
|
@ -539,7 +541,7 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
func updateAdditionalKeyboardLeftEdgeOffset(_ offset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
public func updateAdditionalKeyboardLeftEdgeOffset(_ offset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.additionalKeyboardLeftEdgeOffset = offset
|
||||
self.syncKeyboard(leftEdge: self.currentKeyboardLeftEdge, transition: transition)
|
||||
}
|
||||
|
|
@ -562,7 +564,7 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if !self.bounds.contains(point) {
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -533,8 +533,7 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject {
|
|||
}
|
||||
navigationController.filterController(self, animated: animated)
|
||||
} else {
|
||||
self.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
assertionFailure()
|
||||
self.presentingViewController?.dismiss(animated: flag, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -612,7 +611,7 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject {
|
|||
if let navigationController = self.navigationController as? NavigationController {
|
||||
navigationController.filterController(self, animated: true)
|
||||
} else {
|
||||
self.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
self.presentingViewController?.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ public final class GameController: ViewController {
|
|||
|
||||
private let context: AccountContext
|
||||
private let url: String
|
||||
private let message: EngineMessage
|
||||
private let message: EngineMessage?
|
||||
|
||||
private var presentationData: PresentationData
|
||||
|
||||
public init(context: AccountContext, url: String, message: EngineMessage) {
|
||||
public init(context: AccountContext, url: String, message: EngineMessage?) {
|
||||
self.context = context
|
||||
self.url = url
|
||||
self.message = message
|
||||
|
|
@ -29,26 +29,30 @@ public final class GameController: ViewController {
|
|||
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationShareIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.sharePressed))
|
||||
|
||||
for media in message.media {
|
||||
if let game = media as? TelegramMediaGame {
|
||||
let titleView = GameControllerTitleView(theme: self.presentationData.theme)
|
||||
|
||||
var botPeer: EnginePeer?
|
||||
inner: for attribute in message.attributes {
|
||||
if let attribute = attribute as? InlineBotMessageAttribute, let peerId = attribute.peerId {
|
||||
botPeer = message.peers[peerId].flatMap(EnginePeer.init)
|
||||
break inner
|
||||
if let message = message {
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationShareIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.sharePressed))
|
||||
|
||||
for media in message.media {
|
||||
if let game = media as? TelegramMediaGame {
|
||||
let titleView = GameControllerTitleView(theme: self.presentationData.theme)
|
||||
|
||||
var botPeer: EnginePeer?
|
||||
inner: for attribute in message.attributes {
|
||||
if let attribute = attribute as? InlineBotMessageAttribute, let peerId = attribute.peerId {
|
||||
botPeer = message.peers[peerId].flatMap(EnginePeer.init)
|
||||
break inner
|
||||
}
|
||||
}
|
||||
if botPeer == nil {
|
||||
botPeer = message.author
|
||||
}
|
||||
|
||||
titleView.set(title: game.title, subtitle: "@\(botPeer?.addressName ?? "")")
|
||||
self.navigationItem.titleView = titleView
|
||||
}
|
||||
if botPeer == nil {
|
||||
botPeer = message.author
|
||||
}
|
||||
|
||||
titleView.set(title: game.title, subtitle: "@\(botPeer?.addressName ?? "")")
|
||||
self.navigationItem.titleView = titleView
|
||||
}
|
||||
} else {
|
||||
self.title = "App"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@ final class GameControllerNode: ViewControllerTracingNode {
|
|||
private let context: AccountContext
|
||||
var presentationData: PresentationData
|
||||
private let present: (ViewController, Any?) -> Void
|
||||
private let message: EngineMessage
|
||||
private let message: EngineMessage?
|
||||
|
||||
init(context: AccountContext, presentationData: PresentationData, url: String, present: @escaping (ViewController, Any?) -> Void, message: EngineMessage) {
|
||||
init(context: AccountContext, presentationData: PresentationData, url: String, present: @escaping (ViewController, Any?) -> Void, message: EngineMessage?) {
|
||||
self.context = context
|
||||
self.presentationData = presentationData
|
||||
self.present = present
|
||||
|
|
@ -107,18 +107,21 @@ final class GameControllerNode: ViewControllerTracingNode {
|
|||
}
|
||||
|
||||
private func shareData() -> (EnginePeer, String)? {
|
||||
guard let message = self.message else {
|
||||
return nil
|
||||
}
|
||||
var botPeer: EnginePeer?
|
||||
var gameName: String?
|
||||
for media in self.message.media {
|
||||
for media in message.media {
|
||||
if let game = media as? TelegramMediaGame {
|
||||
inner: for attribute in self.message.attributes {
|
||||
inner: for attribute in message.attributes {
|
||||
if let attribute = attribute as? InlineBotMessageAttribute, let peerId = attribute.peerId {
|
||||
botPeer = self.message.peers[peerId].flatMap(EnginePeer.init)
|
||||
botPeer = message.peers[peerId].flatMap(EnginePeer.init)
|
||||
break inner
|
||||
}
|
||||
}
|
||||
if botPeer == nil {
|
||||
botPeer = self.message.author
|
||||
botPeer = message.author
|
||||
}
|
||||
|
||||
gameName = game.name
|
||||
|
|
@ -144,8 +147,8 @@ final class GameControllerNode: ViewControllerTracingNode {
|
|||
if let (botPeer, gameName) = self.shareData(), let addressName = botPeer.addressName, !addressName.isEmpty, !gameName.isEmpty {
|
||||
if eventName == "share_score" {
|
||||
self.present(ShareController(context: self.context, subject: .fromExternal({ [weak self] peerIds, text, account, _ in
|
||||
if let strongSelf = self {
|
||||
let signals = peerIds.map { TelegramEngine(account: account).messages.forwardGameWithScore(messageId: strongSelf.message.id, to: $0, as: nil) }
|
||||
if let strongSelf = self, let message = strongSelf.message {
|
||||
let signals = peerIds.map { TelegramEngine(account: account).messages.forwardGameWithScore(messageId: message.id, to: $0, as: nil) }
|
||||
return .single(.preparing)
|
||||
|> then(
|
||||
combineLatest(signals)
|
||||
|
|
|
|||
|
|
@ -32,8 +32,6 @@ private struct StickerPackPreviewGridEntry: Comparable, Equatable, Identifiable
|
|||
func item(account: Account, interaction: StickerPackPreviewInteraction, theme: PresentationTheme) -> StickerPackPreviewGridItem {
|
||||
return StickerPackPreviewGridItem(account: account, stickerItem: self.stickerItem, interaction: interaction, theme: theme, isVerified: self.isVerified)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private struct StickerPackPreviewGridTransaction {
|
||||
|
|
|
|||
|
|
@ -761,6 +761,9 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
|
|||
animationNode = current
|
||||
} else {
|
||||
animationNode = AnimatedStickerNode()
|
||||
animationNode.started = { [weak self] in
|
||||
self?.removePlaceholder(animated: false)
|
||||
}
|
||||
strongSelf.animationNode = animationNode
|
||||
strongSelf.addSubnode(animationNode)
|
||||
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@ public class ItemListVenueItemNode: ListViewItemNode, ItemListItemNode {
|
|||
strongSelf.topStripeNode.removeFromSupernode()
|
||||
}
|
||||
if strongSelf.bottomStripeNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 0)
|
||||
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 1)
|
||||
}
|
||||
if strongSelf.maskNode.supernode != nil {
|
||||
strongSelf.maskNode.removeFromSupernode()
|
||||
|
|
|
|||
|
|
@ -1,116 +0,0 @@
|
|||
|
||||
|
||||
//
|
||||
// HPTextView.h
|
||||
//
|
||||
// Created by Hans Pinckaers on 29-06-10.
|
||||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2011 Hans Pinckaers
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class HPGrowingTextView;
|
||||
@class HPTextViewInternal;
|
||||
|
||||
extern NSString *TGMentionUidAttributeName;
|
||||
extern NSString *TGMentionBoldAttributeName;
|
||||
@class TGMessageEntity;
|
||||
|
||||
@class TGKeyCommandController;
|
||||
|
||||
@protocol HPGrowingTextViewDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
- (BOOL)growingTextViewShouldBeginEditing:(HPGrowingTextView *)growingTextView;
|
||||
- (void)growingTextViewDidBeginEditing:(HPGrowingTextView *)growingTextView;
|
||||
- (void)growingTextViewDidEndEditing:(HPGrowingTextView *)growingTextView;
|
||||
- (BOOL)growingTextViewEnabled:(HPGrowingTextView *)growingTextView;
|
||||
|
||||
- (BOOL)growingTextView:(HPGrowingTextView *)growingTextView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
|
||||
- (void)growingTextViewDidChange:(HPGrowingTextView *)growingTextView afterSetText:(bool)afterSetText afterPastingText:(bool)afterPastingText;
|
||||
|
||||
- (void)growingTextView:(HPGrowingTextView *)growingTextView willChangeHeight:(CGFloat)height duration:(NSTimeInterval)duration animationCurve:(int)animationCurve;
|
||||
|
||||
- (void)growingTextViewDidChangeSelection:(HPGrowingTextView *)growingTextView;
|
||||
- (BOOL)growingTextViewShouldReturn:(HPGrowingTextView *)growingTextView;
|
||||
|
||||
- (void)growingTextView:(HPGrowingTextView *)growingTextView didPasteImages:(NSArray *)images andText:(NSString *)text;
|
||||
- (void)growingTextView:(HPGrowingTextView *)growingTextView didPasteData:(NSData *)data;
|
||||
|
||||
- (void)growingTextView:(HPGrowingTextView *)growingTextView receivedReturnKeyCommandWithModifierFlags:(UIKeyModifierFlags)flags;
|
||||
|
||||
@end
|
||||
|
||||
@interface TGAttributedTextRange : NSObject
|
||||
|
||||
@property (nonatomic, strong, readonly) id attachment;
|
||||
|
||||
- (instancetype)initWithAttachment:(id)attachment;
|
||||
|
||||
@end
|
||||
|
||||
@interface HPGrowingTextView : UIView <UITextViewDelegate>
|
||||
|
||||
@property (nonatomic, strong) UIView *placeholderView;
|
||||
@property (nonatomic, assign) bool showPlaceholderWhenFocussed;
|
||||
|
||||
@property (nonatomic) int minNumberOfLines;
|
||||
@property (nonatomic) int maxNumberOfLines;
|
||||
@property (nonatomic) CGFloat maxHeight;
|
||||
@property (nonatomic) CGFloat minHeight;
|
||||
@property (nonatomic) BOOL animateHeightChange;
|
||||
@property (nonatomic) NSTimeInterval animationDuration;
|
||||
@property (nonatomic, strong) HPTextViewInternal *internalTextView;
|
||||
@property (nonatomic, assign) bool disableFormatting;
|
||||
|
||||
@property (nonatomic) bool oneTimeLongAnimation;
|
||||
|
||||
@property (nonatomic, weak) id<HPGrowingTextViewDelegate> delegate;
|
||||
@property (nonatomic, strong) NSString *text;
|
||||
@property (nonatomic, strong) NSAttributedString *attributedText;
|
||||
@property (nonatomic, strong) UIFont *font;
|
||||
@property (nonatomic, strong) UIColor *textColor;
|
||||
@property (nonatomic, strong) UIColor *accentColor;
|
||||
@property (nonatomic) NSTextAlignment textAlignment;
|
||||
|
||||
@property (nonatomic, readonly) bool ignoreChangeNotification;
|
||||
|
||||
@property (nonatomic, assign) bool receiveKeyCommands;
|
||||
|
||||
- (instancetype)initWithKeyCommandController:(TGKeyCommandController *)keyCommandController;
|
||||
|
||||
- (void)refreshHeight:(bool)textChanged;
|
||||
- (void)notifyHeight;
|
||||
|
||||
- (void)setText:(NSString *)newText animated:(bool)animated;
|
||||
- (void)setAttributedText:(NSAttributedString *)newText animated:(bool)animated;
|
||||
- (void)setAttributedText:(NSAttributedString *)newText keepFormatting:(bool)keepFormatting animated:(bool)animated;
|
||||
- (void)selectRange:(NSRange)range force:(bool)force;
|
||||
|
||||
- (NSString *)textWithEntities:(__autoreleasing NSArray<TGMessageEntity *> **)entities;
|
||||
|
||||
+ (void)replaceMention:(NSString *)mention inputField:(HPGrowingTextView *)inputField username:(bool)username userId:(int64_t)userId;
|
||||
+ (void)replaceHashtag:(NSString *)hashtag inputField:(HPGrowingTextView *)inputField;
|
||||
|
||||
@end
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
|
||||
|
||||
//
|
||||
// HPTextViewInternal.h
|
||||
//
|
||||
// Created by Hans Pinckaers on 29-06-10.
|
||||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2011 Hans Pinckaers
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <LegacyComponents/TGWeakDelegate.h>
|
||||
|
||||
@class TGKeyCommandController;
|
||||
|
||||
@interface HPTextViewInternal : UITextView
|
||||
|
||||
@property (nonatomic) bool isPasting;
|
||||
|
||||
@property (nonatomic) bool freezeContentOffset;
|
||||
@property (nonatomic) bool disableContentOffsetAnimation;
|
||||
|
||||
@property (nonatomic, strong) TGWeakDelegate *responderStateDelegate;
|
||||
|
||||
@property (nonatomic) bool enableFirstResponder;
|
||||
|
||||
- (instancetype)initWithKeyCommandController:(TGKeyCommandController *)keyCommandController;
|
||||
|
||||
- (void)textViewEnsureSelectionVisible;
|
||||
|
||||
@end
|
||||
|
||||
@protocol HPTextViewInternalDelegate <NSObject>
|
||||
|
||||
@required
|
||||
|
||||
- (void)hpTextViewChangedResponderState:(bool)firstResponder;
|
||||
|
||||
@end
|
||||
|
|
@ -9,8 +9,6 @@
|
|||
#import <LegacyComponents/ActionStage.h>
|
||||
#import <LegacyComponents/Freedom.h>
|
||||
#import <LegacyComponents/FreedomUIKit.h>
|
||||
#import <LegacyComponents/HPGrowingTextView.h>
|
||||
#import <LegacyComponents/HPTextViewInternal.h>
|
||||
#import <LegacyComponents/JNWSpringAnimation.h>
|
||||
#import <LegacyComponents/LegacyComponents.h>
|
||||
#import <LegacyComponents/LegacyComponentsAccessChecker.h>
|
||||
|
|
@ -187,11 +185,7 @@
|
|||
#import <LegacyComponents/TGModernBackToolbarButton.h>
|
||||
#import <LegacyComponents/TGModernButton.h>
|
||||
#import <LegacyComponents/TGModernCache.h>
|
||||
#import <LegacyComponents/TGModernConversationAlphacodeAssociatedPanel.h>
|
||||
#import <LegacyComponents/TGModernConversationAssociatedInputPanel.h>
|
||||
#import <LegacyComponents/TGModernConversationHashtagsAssociatedPanel.h>
|
||||
#import <LegacyComponents/TGModernConversationInputMicButton.h>
|
||||
#import <LegacyComponents/TGModernConversationMentionsAssociatedPanel.h>
|
||||
#import <LegacyComponents/TGModernConversationTitleActivityIndicator.h>
|
||||
#import <LegacyComponents/TGModernGalleryContainerView.h>
|
||||
#import <LegacyComponents/TGModernGalleryController.h>
|
||||
|
|
|
|||
|
|
@ -90,8 +90,12 @@ typedef enum
|
|||
|
||||
@property (nonatomic, copy) void (^selectionLimitExceeded)(void);
|
||||
|
||||
- (UIBarButtonItem *)leftBarButtonItem;
|
||||
- (UIBarButtonItem *)rightBarButtonItem;
|
||||
|
||||
- (void)send:(bool)silently;
|
||||
- (void)schedule;
|
||||
|
||||
- (NSArray *)resultSignalsWithCurrentItem:(TGMediaAsset *)currentItem descriptionGenerator:(id (^)(id, NSAttributedString *, NSString *, NSString *))descriptionGenerator;
|
||||
|
||||
- (void)completeWithAvatarImage:(UIImage *)image;
|
||||
|
|
|
|||
|
|
@ -68,7 +68,9 @@
|
|||
- (void)setCaption:(NSAttributedString *)caption forItem:(NSObject<TGMediaEditableItem> *)item;
|
||||
|
||||
- (bool)isForcedCaption;
|
||||
- (SSignal *)forcedCaption;
|
||||
- (void)setForcedCaption:(NSAttributedString *)caption;
|
||||
- (void)setForcedCaption:(NSAttributedString *)caption skipUpdate:(bool)skipUpdate;
|
||||
|
||||
- (NSObject<TGMediaEditAdjustments> *)adjustmentsForItem:(NSObject<TGMediaEditableItem> *)item;
|
||||
- (SSignal *)adjustmentsSignalForItem:(NSObject<TGMediaEditableItem> *)item;
|
||||
|
|
|
|||
|
|
@ -1,57 +0,0 @@
|
|||
#import <LegacyComponents/HPGrowingTextView.h>
|
||||
|
||||
@class TGModernConversationAssociatedInputPanel;
|
||||
@class TGKeyCommandController;
|
||||
|
||||
@protocol TGMediaPickerCaptionInputPanelDelegate;
|
||||
|
||||
@interface TGMediaPickerCaptionInputPanel : UIView
|
||||
|
||||
- (instancetype)initWithKeyCommandController:(TGKeyCommandController *)keyCommandController frame:(CGRect)frame;
|
||||
|
||||
@property (nonatomic, weak) id<TGMediaPickerCaptionInputPanelDelegate> delegate;
|
||||
|
||||
@property (nonatomic, strong) NSString *caption;
|
||||
- (void)setCaption:(NSString *)caption entities:(NSArray *)entities animated:(bool)animated;
|
||||
|
||||
@property (nonatomic, readonly) HPGrowingTextView *inputField;
|
||||
@property (nonatomic, assign) bool allowEntities;
|
||||
|
||||
@property (nonatomic, assign) CGFloat bottomMargin;
|
||||
@property (nonatomic, assign, getter=isCollapsed) bool collapsed;
|
||||
- (void)setCollapsed:(bool)collapsed animated:(bool)animated;
|
||||
|
||||
- (void)replaceMention:(NSString *)mention;
|
||||
- (void)replaceMention:(NSString *)mention username:(bool)username userId:(int64_t)userId;
|
||||
- (void)replaceHashtag:(NSString *)hashtag;
|
||||
|
||||
- (void)adjustForOrientation:(UIInterfaceOrientation)orientation keyboardHeight:(CGFloat)keyboardHeight duration:(NSTimeInterval)duration animationCurve:(NSInteger)animationCurve;
|
||||
|
||||
- (void)dismiss;
|
||||
|
||||
- (CGFloat)heightForInputFieldHeight:(CGFloat)inputFieldHeight;
|
||||
- (CGFloat)baseHeight;
|
||||
|
||||
- (void)setAssociatedPanel:(TGModernConversationAssociatedInputPanel *)associatedPanel animated:(bool)animated;
|
||||
- (TGModernConversationAssociatedInputPanel *)associatedPanel;
|
||||
|
||||
- (void)setContentAreaHeight:(CGFloat)contentAreaHeight;
|
||||
|
||||
- (NSInteger)textCaretPosition;
|
||||
|
||||
@end
|
||||
|
||||
@protocol TGMediaPickerCaptionInputPanelDelegate <NSObject>
|
||||
|
||||
- (bool)inputPanelShouldBecomeFirstResponder:(TGMediaPickerCaptionInputPanel *)inputPanel;
|
||||
- (void)inputPanelFocused:(TGMediaPickerCaptionInputPanel *)inputPanel;
|
||||
- (void)inputPanelRequestedSetCaption:(TGMediaPickerCaptionInputPanel *)inputPanel text:(NSString *)text entities:(NSArray *)entities;
|
||||
- (void)inputPanelMentionEntered:(TGMediaPickerCaptionInputPanel *)inputTextPanel mention:(NSString *)mention startOfLine:(bool)startOfLine;
|
||||
- (void)inputPanelHashtagEntered:(TGMediaPickerCaptionInputPanel *)inputTextPanel hashtag:(NSString *)hashtag;
|
||||
- (void)inputPanelAlphacodeEntered:(TGMediaPickerCaptionInputPanel *)inputTextPanel alphacode:(NSString *)alphacode;
|
||||
- (void)inputPanelWillChangeHeight:(TGMediaPickerCaptionInputPanel *)inputPanel height:(CGFloat)height duration:(NSTimeInterval)duration animationCurve:(int)animationCurve;
|
||||
|
||||
@optional
|
||||
- (void)inputPanelTextChanged:(TGMediaPickerCaptionInputPanel *)inputTextPanel text:(NSString *)text;
|
||||
|
||||
@end
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
#import "TGModernConversationAssociatedInputPanel.h"
|
||||
|
||||
#import <SSignalKit/SSignalKit.h>
|
||||
|
||||
@class TGAlphacodeEntry;
|
||||
|
||||
@interface TGModernConversationAlphacodeAssociatedPanel : TGModernConversationAssociatedInputPanel
|
||||
|
||||
@property (nonatomic, copy) void (^alphacodeSelected)(TGAlphacodeEntry *);
|
||||
|
||||
- (void)setAlphacodeListSignal:(SSignal *)alphacodeListSignal;
|
||||
|
||||
@end
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TGModernConversationAssociatedInputPanelDefaultStyle,
|
||||
TGModernConversationAssociatedInputPanelDarkStyle,
|
||||
TGModernConversationAssociatedInputPanelDarkBlurredStyle
|
||||
} TGModernConversationAssociatedInputPanelStyle;
|
||||
|
||||
@interface TGConversationAssociatedInputPanelPallete : NSObject
|
||||
|
||||
@property (nonatomic, readonly) bool isDark;
|
||||
@property (nonatomic, readonly) UIColor *backgroundColor;
|
||||
@property (nonatomic, readonly) UIColor *separatorColor;
|
||||
@property (nonatomic, readonly) UIColor *selectionColor;
|
||||
@property (nonatomic, readonly) UIColor *barBackgroundColor;
|
||||
@property (nonatomic, readonly) UIColor *barSeparatorColor;
|
||||
@property (nonatomic, readonly) UIColor *textColor;
|
||||
@property (nonatomic, readonly) UIColor *secondaryTextColor;
|
||||
@property (nonatomic, readonly) UIColor *accentColor;
|
||||
@property (nonatomic, readonly) UIColor *placeholderBackgroundColor;
|
||||
@property (nonatomic, readonly) UIColor *placeholderIconColor;
|
||||
@property (nonatomic, readonly) UIImage *avatarPlaceholder;
|
||||
@property (nonatomic, readonly) UIImage *closeIcon;
|
||||
@property (nonatomic, readonly) UIImage *largeCloseIcon;
|
||||
|
||||
+ (instancetype)palleteWithDark:(bool)dark backgroundColor:(UIColor *)backgroundColor separatorColor:(UIColor *)separatorColor selectionColor:(UIColor *)selectionColor barBackgroundColor:(UIColor *)barBackgroundColor barSeparatorColor:(UIColor *)barSeparatorColor textColor:(UIColor *)textColor secondaryTextColor:(UIColor *)secondaryTextColor accentColor:(UIColor *)accentColor placeholderBackgroundColor:(UIColor *)placeholderBackgroundColor placeholderIconColor:(UIColor *)placeholderIconColor avatarPlaceholder:(UIImage *)avatarPlaceholder closeIcon:(UIImage *)closeIcon largeCloseIcon:(UIImage *)largeCloseIcon;
|
||||
|
||||
@end
|
||||
|
||||
@interface TGModernConversationAssociatedInputPanel : UIView
|
||||
{
|
||||
UIEdgeInsets _safeAreaInset;
|
||||
}
|
||||
|
||||
@property (nonatomic, readonly) TGModernConversationAssociatedInputPanelStyle style;
|
||||
@property (nonatomic, copy) void (^preferredHeightUpdated)();
|
||||
|
||||
@property (nonatomic, copy) void (^resultPreviewAppeared)(void);
|
||||
@property (nonatomic, copy) void (^resultPreviewDisappeared)(bool restoreFocus);
|
||||
|
||||
@property (nonatomic, strong) TGConversationAssociatedInputPanelPallete *pallete;
|
||||
@property (nonatomic) UIEdgeInsets safeAreaInset;
|
||||
@property (nonatomic) CGFloat overlayBarOffset;
|
||||
@property (nonatomic) CGFloat barInset;
|
||||
@property (nonatomic, copy) void (^updateOverlayBarOffset)(CGFloat);
|
||||
|
||||
- (CGFloat)preferredHeight;
|
||||
- (bool)displayForTextEntryOnly;
|
||||
- (bool)fillsAvailableSpace;
|
||||
- (void)setNeedsPreferredHeightUpdate;
|
||||
|
||||
- (void)setSendAreaWidth:(CGFloat)sendAreaWidth attachmentAreaWidth:(CGFloat)attachmentAreaWidth;
|
||||
- (void)setContentAreaHeight:(CGFloat)contentAreaHeight;
|
||||
|
||||
- (instancetype)initWithStyle:(TGModernConversationAssociatedInputPanelStyle)style;
|
||||
|
||||
- (bool)hasSelectedItem;
|
||||
- (void)selectPreviousItem;
|
||||
- (void)selectNextItem;
|
||||
- (void)commitSelectedItem;
|
||||
|
||||
- (void)animateIn;
|
||||
- (void)animateOut:(void (^)())completion;
|
||||
|
||||
- (void)setBarInset:(CGFloat)barInset animated:(bool)animated;
|
||||
|
||||
@end
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
#import <LegacyComponents/TGModernConversationAssociatedInputPanel.h>
|
||||
|
||||
#import <SSignalKit/SSignalKit.h>
|
||||
|
||||
@interface TGModernConversationHashtagsAssociatedPanel : TGModernConversationAssociatedInputPanel
|
||||
|
||||
@property (nonatomic, copy) void (^hashtagSelected)(NSString *);
|
||||
|
||||
- (void)setHashtagListSignal:(SSignal *)hashtagListSignal;
|
||||
|
||||
@end
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
#import <LegacyComponents/TGModernConversationAssociatedInputPanel.h>
|
||||
|
||||
#import <SSignalKit/SSignalKit.h>
|
||||
|
||||
@class TGUser;
|
||||
|
||||
@interface TGModernConversationMentionsAssociatedPanel : TGModernConversationAssociatedInputPanel
|
||||
|
||||
@property (nonatomic) bool inverted;
|
||||
|
||||
@property (nonatomic, copy) void (^userSelected)(TGUser *);
|
||||
|
||||
- (void)setUserListSignal:(SSignal *)userListSignal;
|
||||
|
||||
@end
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import <LegacyComponents/TGMediaPickerCaptionInputPanel.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class TGSuggestionContext;
|
||||
@protocol TGPhotoPaintStickersContext;
|
||||
|
|
|
|||
|
|
@ -1,912 +0,0 @@
|
|||
#import "HPGrowingTextView.h"
|
||||
|
||||
#import "LegacyComponentsInternal.h"
|
||||
#import "TGFont.h"
|
||||
|
||||
#import "HPTextViewInternal.h"
|
||||
|
||||
#import "TGInputTextTag.h"
|
||||
|
||||
#import "TGMessage.h"
|
||||
|
||||
#import "TGColor.h"
|
||||
|
||||
NSString *TGMentionUidAttributeName = @"TGMentionUidAttributeName";
|
||||
NSString *TGMentionBoldAttributeName = @"TGMentionBoldAttributeName";
|
||||
|
||||
@implementation TGAttributedTextRange
|
||||
|
||||
- (instancetype)initWithAttachment:(id)attachment {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_attachment = attachment;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface HPGrowingTextView ()
|
||||
{
|
||||
UIColor *_intrinsicTextColor;
|
||||
UIFont *_intrinsicTextFont;
|
||||
|
||||
__weak TGKeyCommandController *_keyCommandController;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation HPGrowingTextView
|
||||
|
||||
- (instancetype)initWithKeyCommandController:(TGKeyCommandController *)keyCommandController
|
||||
{
|
||||
self = [super initWithFrame:CGRectZero];
|
||||
if (self != nil)
|
||||
{
|
||||
_keyCommandController = keyCommandController;
|
||||
|
||||
[self commonInitialiser];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSDictionary *)defaultAttributes {
|
||||
if (_intrinsicTextFont == nil) {
|
||||
return @{NSFontAttributeName: TGSystemFontOfSize(17)};
|
||||
} else {
|
||||
if (_intrinsicTextColor)
|
||||
return @{NSFontAttributeName: _intrinsicTextFont, NSForegroundColorAttributeName: _intrinsicTextColor};
|
||||
else
|
||||
return @{NSFontAttributeName: _intrinsicTextFont};
|
||||
}
|
||||
}
|
||||
|
||||
- (void)commonInitialiser
|
||||
{
|
||||
CGRect frame = self.frame;
|
||||
frame.origin = CGPointZero;
|
||||
_internalTextView = [[HPTextViewInternal alloc] initWithKeyCommandController:_keyCommandController];
|
||||
_internalTextView.frame = frame;
|
||||
_internalTextView.delegate = self;
|
||||
_internalTextView.contentInset = UIEdgeInsetsZero;
|
||||
_internalTextView.showsHorizontalScrollIndicator = NO;
|
||||
_internalTextView.attributedText = [[NSAttributedString alloc] initWithString:@"-" attributes:[self defaultAttributes]];
|
||||
_internalTextView.scrollsToTop = false;
|
||||
if (iosMajorVersion() >= 7) {
|
||||
_internalTextView.textContainer.layoutManager.allowsNonContiguousLayout = true;
|
||||
_internalTextView.allowsEditingTextAttributes = true;
|
||||
}
|
||||
[self addSubview:_internalTextView];
|
||||
|
||||
_minHeight = _internalTextView.frame.size.height;
|
||||
_minNumberOfLines = 1;
|
||||
|
||||
_animateHeightChange = true;
|
||||
_animationDuration = 0.1f;
|
||||
|
||||
_internalTextView.attributedText = [[NSAttributedString alloc] initWithString:@"" attributes:[self defaultAttributes]];
|
||||
}
|
||||
|
||||
- (void)setDisableFormatting:(bool)disableFormatting
|
||||
{
|
||||
_disableFormatting = disableFormatting;
|
||||
if (iosMajorVersion() >= 7)
|
||||
_internalTextView.allowsEditingTextAttributes = !disableFormatting;
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)frame
|
||||
{
|
||||
[super setFrame:frame];
|
||||
|
||||
frame.origin = CGPointZero;
|
||||
_internalTextView.frame = frame;
|
||||
}
|
||||
|
||||
- (CGSize)sizeThatFits:(CGSize)size
|
||||
{
|
||||
if (self.attributedText.length == 0)
|
||||
size.height = _minHeight;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
- (void)setMaxNumberOfLines:(int)maxNumberOfLines
|
||||
{
|
||||
if (maxNumberOfLines == 0 && _maxHeight > 0) // the user specified a maxHeight themselves.
|
||||
return;
|
||||
|
||||
// Use internalTextView for height calculations, thanks to Gwynne <http://blog.darkrainfall.org/>
|
||||
NSAttributedString *saveText = _internalTextView.attributedText;
|
||||
NSMutableAttributedString *newText = [[NSMutableAttributedString alloc] initWithString:@"-" attributes:[self defaultAttributes]];
|
||||
|
||||
_internalTextView.delegate = nil;
|
||||
_internalTextView.hidden = YES;
|
||||
|
||||
for (int i = 1; i < maxNumberOfLines; ++i) {
|
||||
[newText appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n|W|"]];
|
||||
}
|
||||
|
||||
_internalTextView.attributedText = newText;
|
||||
|
||||
_maxHeight = [self measureHeight];
|
||||
|
||||
_internalTextView.attributedText = saveText;
|
||||
_internalTextView.hidden = NO;
|
||||
_internalTextView.delegate = self;
|
||||
|
||||
[self sizeToFit];
|
||||
|
||||
_maxNumberOfLines = maxNumberOfLines;
|
||||
}
|
||||
|
||||
- (void)setMaxHeight:(CGFloat)maxHeight
|
||||
{
|
||||
_maxHeight = maxHeight;
|
||||
_maxNumberOfLines = 0;
|
||||
}
|
||||
|
||||
- (void)setMinNumberOfLines:(int)minNumberOfLines
|
||||
{
|
||||
if (minNumberOfLines == 0 && _minHeight > 0) // the user specified a minHeight themselves.
|
||||
return;
|
||||
|
||||
// Use internalTextView for height calculations, thanks to Gwynne <http://blog.darkrainfall.org/>
|
||||
NSAttributedString *saveText = _internalTextView.attributedText;
|
||||
NSMutableAttributedString *newText = [[NSMutableAttributedString alloc] initWithString:@"-" attributes:[self defaultAttributes]];
|
||||
|
||||
_internalTextView.delegate = nil;
|
||||
_internalTextView.hidden = YES;
|
||||
|
||||
for (int i = 1; i < minNumberOfLines; ++i) {
|
||||
[newText appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n|W|"]];
|
||||
}
|
||||
|
||||
_internalTextView.attributedText = newText;
|
||||
|
||||
_minHeight = [self measureHeight];
|
||||
|
||||
_internalTextView.attributedText = saveText;
|
||||
_internalTextView.hidden = NO;
|
||||
_internalTextView.delegate = self;
|
||||
|
||||
[self sizeToFit];
|
||||
|
||||
_minNumberOfLines = minNumberOfLines;
|
||||
}
|
||||
|
||||
- (void)setMinHeight:(CGFloat)minHeight
|
||||
{
|
||||
_minHeight = minHeight;
|
||||
_minNumberOfLines = 0;
|
||||
}
|
||||
|
||||
- (void)textViewDidChange:(UITextView *)__unused textView
|
||||
{
|
||||
[self refreshAttributes];
|
||||
|
||||
[self refreshHeight:true];
|
||||
if (self.showPlaceholderWhenFocussed)
|
||||
_placeholderView.hidden = [_internalTextView hasText];
|
||||
}
|
||||
|
||||
- (void)textViewDidChangeSelection:(UITextView *)__unused textView
|
||||
{
|
||||
id<HPGrowingTextViewDelegate> delegate = _delegate;
|
||||
|
||||
if ([delegate respondsToSelector:@selector(growingTextViewDidChangeSelection:)])
|
||||
[delegate growingTextViewDidChangeSelection:self];
|
||||
|
||||
//_internalTextView.typingAttributes = [self defaultAttributes];
|
||||
|
||||
if ([_internalTextView selectedRange].length == 0) {
|
||||
//[self refreshAttributes];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)refreshHeight:(bool)textChanged
|
||||
{
|
||||
CGFloat newSizeH = [self measureHeight]; //size of content, so we can set the frame of self
|
||||
|
||||
if(newSizeH < _minHeight || !_internalTextView.hasText)
|
||||
newSizeH = _minHeight; //not smalles than minHeight
|
||||
|
||||
if (_internalTextView.frame.size.height > _maxHeight)
|
||||
newSizeH = _maxHeight; // not taller than maxHeight
|
||||
|
||||
id<HPGrowingTextViewDelegate> delegate = _delegate;
|
||||
|
||||
if (ABS(_internalTextView.frame.size.height - newSizeH) > FLT_EPSILON || _oneTimeLongAnimation)
|
||||
{
|
||||
// [fixed] Pasting too much text into the view failed to fire the height change,
|
||||
// thanks to Gwynne <http://blog.darkrainfall.org/>
|
||||
|
||||
if (newSizeH > _maxHeight && _internalTextView.frame.size.height <= _maxHeight)
|
||||
newSizeH = _maxHeight;
|
||||
|
||||
if (newSizeH <= _maxHeight)
|
||||
{
|
||||
if (_animateHeightChange && !_internalTextView.isPasting)
|
||||
{
|
||||
NSTimeInterval currentAnimationDuration = 0.12;
|
||||
if (_oneTimeLongAnimation)
|
||||
{
|
||||
_oneTimeLongAnimation = false;
|
||||
currentAnimationDuration = 0.3;
|
||||
if (iosMajorVersion() < 7)
|
||||
currentAnimationDuration *= 0.7;
|
||||
}
|
||||
|
||||
[UIView animateWithDuration:currentAnimationDuration delay:0 options:(UIViewAnimationOptionAllowUserInteraction| UIViewAnimationOptionBeginFromCurrentState) animations:^
|
||||
{
|
||||
[self resizeTextView:newSizeH];
|
||||
} completion:nil];
|
||||
|
||||
if ([delegate respondsToSelector:@selector(growingTextView:willChangeHeight:duration:animationCurve:)])
|
||||
[delegate growingTextView:self willChangeHeight:newSizeH duration:currentAnimationDuration animationCurve:0];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self resizeTextView:newSizeH];
|
||||
|
||||
if ([delegate respondsToSelector:@selector(growingTextView:willChangeHeight:duration:animationCurve:)])
|
||||
[delegate growingTextView:self willChangeHeight:newSizeH duration:0.0 animationCurve:0];
|
||||
}
|
||||
}
|
||||
|
||||
// scroll to caret (needed on iOS7)
|
||||
if (iosMajorVersion() >= 7)
|
||||
{
|
||||
/*NSRange range = _internalTextView.selectedRange;
|
||||
[_internalTextView _scrollRangeToVisible:range animated:false];
|
||||
|
||||
CGRect r = [_internalTextView caretRectForPosition:_internalTextView.selectedTextRange.end];
|
||||
CGFloat frameHeight = _internalTextView.frame.size.height;
|
||||
CGFloat caretY = MAX(r.origin.y - frameHeight + r.size.height + 8, 0);
|
||||
if (r.origin.y != INFINITY)
|
||||
{
|
||||
CGPoint contentOffset = _internalTextView.contentOffset;
|
||||
contentOffset.y = caretY;
|
||||
_internalTextView.contentOffset = contentOffset;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
if (textChanged && [delegate respondsToSelector:@selector(growingTextViewDidChange:afterSetText:afterPastingText:)]) {
|
||||
[delegate growingTextViewDidChange:self afterSetText:_ignoreChangeNotification afterPastingText:_internalTextView.isPasting];
|
||||
}
|
||||
|
||||
_oneTimeLongAnimation = false;
|
||||
}
|
||||
|
||||
- (void)notifyHeight {
|
||||
id<HPGrowingTextViewDelegate> delegate = _delegate;
|
||||
if ([delegate respondsToSelector:@selector(growingTextView:willChangeHeight:duration:animationCurve:)])
|
||||
[delegate growingTextView:self willChangeHeight:_internalTextView.frame.size.height duration:0.0 animationCurve:0];
|
||||
}
|
||||
|
||||
// Code from apple developer forum - @Steve Krulewitz, @Mark Marszal, @Eric Silverberg
|
||||
- (CGFloat)measureHeight
|
||||
{
|
||||
if (iosMajorVersion() >= 7)
|
||||
{
|
||||
CGRect frame = _internalTextView.bounds;
|
||||
CGSize fudgeFactor = CGSizeMake(10.0, 17.0);
|
||||
|
||||
frame.size.height -= fudgeFactor.height;
|
||||
frame.size.width -= fudgeFactor.width;
|
||||
|
||||
frame.size.width -= _internalTextView.textContainerInset.right;
|
||||
|
||||
NSMutableAttributedString *textToMeasure = [[NSMutableAttributedString alloc] initWithAttributedString:_internalTextView.attributedText];
|
||||
if ([textToMeasure.string hasSuffix:@"\n"])
|
||||
{
|
||||
[textToMeasure appendAttributedString:[[NSAttributedString alloc] initWithString:@"-"]];
|
||||
}
|
||||
[textToMeasure removeAttribute:NSFontAttributeName range:NSMakeRange(0, textToMeasure.length)];
|
||||
if (_intrinsicTextFont != nil) {
|
||||
[textToMeasure addAttribute:NSFontAttributeName value:_intrinsicTextFont range:NSMakeRange(0, textToMeasure.length)];
|
||||
}
|
||||
|
||||
// NSString class method: boundingRectWithSize:options:attributes:context is
|
||||
// available only on ios7.0 sdk.
|
||||
CGRect size = [textToMeasure boundingRectWithSize:CGSizeMake(CGRectGetWidth(frame), MAXFLOAT)
|
||||
options:NSStringDrawingUsesLineFragmentOrigin
|
||||
//attributes:attributes
|
||||
context:nil];
|
||||
|
||||
return CGFloor(CGRectGetHeight(size) + fudgeFactor.height);
|
||||
}
|
||||
else
|
||||
{
|
||||
return CGFloor(self.internalTextView.contentSize.height);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resizeTextView:(CGFloat)newSizeH
|
||||
{
|
||||
CGRect internalTextViewFrame = self.frame;
|
||||
internalTextViewFrame.size.height = CGFloor(newSizeH);
|
||||
self.frame = internalTextViewFrame;
|
||||
|
||||
internalTextViewFrame.origin = CGPointZero;
|
||||
if(!CGRectEqualToRect(_internalTextView.frame, internalTextViewFrame))
|
||||
_internalTextView.frame = internalTextViewFrame;
|
||||
|
||||
//[_internalTextView textViewEnsureSelectionVisible];
|
||||
}
|
||||
|
||||
- (BOOL)becomeFirstResponder
|
||||
{
|
||||
return [_internalTextView becomeFirstResponder];
|
||||
}
|
||||
|
||||
- (BOOL)resignFirstResponder
|
||||
{
|
||||
return [_internalTextView resignFirstResponder];
|
||||
}
|
||||
|
||||
- (BOOL)isFirstResponder
|
||||
{
|
||||
return [_internalTextView isFirstResponder];
|
||||
}
|
||||
|
||||
- (BOOL)canBecomeFirstResponder
|
||||
{
|
||||
return [_internalTextView canBecomeFirstResponder];
|
||||
}
|
||||
|
||||
- (void)setText:(NSString *)newText
|
||||
{
|
||||
[self setText:newText animated:true];
|
||||
}
|
||||
|
||||
- (void)setAttributedText:(NSAttributedString *)attributedText {
|
||||
[self setAttributedText:attributedText animated:true];
|
||||
}
|
||||
|
||||
- (NSAttributedString *)attributedText {
|
||||
return _internalTextView.attributedText;
|
||||
}
|
||||
|
||||
- (void)setText:(NSString *)newText animated:(bool)animated {
|
||||
[self setAttributedText:[[NSAttributedString alloc] initWithString:newText == nil ? @"" : newText attributes:[self defaultAttributes]] animated:animated];
|
||||
}
|
||||
|
||||
- (void)setAttributedText:(NSAttributedString *)newText animated:(bool)animated {
|
||||
[self setAttributedText:newText keepFormatting:false animated:animated];
|
||||
}
|
||||
|
||||
- (void)setAttributedText:(NSAttributedString *)newText keepFormatting:(bool)keepFormatting animated:(bool)animated {
|
||||
NSMutableAttributedString *fixedFontString = [[NSMutableAttributedString alloc] initWithAttributedString:newText];
|
||||
if (!keepFormatting)
|
||||
{
|
||||
[fixedFontString removeAttribute:NSFontAttributeName range:NSMakeRange(0, fixedFontString.length)];
|
||||
[fixedFontString addAttribute:NSFontAttributeName value:_intrinsicTextFont == nil ? [UIFont systemFontOfSize:17.0f] : _intrinsicTextFont range:NSMakeRange(0, fixedFontString.length)];
|
||||
}
|
||||
|
||||
_internalTextView.attributedText = fixedFontString;
|
||||
_internalTextView.typingAttributes = [self defaultAttributes];
|
||||
[self refreshAttributes];
|
||||
|
||||
_placeholderView.hidden = fixedFontString.length != 0 || [_internalTextView isFirstResponder];
|
||||
|
||||
// include this line to analyze the height of the textview.
|
||||
// fix from Ankit Thakur
|
||||
|
||||
bool previousAnimateHeightChange = _animateHeightChange;
|
||||
_animateHeightChange = animated;
|
||||
_ignoreChangeNotification = true;
|
||||
[self performSelector:@selector(textViewDidChange:) withObject:_internalTextView];
|
||||
_ignoreChangeNotification = false;
|
||||
_animateHeightChange = previousAnimateHeightChange;
|
||||
}
|
||||
|
||||
- (void)selectRange:(NSRange)range force:(bool)force {
|
||||
if (range.length != 0 || force) {
|
||||
UITextPosition *startPosition = [_internalTextView positionFromPosition:_internalTextView.beginningOfDocument offset:range.location];
|
||||
UITextPosition *endPosition = [_internalTextView positionFromPosition:_internalTextView.beginningOfDocument offset:range.location + range.length];
|
||||
UITextRange *selection = [_internalTextView textRangeFromPosition:startPosition toPosition:endPosition];
|
||||
_internalTextView.selectedTextRange = selection;
|
||||
}
|
||||
}
|
||||
|
||||
-(NSString *)text
|
||||
{
|
||||
return _internalTextView.text;
|
||||
}
|
||||
|
||||
- (void)setFont:(UIFont *)afont
|
||||
{
|
||||
_internalTextView.font = afont;
|
||||
_intrinsicTextFont = afont;
|
||||
|
||||
[self setMaxNumberOfLines:_maxNumberOfLines];
|
||||
[self setMinNumberOfLines:_minNumberOfLines];
|
||||
}
|
||||
|
||||
- (UIFont *)font
|
||||
{
|
||||
return _intrinsicTextFont;
|
||||
}
|
||||
|
||||
- (void)setTextColor:(UIColor *)color
|
||||
{
|
||||
_internalTextView.textColor = color;
|
||||
_intrinsicTextColor = color;
|
||||
}
|
||||
|
||||
- (UIColor *)textColor
|
||||
{
|
||||
return _internalTextView.textColor;
|
||||
}
|
||||
|
||||
- (UIColor *)accentColor
|
||||
{
|
||||
if (_accentColor != nil)
|
||||
return _accentColor;
|
||||
|
||||
return TGAccentColor();
|
||||
}
|
||||
|
||||
- (void)setTextAlignment:(NSTextAlignment)aligment
|
||||
{
|
||||
_internalTextView.textAlignment = aligment;
|
||||
}
|
||||
|
||||
- (NSTextAlignment)textAlignment
|
||||
{
|
||||
return _internalTextView.textAlignment;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (BOOL)textViewShouldBeginEditing:(UITextView *)__unused textView
|
||||
{
|
||||
id<HPGrowingTextViewDelegate> delegate = _delegate;
|
||||
|
||||
if ([delegate respondsToSelector:@selector(growingTextViewShouldBeginEditing:)])
|
||||
return [delegate growingTextViewShouldBeginEditing:self];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
- (void)textViewDidBeginEditing:(UITextView *)__unused textView
|
||||
{
|
||||
id<HPGrowingTextViewDelegate> delegate = _delegate;
|
||||
|
||||
if ([delegate respondsToSelector:@selector(growingTextViewDidBeginEditing:)])
|
||||
[delegate growingTextViewDidBeginEditing:self];
|
||||
|
||||
if (!self.showPlaceholderWhenFocussed && ![_internalTextView hasText])
|
||||
_placeholderView.hidden = true;
|
||||
}
|
||||
|
||||
- (void)textViewDidEndEditing:(UITextView *)__unused textView
|
||||
{
|
||||
id<HPGrowingTextViewDelegate> delegate = _delegate;
|
||||
|
||||
if ([delegate respondsToSelector:@selector(growingTextViewDidEndEditing:)])
|
||||
[delegate growingTextViewDidEndEditing:self];
|
||||
|
||||
if (!self.showPlaceholderWhenFocussed)
|
||||
_placeholderView.hidden = [_internalTextView hasText];
|
||||
}
|
||||
|
||||
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)atext
|
||||
{
|
||||
id<HPGrowingTextViewDelegate> delegate = _delegate;
|
||||
|
||||
if ([delegate respondsToSelector:@selector(growingTextViewEnabled:)]) {
|
||||
if (![delegate growingTextViewEnabled:self]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (![textView hasText] && [atext isEqualToString:@""])
|
||||
return NO;
|
||||
|
||||
if ([atext isEqualToString:@"\n"])
|
||||
{
|
||||
id<HPGrowingTextViewDelegate> delegate = _delegate;
|
||||
|
||||
if ([delegate respondsToSelector:@selector(growingTextViewShouldReturn:)])
|
||||
return (BOOL)[delegate performSelector:@selector(growingTextViewShouldReturn:) withObject:self];
|
||||
}
|
||||
|
||||
if (atext.length == 0) {
|
||||
if (range.location != 0 && range.length == 1) {
|
||||
NSAttributedString *string = self.attributedText;
|
||||
if ([string attributesAtIndex:range.location effectiveRange:nil][NSAttachmentAttributeName] != nil) {
|
||||
NSMutableAttributedString *mutableString = [[NSMutableAttributedString alloc] initWithAttributedString:string];
|
||||
[mutableString replaceCharactersInRange:NSMakeRange(range.location - 1, 1) withString:@""];
|
||||
NSRange badRange = [mutableString.string rangeOfString:@"\uFFFC"];
|
||||
if (badRange.location != NSNotFound)
|
||||
[mutableString replaceCharactersInRange:badRange withString:@""];
|
||||
self.attributedText = mutableString;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (range.length == 0) {
|
||||
NSAttributedString *string = self.attributedText;
|
||||
if (range.location != 0 && [string attributesAtIndex:range.location - 1 effectiveRange:nil][NSAttachmentAttributeName] != nil) {
|
||||
NSMutableAttributedString *mutableString = [[NSMutableAttributedString alloc] initWithAttributedString:string];
|
||||
[mutableString replaceCharactersInRange:NSMakeRange(range.location - 1, 0) withString:atext];
|
||||
NSRange badRange = [mutableString.string rangeOfString:@"\uFFFC"];
|
||||
if (badRange.location != NSNotFound)
|
||||
[mutableString replaceCharactersInRange:badRange withString:@""];
|
||||
self.attributedText = mutableString;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
- (void)keyCommandPressed:(UIKeyCommand *)keyCommand
|
||||
{
|
||||
id<HPGrowingTextViewDelegate> delegate = _delegate;
|
||||
|
||||
if ([delegate respondsToSelector:@selector(growingTextView:receivedReturnKeyCommandWithModifierFlags:)])
|
||||
[delegate growingTextView:self receivedReturnKeyCommandWithModifierFlags:keyCommand.modifierFlags];
|
||||
}
|
||||
|
||||
- (NSArray *)keyCommands
|
||||
{
|
||||
if (!self.receiveKeyCommands)
|
||||
return nil;
|
||||
|
||||
return @
|
||||
[
|
||||
[UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:0 action:@selector(keyCommandPressed:)],
|
||||
[UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:UIKeyModifierAlternate action:@selector(keyCommandPressed:)]
|
||||
];
|
||||
}
|
||||
|
||||
- (void)refreshAttributes {
|
||||
if (iosMajorVersion() < 7) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.internalTextView.typingAttributes = [self defaultAttributes];
|
||||
|
||||
NSAttributedString *string = self.attributedText;
|
||||
if (string.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGPoint contentOffset = _internalTextView.contentOffset;
|
||||
[_internalTextView setScrollEnabled:false];
|
||||
_internalTextView.disableContentOffsetAnimation = true;
|
||||
_internalTextView.freezeContentOffset = true;
|
||||
|
||||
//NSMutableAttributedString *mutableString = [[NSMutableAttributedString alloc] initWithAttributedString:string];
|
||||
//[mutableString removeAttribute:NSForegroundColorAttributeName range:NSMakeRange(0, string.length)];
|
||||
[_internalTextView.textStorage removeAttribute:NSForegroundColorAttributeName range:NSMakeRange(0, string.length)];
|
||||
|
||||
if (_intrinsicTextColor != nil) {
|
||||
//[mutableString addAttribute:NSForegroundColorAttributeName value:_intrinsicTextColor == nil ? [UIColor blackColor] : _intrinsicTextColor range:NSMakeRange(0, string.length)];
|
||||
[_internalTextView.textStorage addAttribute:NSForegroundColorAttributeName value:_intrinsicTextColor == nil ? [UIColor blackColor] : _intrinsicTextColor range:NSMakeRange(0, string.length)];
|
||||
}
|
||||
|
||||
__block NSMutableArray<TGInputTextTagAndRange *> *inputTextTags = [[NSMutableArray alloc] init];
|
||||
[string enumerateAttribute:TGMentionUidAttributeName inRange:NSMakeRange(0, string.length) options:0 usingBlock:^(__unused id value, NSRange range, __unused BOOL *stop) {
|
||||
if ([value isKindOfClass:[TGInputTextTag class]]) {
|
||||
[inputTextTags addObject:[[TGInputTextTagAndRange alloc] initWithTag:value range:range]];
|
||||
}
|
||||
}];
|
||||
|
||||
[string enumerateAttribute:TGMentionBoldAttributeName inRange:NSMakeRange(0, string.length) options:0 usingBlock:^(__unused id value, NSRange range, __unused BOOL *stop) {
|
||||
if ([value isKindOfClass:[TGInputTextTag class]]) {
|
||||
[inputTextTags addObject:[[TGInputTextTagAndRange alloc] initWithTag:value range:range]];
|
||||
}
|
||||
}];
|
||||
|
||||
if (inputTextTags != nil) {
|
||||
/*if (mutableString == nil) {
|
||||
mutableString = [[NSMutableAttributedString alloc] initWithAttributedString:string];
|
||||
}*/
|
||||
|
||||
static NSCharacterSet *alphanumericSet = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
alphanumericSet = [NSCharacterSet alphanumericCharacterSet];
|
||||
});
|
||||
|
||||
NSMutableSet<NSNumber *> *removeTags = [[NSMutableSet alloc] init];
|
||||
for (NSInteger i = 0; i < ((NSInteger)inputTextTags.count); i++) {
|
||||
TGInputTextTagAndRange *tagAndRange = inputTextTags[i];
|
||||
if ([removeTags containsObject:@(tagAndRange.tag.uniqueId)]) {
|
||||
[inputTextTags removeObjectAtIndex:i];
|
||||
//[mutableString removeAttribute:TGMentionUidAttributeName range:tagAndRange.range];
|
||||
[_internalTextView.textStorage removeAttribute:TGMentionUidAttributeName range:tagAndRange.range];
|
||||
|
||||
i--;
|
||||
} else {
|
||||
NSInteger j = tagAndRange.range.location;
|
||||
while (j < (NSInteger)(tagAndRange.range.location + tagAndRange.range.length)) {
|
||||
unichar c = [string.string characterAtIndex:j];
|
||||
if (c != ' ') {
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
||||
if (j != (NSInteger)tagAndRange.range.location) {
|
||||
NSRange updatedRange = NSMakeRange(j, tagAndRange.range.location + tagAndRange.range.length - j);
|
||||
//[mutableString removeAttribute:TGMentionUidAttributeName range:tagAndRange.range];
|
||||
[_internalTextView.textStorage removeAttribute:TGMentionUidAttributeName range:tagAndRange.range];
|
||||
|
||||
//[mutableString addAttribute:TGMentionUidAttributeName value:tagAndRange.tag range:updatedRange];
|
||||
[_internalTextView.textStorage addAttribute:TGMentionUidAttributeName value:tagAndRange.tag range:updatedRange];
|
||||
|
||||
inputTextTags[i] = [[TGInputTextTagAndRange alloc] initWithTag:tagAndRange.tag range:updatedRange];
|
||||
|
||||
i--;
|
||||
} else {
|
||||
NSInteger j = tagAndRange.range.location;
|
||||
while (j >= 0) {
|
||||
unichar c = [string.string characterAtIndex:j];
|
||||
if (![alphanumericSet characterIsMember:c]) {
|
||||
break;
|
||||
}
|
||||
j--;
|
||||
}
|
||||
j++;
|
||||
|
||||
if (j < ((NSInteger)tagAndRange.range.location)) {
|
||||
NSRange updatedRange = NSMakeRange(j, tagAndRange.range.location + tagAndRange.range.length - j);
|
||||
//[mutableString removeAttribute:TGMentionUidAttributeName range:tagAndRange.range];
|
||||
[_internalTextView.textStorage removeAttribute:TGMentionUidAttributeName range:tagAndRange.range];
|
||||
|
||||
//[mutableString addAttribute:TGMentionUidAttributeName value:tagAndRange.tag range:updatedRange];
|
||||
[_internalTextView.textStorage addAttribute:TGMentionUidAttributeName value:tagAndRange.tag range:updatedRange];
|
||||
|
||||
inputTextTags[i] = [[TGInputTextTagAndRange alloc] initWithTag:tagAndRange.tag range:updatedRange];
|
||||
|
||||
i--;
|
||||
} else {
|
||||
TGInputTextTagAndRange *nextTagAndRange = nil;
|
||||
if (i != ((NSInteger)inputTextTags.count) - 1) {
|
||||
nextTagAndRange = inputTextTags[i + 1];
|
||||
}
|
||||
|
||||
if (nextTagAndRange == nil || nextTagAndRange.tag.uniqueId != tagAndRange.tag.uniqueId) {
|
||||
NSInteger candidateStart = tagAndRange.range.location + tagAndRange.range.length;
|
||||
NSInteger candidateEnd = nextTagAndRange == nil ? string.length : nextTagAndRange.range.location;
|
||||
NSInteger j = candidateStart;
|
||||
while (j < candidateEnd) {
|
||||
unichar c = [string.string characterAtIndex:j];
|
||||
static NSCharacterSet *alphanumericSet = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
alphanumericSet = [NSCharacterSet alphanumericCharacterSet];
|
||||
});
|
||||
if (![alphanumericSet characterIsMember:c]) {
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
||||
if (j == candidateStart) {
|
||||
[removeTags addObject:@(tagAndRange.tag.uniqueId)];
|
||||
//[mutableString addAttribute:NSForegroundColorAttributeName value:TGAccentColor() range:tagAndRange.range];
|
||||
[_internalTextView.textStorage addAttribute:NSForegroundColorAttributeName value:self.accentColor range:tagAndRange.range];
|
||||
} else {
|
||||
//[mutableString removeAttribute:TGMentionUidAttributeName range:tagAndRange.range];
|
||||
[_internalTextView.textStorage removeAttribute:TGMentionUidAttributeName range:tagAndRange.range];
|
||||
|
||||
NSRange updatedRange = NSMakeRange(tagAndRange.range.location, j - tagAndRange.range.location);
|
||||
//[mutableString addAttribute:TGMentionUidAttributeName value:tagAndRange.tag range:updatedRange];
|
||||
[_internalTextView.textStorage addAttribute:TGMentionUidAttributeName value:tagAndRange.tag range:updatedRange];
|
||||
inputTextTags[i] = [[TGInputTextTagAndRange alloc] initWithTag:tagAndRange.tag range:updatedRange];
|
||||
|
||||
i--;
|
||||
}
|
||||
} else {
|
||||
NSInteger candidateStart = tagAndRange.range.location + tagAndRange.range.length;
|
||||
NSInteger candidateEnd = nextTagAndRange.range.location;
|
||||
NSInteger j = candidateStart;
|
||||
while (j < candidateEnd) {
|
||||
unichar c = [string.string characterAtIndex:j];
|
||||
if (![alphanumericSet characterIsMember:c] && c != ' ') {
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
||||
if (j == candidateEnd) {
|
||||
//[mutableString removeAttribute:TGMentionUidAttributeName range:tagAndRange.range];
|
||||
[_internalTextView.textStorage removeAttribute:TGMentionUidAttributeName range:tagAndRange.range];
|
||||
|
||||
//[mutableString removeAttribute:TGMentionUidAttributeName range:nextTagAndRange.range];
|
||||
[_internalTextView.textStorage removeAttribute:TGMentionUidAttributeName range:nextTagAndRange.range];
|
||||
|
||||
NSRange updatedRange = NSMakeRange(tagAndRange.range.location, nextTagAndRange.range.location + nextTagAndRange.range.length - tagAndRange.range.location);
|
||||
|
||||
//[mutableString addAttribute:TGMentionUidAttributeName value:tagAndRange.tag range:updatedRange];
|
||||
[_internalTextView.textStorage addAttribute:TGMentionUidAttributeName value:tagAndRange.tag range:updatedRange];
|
||||
|
||||
inputTextTags[i] = [[TGInputTextTagAndRange alloc] initWithTag:tagAndRange.tag range:updatedRange];
|
||||
[inputTextTags removeObjectAtIndex:i + 1];
|
||||
|
||||
i--;
|
||||
} else if (j != candidateStart) {
|
||||
//[mutableString removeAttribute:TGMentionUidAttributeName range:tagAndRange.range];
|
||||
[_internalTextView.textStorage removeAttribute:TGMentionUidAttributeName range:tagAndRange.range];
|
||||
|
||||
NSRange updatedRange = NSMakeRange(tagAndRange.range.location, j - tagAndRange.range.location);
|
||||
//[mutableString addAttribute:TGMentionUidAttributeName value:tagAndRange.tag range:updatedRange];
|
||||
[_internalTextView.textStorage addAttribute:TGMentionUidAttributeName value:tagAndRange.tag range:updatedRange];
|
||||
|
||||
inputTextTags[i] = [[TGInputTextTagAndRange alloc] initWithTag:tagAndRange.tag range:updatedRange];
|
||||
|
||||
i--;
|
||||
} else {
|
||||
[removeTags addObject:@(tagAndRange.tag.uniqueId)];
|
||||
//[mutableString addAttribute:NSForegroundColorAttributeName value:TGAccentColor() range:tagAndRange.range];
|
||||
[_internalTextView.textStorage addAttribute:NSForegroundColorAttributeName value:self.accentColor range:tagAndRange.range];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_internalTextView.freezeContentOffset = false;
|
||||
[_internalTextView setContentOffset:contentOffset];
|
||||
_internalTextView.disableContentOffsetAnimation = false;
|
||||
[_internalTextView setScrollEnabled:true];
|
||||
|
||||
/*if (mutableString != nil && ![mutableString isEqualToAttributedString:_internalTextView.attributedText])*/ {
|
||||
/*[_internalTextView.textStorage removeAttribute:NSForegroundColorAttributeName range:NSMakeRange(0, string.length)];
|
||||
[_internalTextView.textStorage addAttribute:NSForegroundColorAttributeName value:TGAccentColor() range:NSMakeRange(string.length - 1, 1)];
|
||||
|
||||
return;
|
||||
|
||||
UITextRange *previousRange = [_internalTextView selectedTextRange];
|
||||
UITextPosition *selStartPos = previousRange.start;
|
||||
NSInteger previousIdx = [_internalTextView offsetFromPosition:_internalTextView.beginningOfDocument toPosition:selStartPos];
|
||||
|
||||
_internalTextView.attributedText = mutableString;
|
||||
|
||||
UITextPosition *textPosition = [_internalTextView positionFromPosition:_internalTextView.beginningOfDocument offset:MIN(previousIdx, (NSInteger)mutableString.length)];
|
||||
[_internalTextView setSelectedTextRange:[_internalTextView textRangeFromPosition:textPosition toPosition:textPosition]];*/
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)textWithEntities:(__autoreleasing NSArray<TGMessageEntity *> ** _Nullable)entities {
|
||||
NSAttributedString *string = self.attributedText;
|
||||
|
||||
NSMutableArray *result = [[NSMutableArray alloc] init];
|
||||
[string enumerateAttribute:TGMentionUidAttributeName inRange:NSMakeRange(0, string.length) options:0 usingBlock:^(__unused id value, NSRange range, __unused BOOL *stop) {
|
||||
if ([value isKindOfClass:[TGInputTextTag class]]) {
|
||||
[result addObject:[[TGMessageEntityMentionName alloc] initWithRange:range userId:[((TGInputTextTag *)value).attachment intValue]]];
|
||||
}
|
||||
}];
|
||||
|
||||
if (iosMajorVersion() >= 7) {
|
||||
[string enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, string.length) options:0 usingBlock:^(UIFont *font, NSRange range, __unused BOOL *stop) {
|
||||
NSString *fontDescription = font.description;
|
||||
if ([fontDescription rangeOfString:@"font-weight: bold"].location != NSNotFound) {
|
||||
[result addObject:[[TGMessageEntityBold alloc] initWithRange:range]];
|
||||
} else if ([fontDescription rangeOfString:@"font-style: italic"].location != NSNotFound) {
|
||||
[result addObject:[[TGMessageEntityItalic alloc] initWithRange:range]];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
if (entities) {
|
||||
*entities = result;
|
||||
}
|
||||
|
||||
return string.string;
|
||||
}
|
||||
|
||||
+ (void)replaceMention:(NSString *)mention inputField:(HPGrowingTextView *)inputField username:(bool)username userId:(int64_t)userId
|
||||
{
|
||||
NSString *replacementText = [mention stringByAppendingString:@" "];
|
||||
|
||||
NSMutableAttributedString *text = inputField.internalTextView.attributedText == nil ? [[NSMutableAttributedString alloc] init] : [[NSMutableAttributedString alloc] initWithAttributedString:inputField.internalTextView.attributedText];
|
||||
|
||||
UITextRange *selRange = inputField.internalTextView.selectedTextRange;
|
||||
UITextPosition *selStartPos = selRange.start;
|
||||
NSInteger idx = [inputField.internalTextView offsetFromPosition:inputField.internalTextView.beginningOfDocument toPosition:selStartPos];
|
||||
idx--;
|
||||
NSRange candidateMentionRange = NSMakeRange(NSNotFound, 0);
|
||||
|
||||
if (idx >= 0 && idx < (int)text.length)
|
||||
{
|
||||
for (NSInteger i = idx; i >= 0; i--)
|
||||
{
|
||||
unichar c = [text.string characterAtIndex:i];
|
||||
if (c == '@')
|
||||
{
|
||||
if (i == idx)
|
||||
candidateMentionRange = NSMakeRange(i + 1, 0);
|
||||
else
|
||||
candidateMentionRange = NSMakeRange(i + 1, idx - i);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (candidateMentionRange.location != NSNotFound)
|
||||
{
|
||||
if (!username) {
|
||||
candidateMentionRange.location -= 1;
|
||||
candidateMentionRange.length += 1;
|
||||
|
||||
[text replaceCharactersInRange:candidateMentionRange withString:replacementText];
|
||||
|
||||
static int64_t nextId = 0;
|
||||
nextId++;
|
||||
[text addAttributes:@{TGMentionUidAttributeName: [[TGInputTextTag alloc] initWithUniqueId:nextId left:true attachment:@(userId)]} range:NSMakeRange(candidateMentionRange.location, replacementText.length - 1)];
|
||||
} else {
|
||||
[text replaceCharactersInRange:candidateMentionRange withString:replacementText];
|
||||
}
|
||||
|
||||
[inputField setAttributedText:text];
|
||||
UITextPosition *textPosition = [inputField.internalTextView positionFromPosition:inputField.internalTextView.beginningOfDocument offset:candidateMentionRange.location + replacementText.length];
|
||||
[inputField.internalTextView setSelectedTextRange:[inputField.internalTextView textRangeFromPosition:textPosition toPosition:textPosition]];
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)replaceHashtag:(NSString *)hashtag inputField:(HPGrowingTextView *)inputField
|
||||
{
|
||||
if (inputField.attributedText == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
static NSCharacterSet *characterSet = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^
|
||||
{
|
||||
characterSet = [NSCharacterSet alphanumericCharacterSet];
|
||||
});
|
||||
|
||||
NSString *replacementText = [hashtag stringByAppendingString:@" "];
|
||||
|
||||
NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithAttributedString:inputField.attributedText];
|
||||
|
||||
UITextRange *selRange = inputField.internalTextView.selectedTextRange;
|
||||
UITextPosition *selStartPos = selRange.start;
|
||||
NSInteger idx = [inputField.internalTextView offsetFromPosition:inputField.internalTextView.beginningOfDocument toPosition:selStartPos];
|
||||
idx--;
|
||||
NSRange candidateHashtagRange = NSMakeRange(NSNotFound, 0);
|
||||
NSString *string = text.string;
|
||||
|
||||
if (idx >= 0 && idx < (int)text.length)
|
||||
{
|
||||
for (NSInteger i = idx; i >= 0; i--)
|
||||
{
|
||||
unichar c = [string characterAtIndex:i];
|
||||
if (c == '#')
|
||||
{
|
||||
if (i == idx)
|
||||
candidateHashtagRange = NSMakeRange(i + 1, 0);
|
||||
else
|
||||
candidateHashtagRange = NSMakeRange(i + 1, idx - i);
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == ' ' || (![characterSet characterIsMember:c] && c != '_'))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (candidateHashtagRange.location != NSNotFound)
|
||||
{
|
||||
[text replaceCharactersInRange:candidateHashtagRange withString:replacementText];
|
||||
[inputField setAttributedText:text];
|
||||
UITextPosition *textPosition = [inputField.internalTextView positionFromPosition:inputField.internalTextView.beginningOfDocument offset:candidateHashtagRange.location + replacementText.length];
|
||||
[inputField.internalTextView setSelectedTextRange:[inputField.internalTextView textRangeFromPosition:textPosition toPosition:textPosition]];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -1,290 +0,0 @@
|
|||
#import "HPTextViewInternal.h"
|
||||
|
||||
#import "LegacyComponentsInternal.h"
|
||||
#import "TGHacks.h"
|
||||
#import "FreedomUIKit.h"
|
||||
|
||||
#import "HPGrowingTextView.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
#import <objc/message.h>
|
||||
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
|
||||
#import "TGKeyCommandController.h"
|
||||
|
||||
@interface HPTextViewInternal () {
|
||||
__weak TGKeyCommandController *_keyCommandController;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation HPTextViewInternal
|
||||
|
||||
- (instancetype)initWithKeyCommandController:(TGKeyCommandController *)keyCommandController {
|
||||
self = [super initWithFrame:CGRectZero];
|
||||
if (self != nil) {
|
||||
_keyCommandController = keyCommandController;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setText:(NSString *)text
|
||||
{
|
||||
BOOL originalValue = self.scrollEnabled;
|
||||
//If one of GrowingTextView's superviews is a scrollView, and self.scrollEnabled == NO,
|
||||
//setting the text programatically will cause UIKit to search upwards until it finds a scrollView with scrollEnabled==yes
|
||||
//then scroll it erratically. Setting scrollEnabled temporarily to YES prevents this.
|
||||
[self setScrollEnabled:YES];
|
||||
[super setText:text];
|
||||
[self setScrollEnabled:originalValue];
|
||||
}
|
||||
|
||||
- (void)setAttributedText:(NSAttributedString *)attributedText {
|
||||
BOOL originalValue = self.scrollEnabled;
|
||||
//If one of GrowingTextView's superviews is a scrollView, and self.scrollEnabled == NO,
|
||||
//setting the text programatically will cause UIKit to search upwards until it finds a scrollView with scrollEnabled==yes
|
||||
//then scroll it erratically. Setting scrollEnabled temporarily to YES prevents this.
|
||||
[self setScrollEnabled:YES];
|
||||
[super setAttributedText:attributedText];
|
||||
[self setScrollEnabled:originalValue];
|
||||
}
|
||||
|
||||
- (void)setScrollable:(BOOL)isScrollable
|
||||
{
|
||||
[super setScrollEnabled:isScrollable];
|
||||
}
|
||||
|
||||
- (void)scrollRectToVisible:(CGRect)__unused rect animated:(BOOL)__unused animated
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated
|
||||
{
|
||||
if (_freezeContentOffset)
|
||||
return;
|
||||
|
||||
[super setContentOffset:contentOffset animated:_disableContentOffsetAnimation ? false : animated];
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)frame
|
||||
{
|
||||
[super setFrame:frame];
|
||||
}
|
||||
|
||||
-(void)setContentOffset:(CGPoint)s
|
||||
{
|
||||
if (_freezeContentOffset)
|
||||
return;
|
||||
|
||||
[super setContentOffset:s];
|
||||
}
|
||||
|
||||
- (void)textViewEnsureSelectionVisible
|
||||
{
|
||||
if (iosMajorVersion() >= 9) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
CGRect caretFrame = [self caretRectForPosition:self.selectedTextRange.end];
|
||||
if (caretFrame.origin.x < CGFLOAT_MAX && caretFrame.origin.y < CGFLOAT_MAX && !CGRectIsInfinite(caretFrame))
|
||||
{
|
||||
UIEdgeInsets implicitInset = UIEdgeInsetsMake(8, 0, 8, 0);
|
||||
|
||||
caretFrame.origin.y -= implicitInset.top;
|
||||
caretFrame.size.height += implicitInset.top + implicitInset.bottom;
|
||||
caretFrame.origin.y = CGFloor(caretFrame.origin.y * 2.0f) / 2.0f;
|
||||
caretFrame.size.height = CGFloor(caretFrame.size.height * 2.0f) / 2.0f;
|
||||
|
||||
CGFloat frameHeight = self.frame.size.height;
|
||||
CGPoint contentOffset = self.contentOffset;
|
||||
|
||||
if (caretFrame.origin.y < contentOffset.y)
|
||||
contentOffset.y = caretFrame.origin.y;
|
||||
if (caretFrame.origin.y + caretFrame.size.height > contentOffset.y + frameHeight)
|
||||
contentOffset.y = caretFrame.origin.y + caretFrame.size.height - frameHeight;
|
||||
contentOffset.y = MAX(0, contentOffset.y);
|
||||
|
||||
if (!CGPointEqualToPoint(contentOffset, self.contentOffset))
|
||||
self.contentOffset = contentOffset;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
CGRect caretFrame = [self caretRectForPosition:self.selectedTextRange.end];
|
||||
if (caretFrame.origin.x < CGFLOAT_MAX && caretFrame.origin.y < CGFLOAT_MAX && !CGRectIsInfinite(caretFrame))
|
||||
{
|
||||
UIEdgeInsets implicitInset = UIEdgeInsetsMake(8, 0, 8, 0);
|
||||
|
||||
caretFrame.origin.y -= implicitInset.top;
|
||||
caretFrame.size.height += implicitInset.top + implicitInset.bottom;
|
||||
caretFrame.origin.y = CGFloor(caretFrame.origin.y * 2.0f) / 2.0f;
|
||||
caretFrame.size.height = CGFloor(caretFrame.size.height * 2.0f) / 2.0f;
|
||||
|
||||
CGFloat frameHeight = self.frame.size.height;
|
||||
CGPoint contentOffset = self.contentOffset;
|
||||
|
||||
if (caretFrame.origin.y < contentOffset.y)
|
||||
contentOffset.y = caretFrame.origin.y;
|
||||
if (caretFrame.origin.y + caretFrame.size.height > contentOffset.y + frameHeight)
|
||||
contentOffset.y = caretFrame.origin.y + caretFrame.size.height - frameHeight;
|
||||
contentOffset.y = MAX(0, contentOffset.y);
|
||||
|
||||
if (!CGPointEqualToPoint(contentOffset, self.contentOffset))
|
||||
self.contentOffset = contentOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setContentSize:(CGSize)contentSize
|
||||
{
|
||||
[super setContentSize:contentSize];
|
||||
|
||||
[self textViewEnsureSelectionVisible];
|
||||
}
|
||||
|
||||
- (BOOL)canBecomeFirstResponder
|
||||
{
|
||||
if (!_enableFirstResponder)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
- (BOOL)becomeFirstResponder
|
||||
{
|
||||
if (!_enableFirstResponder)
|
||||
return false;
|
||||
|
||||
__block BOOL result = false;
|
||||
freedomUIKitTest4(^
|
||||
{
|
||||
if ([self.delegate respondsToSelector:@selector(textViewShouldBeginEditing:)] && ![self.delegate textViewShouldBeginEditing:self])
|
||||
result = false;
|
||||
else
|
||||
result = [super becomeFirstResponder];
|
||||
});
|
||||
|
||||
if (result)
|
||||
{
|
||||
id delegate = _responderStateDelegate.object;
|
||||
if (delegate != nil && [delegate conformsToProtocol:@protocol(HPTextViewInternalDelegate)])
|
||||
{
|
||||
[(id<HPTextViewInternalDelegate>)delegate hpTextViewChangedResponderState:true];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)resignFirstResponder
|
||||
{
|
||||
__block BOOL result = false;
|
||||
freedomUIKitTest4(^
|
||||
{
|
||||
result = [super resignFirstResponder];
|
||||
});
|
||||
|
||||
if (result)
|
||||
{
|
||||
id delegate = _responderStateDelegate.object;
|
||||
if (delegate != nil && [delegate conformsToProtocol:@protocol(HPTextViewInternalDelegate)])
|
||||
{
|
||||
[(id<HPTextViewInternalDelegate>)delegate hpTextViewChangedResponderState:false];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
|
||||
{
|
||||
if (action == @selector(paste:))
|
||||
return true;
|
||||
|
||||
if (action == @selector(toggleUnderline:)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wundeclared-selector"
|
||||
static SEL promptForReplaceSelector;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
promptForReplaceSelector = NSSelectorFromString(@"_promptForReplace:");
|
||||
});
|
||||
if (action == promptForReplaceSelector) {
|
||||
return false;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
return [super canPerformAction:action withSender:sender];
|
||||
}
|
||||
|
||||
- (id)targetForAction:(SEL)action withSender:(id)__unused sender
|
||||
{
|
||||
if (action == @selector(processKeyCommand:)) {
|
||||
TGKeyCommandController *keyCommandController = _keyCommandController;
|
||||
return [keyCommandController targetForAction:action withSender:sender];
|
||||
}
|
||||
|
||||
return [super targetForAction:action withSender:sender];
|
||||
}
|
||||
|
||||
- (void)paste:(id)sender
|
||||
{
|
||||
UIPasteboard *pasteBoard = [UIPasteboard generalPasteboard];
|
||||
|
||||
NSData *gifData = [pasteBoard dataForPasteboardType:@"com.compuserve.gif"];
|
||||
if (gifData != nil)
|
||||
{
|
||||
id delegate = self.delegate;
|
||||
if ([delegate isKindOfClass:[HPGrowingTextView class]])
|
||||
{
|
||||
HPGrowingTextView *textView = delegate;
|
||||
NSObject<HPGrowingTextViewDelegate> *textViewDelegate = (NSObject<HPGrowingTextViewDelegate> *)textView.delegate;
|
||||
if ([textViewDelegate respondsToSelector:@selector(growingTextView:didPasteData:)])
|
||||
[textViewDelegate growingTextView:textView didPasteData:gifData];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSMutableArray *images = [NSMutableArray arrayWithCapacity:1];
|
||||
NSString *text = nil;
|
||||
|
||||
for (NSDictionary *item in pasteBoard.items) {
|
||||
if (item[(__bridge NSString *)kUTTypeJPEG] != nil) {
|
||||
[images addObject:item[(__bridge NSString *)kUTTypeJPEG]];
|
||||
} else if (item[(__bridge NSString *)kUTTypePNG] != nil) {
|
||||
[images addObject:item[(__bridge NSString *)kUTTypePNG]];
|
||||
} else if (item[(__bridge NSString *)kUTTypeGIF] != nil) {
|
||||
[images addObject:item[(__bridge NSString *)kUTTypeGIF]];
|
||||
} else if (item[(__bridge NSString *)kUTTypeURL] != nil) {
|
||||
id url = item[(__bridge NSString *)kUTTypeURL];
|
||||
if ([url respondsToSelector:@selector(characterAtIndex:)]) {
|
||||
text = url;
|
||||
} else if ([url isKindOfClass:[NSURL class]]) {
|
||||
text = ((NSURL *)url).absoluteString;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (images.count != 0)
|
||||
{
|
||||
id delegate = self.delegate;
|
||||
if ([delegate isKindOfClass:[HPGrowingTextView class]])
|
||||
{
|
||||
HPGrowingTextView *textView = delegate;
|
||||
NSObject<HPGrowingTextViewDelegate> *textViewDelegate = (NSObject<HPGrowingTextViewDelegate> *)textView.delegate;
|
||||
if ([textViewDelegate respondsToSelector:@selector(growingTextView:didPasteImages:andText:)])
|
||||
[textViewDelegate growingTextView:textView didPasteImages:images andText:text];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_isPasting = true;
|
||||
bool previousAllowsEditingTextAttributes = self.allowsEditingTextAttributes;
|
||||
self.allowsEditingTextAttributes = false;
|
||||
[super paste:sender];
|
||||
self.allowsEditingTextAttributes = previousAllowsEditingTextAttributes;
|
||||
_isPasting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#import <LegacyComponents/TGModernConversationAssociatedInputPanel.h>
|
||||
|
||||
extern NSString *const TGAlphacodePanelCellKind;
|
||||
|
||||
@class TGConversationAssociatedInputPanelPallete;
|
||||
|
||||
@interface TGAlphacodePanelCell : UITableViewCell
|
||||
|
||||
@property (nonatomic, strong) TGConversationAssociatedInputPanelPallete *pallete;
|
||||
|
||||
- (instancetype)initWithStyle:(TGModernConversationAssociatedInputPanelStyle)style;
|
||||
- (void)setEmoji:(NSString *)emoji label:(NSString *)label;
|
||||
|
||||
@end
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
#import "TGAlphacodePanelCell.h"
|
||||
|
||||
#import "LegacyComponentsInternal.h"
|
||||
#import "TGColor.h"
|
||||
#import "TGFont.h"
|
||||
|
||||
#import "TGModernConversationAssociatedInputPanel.h"
|
||||
|
||||
NSString *const TGAlphacodePanelCellKind = @"TGAlphacodePanelCell";
|
||||
|
||||
@interface TGAlphacodePanelCell () {
|
||||
UILabel *_emojiLabel;
|
||||
UILabel *_descriptionLabel;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TGAlphacodePanelCell
|
||||
|
||||
- (instancetype)initWithStyle:(TGModernConversationAssociatedInputPanelStyle)style {
|
||||
self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TGAlphacodePanelCellKind];
|
||||
if (self != nil) {
|
||||
UIColor *backgroundColor = [UIColor whiteColor];
|
||||
UIColor *nameColor = [UIColor blackColor];
|
||||
UIColor *usernameColor = [UIColor blackColor];
|
||||
UIColor *selectionColor = TGSelectionColor();
|
||||
|
||||
if (style == TGModernConversationAssociatedInputPanelDarkStyle)
|
||||
{
|
||||
backgroundColor = UIColorRGB(0x171717);
|
||||
nameColor = [UIColor whiteColor];
|
||||
usernameColor = UIColorRGB(0x828282);
|
||||
selectionColor = UIColorRGB(0x292929);
|
||||
}
|
||||
else if (style == TGModernConversationAssociatedInputPanelDarkBlurredStyle)
|
||||
{
|
||||
backgroundColor = [UIColor clearColor];
|
||||
nameColor = [UIColor whiteColor];
|
||||
usernameColor = UIColorRGB(0x828282);
|
||||
selectionColor = UIColorRGB(0x3d3d3d);
|
||||
}
|
||||
|
||||
self.backgroundColor = backgroundColor;
|
||||
self.backgroundView = [[UIView alloc] init];
|
||||
self.backgroundView.backgroundColor = backgroundColor;
|
||||
self.backgroundView.opaque = false;
|
||||
|
||||
self.selectedBackgroundView = [[UIView alloc] init];
|
||||
self.selectedBackgroundView.backgroundColor = selectionColor;
|
||||
|
||||
_emojiLabel = [[UILabel alloc] init];
|
||||
_emojiLabel.backgroundColor = [UIColor clearColor];
|
||||
_emojiLabel.textColor = nameColor;
|
||||
_emojiLabel.font = TGSystemFontOfSize(14.0f);
|
||||
_emojiLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
[self.contentView addSubview:_emojiLabel];
|
||||
|
||||
_descriptionLabel = [[UILabel alloc] init];
|
||||
_descriptionLabel.backgroundColor = [UIColor clearColor];
|
||||
_descriptionLabel.textColor = usernameColor;
|
||||
_descriptionLabel.font = TGSystemFontOfSize(14.0f);
|
||||
_descriptionLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
[self.contentView addSubview:_descriptionLabel];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setPallete:(TGConversationAssociatedInputPanelPallete *)pallete
|
||||
{
|
||||
if (pallete == nil || _pallete == pallete)
|
||||
return;
|
||||
|
||||
_pallete = pallete;
|
||||
|
||||
_emojiLabel.textColor = pallete.textColor;
|
||||
_descriptionLabel.textColor = pallete.textColor;
|
||||
|
||||
self.backgroundColor = pallete.backgroundColor;
|
||||
self.backgroundView.backgroundColor = self.backgroundColor;
|
||||
self.selectedBackgroundView.backgroundColor = pallete.selectionColor;
|
||||
}
|
||||
|
||||
- (void)setEmoji:(NSString *)emoji label:(NSString *)label {
|
||||
_emojiLabel.text = emoji;
|
||||
_descriptionLabel.text = label;
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
CGSize boundsSize = self.bounds.size;
|
||||
|
||||
CGFloat leftInset = 11.0f;
|
||||
CGFloat rightInset = 6.0f;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
CGSize titleSize = [_emojiLabel.text sizeWithFont:_emojiLabel.font];
|
||||
#pragma clang diagnostic pop
|
||||
titleSize.width = CGCeil(MIN((boundsSize.width - leftInset - rightInset) * 3.0f / 4.0f, titleSize.width));
|
||||
titleSize.height = CGCeil(titleSize.height);
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
CGSize descriptionSize = [_descriptionLabel.text sizeWithFont:_descriptionLabel.font];
|
||||
#pragma clang diagnostic pop
|
||||
descriptionSize.width = CGCeil(MIN(boundsSize.width - leftInset - 40.0f, descriptionSize.width));
|
||||
|
||||
_emojiLabel.frame = CGRectMake(leftInset, CGFloor((boundsSize.height - titleSize.height) / 2.0f), titleSize.width, titleSize.height);
|
||||
_descriptionLabel.frame = CGRectMake(40.0f, CGFloor((boundsSize.height - descriptionSize.height) / 2.0f), descriptionSize.width, descriptionSize.height);
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -2293,6 +2293,9 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
|
|||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
if (strongSelf.finishedTransitionOut != nil)
|
||||
strongSelf.finishedTransitionOut();
|
||||
|
||||
[strongSelf dismiss];
|
||||
}];
|
||||
return;
|
||||
|
|
@ -2371,13 +2374,13 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
|
|||
{
|
||||
self.view.userInteractionEnabled = false;
|
||||
|
||||
const CGFloat minVelocity = 2000.0f;
|
||||
const CGFloat minVelocity = 4000.0f;
|
||||
if (ABS(velocity) < minVelocity)
|
||||
velocity = (velocity < 0.0f ? -1.0f : 1.0f) * minVelocity;
|
||||
CGFloat distance = (velocity < FLT_EPSILON ? -1.0f : 1.0f) * self.view.frame.size.height;
|
||||
CGRect targetFrame = (CGRect){{_previewView.frame.origin.x, distance}, _previewView.frame.size};
|
||||
|
||||
[UIView animateWithDuration:ABS(distance / velocity) animations:^
|
||||
[UIView animateWithDuration:ABS(distance / velocity) delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^
|
||||
{
|
||||
_previewView.frame = targetFrame;
|
||||
_cornersView.frame = targetFrame;
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
#import <LegacyComponents/TGModernConversationAssociatedInputPanel.h>
|
||||
|
||||
@class TGConversationAssociatedInputPanelPallete;
|
||||
|
||||
@interface TGHashtagPanelCell : UITableViewCell
|
||||
|
||||
@property (nonatomic, strong) TGConversationAssociatedInputPanelPallete *pallete;
|
||||
|
||||
- (instancetype)initWithStyle:(TGModernConversationAssociatedInputPanelStyle)style;
|
||||
|
||||
- (void)setDisplaySeparator:(bool)displaySeparator;
|
||||
- (void)setHashtag:(NSString *)hashtag;
|
||||
|
||||
@end
|
||||
|
||||
extern NSString *const TGHashtagPanelCellKind;
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
#import "TGHashtagPanelCell.h"
|
||||
|
||||
#import "LegacyComponentsInternal.h"
|
||||
#import "TGColor.h"
|
||||
#import "TGFont.h"
|
||||
#import "TGImageUtils.h"
|
||||
|
||||
#import "TGModernConversationAssociatedInputPanel.h"
|
||||
|
||||
NSString *const TGHashtagPanelCellKind = @"TGHashtagPanelCell";
|
||||
|
||||
@interface TGHashtagPanelCell ()
|
||||
{
|
||||
TGModernConversationAssociatedInputPanelStyle _style;
|
||||
UILabel *_label;
|
||||
UIView *_separatorView;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TGHashtagPanelCell
|
||||
|
||||
- (instancetype)initWithStyle:(TGModernConversationAssociatedInputPanelStyle)style
|
||||
{
|
||||
self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TGHashtagPanelCellKind];
|
||||
if (self != nil)
|
||||
{
|
||||
_style = style;
|
||||
|
||||
UIColor *backgroundColor = [UIColor whiteColor];
|
||||
UIColor *textColor = [UIColor blackColor];
|
||||
UIColor *selectionColor = TGSelectionColor();
|
||||
|
||||
if (style == TGModernConversationAssociatedInputPanelDarkStyle)
|
||||
{
|
||||
backgroundColor = UIColorRGB(0x171717);
|
||||
textColor = [UIColor whiteColor];
|
||||
selectionColor = UIColorRGB(0x292929);
|
||||
}
|
||||
else if (style == TGModernConversationAssociatedInputPanelDarkBlurredStyle)
|
||||
{
|
||||
backgroundColor = [UIColor clearColor];
|
||||
textColor = [UIColor whiteColor];
|
||||
selectionColor = UIColorRGB(0x3d3d3d);
|
||||
}
|
||||
|
||||
self.backgroundColor = backgroundColor;
|
||||
self.backgroundView = [[UIView alloc] init];
|
||||
self.backgroundView.backgroundColor = backgroundColor;
|
||||
self.backgroundView.opaque = false;
|
||||
|
||||
self.selectedBackgroundView = [[UIView alloc] init];
|
||||
self.selectedBackgroundView.backgroundColor = selectionColor;
|
||||
|
||||
_label = [[UILabel alloc] init];
|
||||
_label.backgroundColor = [UIColor clearColor];
|
||||
_label.textColor = textColor;
|
||||
_label.font = TGSystemFontOfSize(14.0f);
|
||||
[self.contentView addSubview:_label];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setPallete:(TGConversationAssociatedInputPanelPallete *)pallete
|
||||
{
|
||||
if (pallete == nil || _pallete == pallete)
|
||||
return;
|
||||
|
||||
_pallete = pallete;
|
||||
|
||||
_label.textColor = pallete.textColor;
|
||||
|
||||
self.backgroundColor = pallete.backgroundColor;
|
||||
self.backgroundView.backgroundColor = self.backgroundColor;
|
||||
self.selectedBackgroundView.backgroundColor = pallete.selectionColor;
|
||||
|
||||
_separatorView.backgroundColor = pallete.separatorColor;
|
||||
}
|
||||
|
||||
- (void)setDisplaySeparator:(bool)displaySeparator
|
||||
{
|
||||
if (displaySeparator && _separatorView == nil)
|
||||
{
|
||||
UIColor *separatorColor = _pallete != nil ? _pallete.separatorColor : TGSeparatorColor();
|
||||
if (_style == TGModernConversationAssociatedInputPanelDarkStyle)
|
||||
separatorColor = UIColorRGB(0x292929);
|
||||
|
||||
_separatorView = [[UIView alloc] init];
|
||||
_separatorView.backgroundColor = separatorColor;
|
||||
[self insertSubview:_separatorView belowSubview:self.contentView];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setHashtag:(NSString *)hashtag
|
||||
{
|
||||
_label.text = [[NSString alloc] initWithFormat:@"#%@", hashtag];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
CGFloat inset = 15.0f;
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
CGSize labelSize = [_label.text sizeWithFont:_label.font];
|
||||
#pragma clang diagnostic pop
|
||||
labelSize.width = CGCeil(MIN(labelSize.width, self.frame.size.width - inset * 2.0f));
|
||||
labelSize.height = CGCeil(labelSize.height);
|
||||
_label.frame = CGRectMake(inset, CGFloor((self.frame.size.height - labelSize.height) / 2.0f), labelSize.width, labelSize.height);
|
||||
|
||||
if (_separatorView != nil)
|
||||
{
|
||||
CGFloat separatorHeight = TGScreenPixel;
|
||||
_separatorView.frame = CGRectMake(inset, self.frame.size.height - separatorHeight, self.frame.size.width - inset, separatorHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -211,6 +211,9 @@
|
|||
if (strongController == nil)
|
||||
return;
|
||||
|
||||
if (strongController->_toolbarView.superview == nil)
|
||||
return;
|
||||
|
||||
UIView *toolbarView = strongController->_toolbarView;
|
||||
if (enabled)
|
||||
{
|
||||
|
|
@ -271,7 +274,11 @@
|
|||
[groupsController setIsFirstInStack:true];
|
||||
[pickerController setIsFirstInStack:false];
|
||||
|
||||
[assetsController setViewControllers:@[ groupsController, pickerController ]];
|
||||
if (intent == TGMediaAssetsControllerSendMediaIntent) {
|
||||
[assetsController setViewControllers:@[ pickerController ]];
|
||||
} else {
|
||||
[assetsController setViewControllers:@[ groupsController, pickerController ]];
|
||||
}
|
||||
((TGNavigationBar *)assetsController.navigationBar).navigationController = assetsController;
|
||||
|
||||
assetsController.recipientName = recipientName;
|
||||
|
|
@ -611,7 +618,8 @@
|
|||
[strongSelf groupPhotosPressed];
|
||||
};
|
||||
}
|
||||
[self.view addSubview:_toolbarView];
|
||||
if (_intent != TGMediaAssetsControllerSendMediaIntent)
|
||||
[self.view addSubview:_toolbarView];
|
||||
|
||||
if (@available(iOS 14.0, *)) {
|
||||
if ([PHPhotoLibrary authorizationStatusForAccessLevel:PHAccessLevelReadWrite] == PHAuthorizationStatusLimited) {
|
||||
|
|
@ -1386,23 +1394,37 @@
|
|||
|
||||
#pragma mark -
|
||||
|
||||
- (UIBarButtonItem *)leftBarButtonItem
|
||||
{
|
||||
if (_intent == TGMediaAssetsControllerSendMediaIntent) {
|
||||
return [[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Cancel") style:UIBarButtonItemStylePlain target:self action:@selector(cancelButtonPressed)];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (UIBarButtonItem *)rightBarButtonItem
|
||||
{
|
||||
if (_intent == TGMediaAssetsControllerSendFileIntent)
|
||||
return nil;
|
||||
if (self.requestSearchController == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (iosMajorVersion() < 7)
|
||||
{
|
||||
TGModernBarButton *searchButton = [[TGModernBarButton alloc] initWithImage:TGComponentsImageNamed(@"NavigationSearchIcon.png")];
|
||||
searchButton.portraitAdjustment = CGPointMake(-7, -5);
|
||||
[searchButton addTarget:self action:@selector(searchButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||
return [[UIBarButtonItem alloc] initWithCustomView:searchButton];
|
||||
}
|
||||
|
||||
return [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSearch target:self action:@selector(searchButtonPressed)];
|
||||
return nil;
|
||||
// if (_intent == TGMediaAssetsControllerSendFileIntent)
|
||||
// return nil;
|
||||
// if (self.requestSearchController == nil) {
|
||||
// return nil;
|
||||
// }
|
||||
//
|
||||
// if (iosMajorVersion() < 7)
|
||||
// {
|
||||
// TGModernBarButton *searchButton = [[TGModernBarButton alloc] initWithImage:TGComponentsImageNamed(@"NavigationSearchIcon.png")];
|
||||
// searchButton.portraitAdjustment = CGPointMake(-7, -5);
|
||||
// [searchButton addTarget:self action:@selector(searchButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||
// return [[UIBarButtonItem alloc] initWithCustomView:searchButton];
|
||||
// }
|
||||
//
|
||||
// return [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSearch target:self action:@selector(searchButtonPressed)];
|
||||
}
|
||||
|
||||
- (void)cancelButtonPressed
|
||||
{
|
||||
[self dismiss];
|
||||
}
|
||||
|
||||
- (void)searchButtonPressed
|
||||
|
|
@ -1412,6 +1434,18 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void)send:(bool)silently
|
||||
{
|
||||
[self completeWithCurrentItem:nil silentPosting:silently scheduleTime:0];
|
||||
}
|
||||
|
||||
- (void)schedule {
|
||||
__weak TGMediaAssetsController *weakSelf = self;
|
||||
self.presentScheduleController(^(int32_t scheduleTime) {
|
||||
[weakSelf completeWithCurrentItem:nil silentPosting:false scheduleTime:scheduleTime];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)navigationController:(UINavigationController *)__unused navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)__unused animated
|
||||
{
|
||||
if (_searchController == nil)
|
||||
|
|
|
|||
|
|
@ -97,38 +97,22 @@
|
|||
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||
{
|
||||
PHFetchOptions *options = [PHFetchOptions new];
|
||||
PHFetchResult *fetchResult = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumUserLibrary options:nil];
|
||||
PHAssetCollection *assetCollection = fetchResult.firstObject;
|
||||
|
||||
if (iosMajorVersion() == 8 && iosMinorVersion() < 1)
|
||||
if (assetCollection != nil)
|
||||
{
|
||||
PHFetchResult *fetchResult = nil;
|
||||
if (assetType != TGMediaAssetAnyType)
|
||||
options.predicate = [NSPredicate predicateWithFormat:@"mediaType = %i", [TGMediaAsset assetMediaTypeForAssetType:assetType]];
|
||||
|
||||
if (assetType == TGMediaAssetAnyType)
|
||||
fetchResult = [PHAsset fetchAssetsWithOptions:options];
|
||||
else
|
||||
fetchResult = [PHAsset fetchAssetsWithMediaType:[TGMediaAsset assetMediaTypeForAssetType:assetType] options:options];
|
||||
PHFetchResult *assetsFetchResult = [PHAsset fetchAssetsInAssetCollection:assetCollection options:options];
|
||||
|
||||
[subscriber putNext:[[TGMediaAssetGroup alloc] initWithPHFetchResult:fetchResult]];
|
||||
[subscriber putNext:[[TGMediaAssetGroup alloc] initWithPHAssetCollection:assetCollection fetchResult:assetsFetchResult]];
|
||||
[subscriber putCompletion];
|
||||
}
|
||||
else
|
||||
{
|
||||
PHFetchResult *fetchResult = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumUserLibrary options:nil];
|
||||
PHAssetCollection *assetCollection = fetchResult.firstObject;
|
||||
|
||||
if (assetCollection != nil)
|
||||
{
|
||||
if (assetType != TGMediaAssetAnyType)
|
||||
options.predicate = [NSPredicate predicateWithFormat:@"mediaType = %i", [TGMediaAsset assetMediaTypeForAssetType:assetType]];
|
||||
|
||||
PHFetchResult *assetsFetchResult = [PHAsset fetchAssetsInAssetCollection:assetCollection options:options];
|
||||
|
||||
[subscriber putNext:[[TGMediaAssetGroup alloc] initWithPHAssetCollection:assetCollection fetchResult:assetsFetchResult]];
|
||||
[subscriber putCompletion];
|
||||
}
|
||||
else
|
||||
{
|
||||
[subscriber putError:nil];
|
||||
}
|
||||
[subscriber putError:nil];
|
||||
}
|
||||
|
||||
return nil;
|
||||
|
|
|
|||
|
|
@ -89,7 +89,11 @@
|
|||
_assetGroup = assetGroup;
|
||||
_intent = intent;
|
||||
|
||||
[self setTitle:_assetGroup.title];
|
||||
if (_intent == TGMediaAssetsControllerSendMediaIntent) {
|
||||
[self setTitle:TGLocalized(@"Attachment.Gallery")];
|
||||
} else {
|
||||
[self setTitle:assetGroup.title];
|
||||
}
|
||||
|
||||
_assetsDisposable = [[SMetaDisposable alloc] init];
|
||||
}
|
||||
|
|
@ -152,10 +156,13 @@
|
|||
[super viewDidLoad];
|
||||
|
||||
SSignal *groupSignal = nil;
|
||||
if (_assetGroup != nil)
|
||||
bool reversed = false;
|
||||
if (_assetGroup != nil) {
|
||||
groupSignal = [SSignal single:_assetGroup];
|
||||
else
|
||||
} else {
|
||||
groupSignal = [_assetsLibrary cameraRollGroup];
|
||||
reversed = true;
|
||||
}
|
||||
|
||||
__weak TGMediaAssetsPickerController *weakSelf = self;
|
||||
[_assetsDisposable setDisposable:[[[[groupSignal deliverOn:[SQueue mainQueue]] mapToSignal:^SSignal *(TGMediaAssetGroup *assetGroup)
|
||||
|
|
@ -167,8 +174,11 @@
|
|||
if (strongSelf->_assetGroup == nil)
|
||||
strongSelf->_assetGroup = assetGroup;
|
||||
|
||||
[strongSelf setTitle:assetGroup.title];
|
||||
|
||||
if (strongSelf->_intent == TGMediaAssetsControllerSendMediaIntent) {
|
||||
[strongSelf setTitle:TGLocalized(@"Attachment.Gallery")];
|
||||
} else {
|
||||
[strongSelf setTitle:assetGroup.title];
|
||||
}
|
||||
return [strongSelf->_assetsLibrary assetsOfAssetGroup:assetGroup reversed:false];
|
||||
}] deliverOn:[SQueue mainQueue]] startWithNext:^(id next)
|
||||
{
|
||||
|
|
@ -190,13 +200,16 @@
|
|||
{
|
||||
TGMediaAssetFetchResult *fetchResult = (TGMediaAssetFetchResult *)next;
|
||||
|
||||
bool scrollToBottom = (strongSelf->_fetchResult == nil);
|
||||
bool scrollToTop = (strongSelf->_fetchResult == nil && strongSelf->_intent == TGMediaAssetsControllerSendMediaIntent);
|
||||
bool scrollToBottom = (strongSelf->_fetchResult == nil && strongSelf->_intent != TGMediaAssetsControllerSendMediaIntent);
|
||||
|
||||
strongSelf->_fetchResult = fetchResult;
|
||||
[strongSelf->_collectionView reloadData];
|
||||
|
||||
if (scrollToBottom)
|
||||
{
|
||||
|
||||
if (scrollToTop) {
|
||||
[strongSelf->_collectionView layoutSubviews];
|
||||
[strongSelf->_collectionView setContentOffset:CGPointMake(0.0, -strongSelf->_collectionView.contentInset.top) animated:false];
|
||||
} else if (scrollToBottom) {
|
||||
[strongSelf->_collectionView layoutSubviews];
|
||||
[strongSelf _adjustContentOffsetToBottom];
|
||||
}
|
||||
|
|
@ -219,6 +232,7 @@
|
|||
[super viewWillAppear:animated];
|
||||
|
||||
[self setup3DTouch];
|
||||
[self setLeftBarButtonItem:[(TGMediaAssetsController *)self.navigationController leftBarButtonItem]];
|
||||
[self setRightBarButtonItem:[(TGMediaAssetsController *)self.navigationController rightBarButtonItem]];
|
||||
}
|
||||
|
||||
|
|
@ -369,6 +383,8 @@
|
|||
|
||||
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
[self.view.window endEditing:true];
|
||||
|
||||
TGMediaAsset *asset = [self _itemAtIndexPath:indexPath];
|
||||
|
||||
TGMediaSelectionContext *selectionContext = ((TGMediaAssetsController *)self.navigationController).selectionContext;
|
||||
|
|
|
|||
|
|
@ -406,9 +406,34 @@
|
|||
return _forcedCaption.string.length > 0;
|
||||
}
|
||||
|
||||
- (SSignal *)forcedCaption
|
||||
{
|
||||
__weak TGMediaEditingContext *weakSelf = self;
|
||||
SSignal *updateSignal = [_captionPipe.signalProducer() map:^NSAttributedString *(TGMediaCaptionUpdate *update)
|
||||
{
|
||||
__strong TGMediaEditingContext *strongSelf = weakSelf;
|
||||
if (strongSelf.isForcedCaption) {
|
||||
return strongSelf->_forcedCaption;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}];
|
||||
|
||||
return [[SSignal single:_forcedCaption] then:updateSignal];
|
||||
}
|
||||
|
||||
- (void)setForcedCaption:(NSAttributedString *)caption
|
||||
{
|
||||
[self setForcedCaption:caption skipUpdate:false];
|
||||
}
|
||||
|
||||
- (void)setForcedCaption:(NSAttributedString *)caption skipUpdate:(bool)skipUpdate
|
||||
{
|
||||
_forcedCaption = caption;
|
||||
|
||||
if (!skipUpdate) {
|
||||
_captionPipe.sink([TGMediaCaptionUpdate captionUpdateWithItem:nil caption:caption]);
|
||||
}
|
||||
}
|
||||
|
||||
- (SSignal *)captionSignalForItem:(NSObject<TGMediaEditableItem> *)item
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,15 +0,0 @@
|
|||
#import <LegacyComponents/TGModernConversationAssociatedInputPanel.h>
|
||||
|
||||
@class TGUser;
|
||||
@class TGConversationAssociatedInputPanelPallete;
|
||||
|
||||
@interface TGMentionPanelCell : UITableViewCell
|
||||
|
||||
@property (nonatomic, strong) TGUser *user;
|
||||
@property (nonatomic, strong) TGConversationAssociatedInputPanelPallete *pallete;
|
||||
|
||||
- (instancetype)initWithStyle:(TGModernConversationAssociatedInputPanelStyle)style;
|
||||
|
||||
@end
|
||||
|
||||
extern NSString *const TGMentionPanelCellKind;
|
||||
|
|
@ -1,175 +0,0 @@
|
|||
#import "TGMentionPanelCell.h"
|
||||
|
||||
#import "LegacyComponentsInternal.h"
|
||||
#import "TGColor.h"
|
||||
#import "TGFont.h"
|
||||
#import "TGUser.h"
|
||||
#import "TGImageUtils.h"
|
||||
|
||||
#import "TGLetteredAvatarView.h"
|
||||
#import "TGModernConversationAssociatedInputPanel.h"
|
||||
|
||||
NSString *const TGMentionPanelCellKind = @"TGMentionPanelCell";
|
||||
|
||||
@interface TGMentionPanelCell ()
|
||||
{
|
||||
TGModernConversationAssociatedInputPanelStyle _style;
|
||||
TGLetteredAvatarView *_avatarView;
|
||||
UILabel *_nameLabel;
|
||||
UILabel *_usernameLabel;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TGMentionPanelCell
|
||||
|
||||
- (instancetype)initWithStyle:(TGModernConversationAssociatedInputPanelStyle)style
|
||||
{
|
||||
self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TGMentionPanelCellKind];
|
||||
if (self != nil)
|
||||
{
|
||||
_style = style;
|
||||
|
||||
UIColor *backgroundColor = [UIColor whiteColor];
|
||||
UIColor *nameColor = [UIColor blackColor];
|
||||
UIColor *usernameColor = [UIColor blackColor];
|
||||
UIColor *selectionColor = TGSelectionColor();
|
||||
|
||||
if (style == TGModernConversationAssociatedInputPanelDarkStyle)
|
||||
{
|
||||
backgroundColor = UIColorRGB(0x171717);
|
||||
nameColor = [UIColor whiteColor];
|
||||
usernameColor = UIColorRGB(0x828282);
|
||||
selectionColor = UIColorRGB(0x292929);
|
||||
}
|
||||
else if (style == TGModernConversationAssociatedInputPanelDarkBlurredStyle)
|
||||
{
|
||||
backgroundColor = [UIColor clearColor];
|
||||
nameColor = [UIColor whiteColor];
|
||||
usernameColor = UIColorRGB(0x828282);
|
||||
selectionColor = UIColorRGB(0x3d3d3d);
|
||||
}
|
||||
|
||||
self.backgroundColor = backgroundColor;
|
||||
self.backgroundView = [[UIView alloc] init];
|
||||
self.backgroundView.backgroundColor = backgroundColor;
|
||||
self.backgroundView.opaque = false;
|
||||
|
||||
self.selectedBackgroundView = [[UIView alloc] init];
|
||||
self.selectedBackgroundView.backgroundColor = selectionColor;
|
||||
|
||||
_avatarView = [[TGLetteredAvatarView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 32.0f, 32.0f)];
|
||||
[_avatarView setSingleFontSize:16.0f doubleFontSize:16.0f useBoldFont:false];
|
||||
_avatarView.fadeTransition = true;
|
||||
[self.contentView addSubview:_avatarView];
|
||||
|
||||
_nameLabel = [[UILabel alloc] init];
|
||||
_nameLabel.backgroundColor = [UIColor clearColor];
|
||||
_nameLabel.textColor = nameColor;
|
||||
_nameLabel.font = TGMediumSystemFontOfSize(14.0f);
|
||||
_nameLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
[self.contentView addSubview:_nameLabel];
|
||||
|
||||
_usernameLabel = [[UILabel alloc] init];
|
||||
_usernameLabel.backgroundColor = [UIColor clearColor];
|
||||
_usernameLabel.textColor = usernameColor;
|
||||
_usernameLabel.font = TGSystemFontOfSize(14.0f);
|
||||
_usernameLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
[self.contentView addSubview:_usernameLabel];
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setPallete:(TGConversationAssociatedInputPanelPallete *)pallete
|
||||
{
|
||||
if (pallete == nil || _pallete == pallete)
|
||||
return;
|
||||
|
||||
_pallete = pallete;
|
||||
|
||||
_nameLabel.textColor = pallete.textColor;
|
||||
_usernameLabel.textColor = pallete.textColor;
|
||||
|
||||
self.backgroundColor = pallete.backgroundColor;
|
||||
self.backgroundView.backgroundColor = self.backgroundColor;
|
||||
self.selectedBackgroundView.backgroundColor = pallete.selectionColor;
|
||||
}
|
||||
|
||||
- (void)setUser:(TGUser *)user
|
||||
{
|
||||
_user = user;
|
||||
|
||||
_nameLabel.text = user.displayName;
|
||||
_usernameLabel.text = user.userName.length == 0 ? @"" : [[NSString alloc] initWithFormat:@"@%@", user.userName];
|
||||
|
||||
NSString *avatarUrl = user.photoFullUrlSmall;
|
||||
|
||||
CGFloat diameter = 32.0f;
|
||||
|
||||
static UIImage *staticPlaceholder = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^
|
||||
{
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), false, 0.0f);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
//!placeholder
|
||||
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
|
||||
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter));
|
||||
CGContextSetStrokeColorWithColor(context, UIColorRGB(0xd9d9d9).CGColor);
|
||||
CGContextSetLineWidth(context, 1.0f);
|
||||
CGContextStrokeEllipseInRect(context, CGRectMake(0.5f, 0.5f, diameter - 1.0f, diameter - 1.0f));
|
||||
|
||||
staticPlaceholder = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
});
|
||||
|
||||
UIImage *placeholder = staticPlaceholder;
|
||||
if (self.pallete != nil)
|
||||
placeholder = self.pallete.avatarPlaceholder;
|
||||
|
||||
if (avatarUrl.length != 0)
|
||||
{
|
||||
_avatarView.fadeTransitionDuration = 0.3;
|
||||
if (![avatarUrl isEqualToString:_avatarView.currentUrl])
|
||||
[_avatarView loadImage:avatarUrl filter:@"circle:32x32" placeholder:placeholder];
|
||||
}
|
||||
else
|
||||
{
|
||||
[_avatarView loadUserPlaceholderWithSize:CGSizeMake(diameter, diameter) uid:user.uid firstName:user.firstName lastName:user.lastName placeholder:placeholder];
|
||||
}
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
CGSize boundsSize = self.bounds.size;
|
||||
|
||||
_avatarView.frame = CGRectMake(7.0f + TGRetinaPixel, TGRetinaFloor((boundsSize.height - _avatarView.frame.size.height) / 2.0f), _avatarView.frame.size.width, _avatarView.frame.size.height);
|
||||
|
||||
CGFloat leftInset = 51.0f;
|
||||
CGFloat spacing = 6.0f;
|
||||
CGFloat rightInset = 6.0f;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
CGSize nameSize = [_nameLabel.text sizeWithFont:_nameLabel.font];
|
||||
#pragma clang diagnostic pop
|
||||
nameSize.width = CGCeil(MIN((boundsSize.width - leftInset - rightInset) * 3.0f / 4.0f, nameSize.width));
|
||||
nameSize.height = CGCeil(nameSize.height);
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
CGSize usernameSize = [_usernameLabel.text sizeWithFont:_usernameLabel.font];
|
||||
#pragma clang diagnostic pop
|
||||
usernameSize.width = CGCeil(MIN(boundsSize.width - leftInset - rightInset - nameSize.width - spacing, usernameSize.width));
|
||||
|
||||
_nameLabel.frame = CGRectMake(leftInset, CGFloor((boundsSize.height - nameSize.height) / 2.0f), nameSize.width, nameSize.height);
|
||||
_usernameLabel.frame = CGRectMake(leftInset + nameSize.width + spacing, CGFloor((boundsSize.height - usernameSize.height) / 2.0f), usernameSize.width, usernameSize.height);
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -1,488 +0,0 @@
|
|||
#import "TGModernConversationAlphacodeAssociatedPanel.h"
|
||||
|
||||
#import "LegacyComponentsInternal.h"
|
||||
#import "TGColor.h"
|
||||
#import "TGImageUtils.h"
|
||||
#import "TGFont.h"
|
||||
#import "TGViewController.h"
|
||||
|
||||
#import "TGAlphacodePanelCell.h"
|
||||
|
||||
#import "TGAlphacode.h"
|
||||
|
||||
@interface TGModernConversationAlphacodeAssociatedPanel () <UITableViewDelegate, UITableViewDataSource>
|
||||
{
|
||||
SMetaDisposable *_disposable;
|
||||
NSArray *_codeList;
|
||||
|
||||
UIView *_backgroundView;
|
||||
UIView *_effectView;
|
||||
|
||||
UITableView *_tableView;
|
||||
UIView *_stripeView;
|
||||
UIView *_separatorView;
|
||||
|
||||
UIView *_bottomView;
|
||||
UIView *_tableViewBackground;
|
||||
UIView *_tableViewSeparator;
|
||||
|
||||
bool _resetOffsetOnLayout;
|
||||
bool _animatingOut;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TGModernConversationAlphacodeAssociatedPanel
|
||||
|
||||
- (instancetype)initWithStyle:(TGModernConversationAssociatedInputPanelStyle)style
|
||||
{
|
||||
self = [super initWithStyle:style];
|
||||
if (self != nil)
|
||||
{
|
||||
_disposable = [[SMetaDisposable alloc] init];
|
||||
|
||||
UIColor *backgroundColor = [UIColor whiteColor];
|
||||
UIColor *bottomColor = UIColorRGBA(0xfafafa, 0.98f);
|
||||
UIColor *separatorColor = UIColorRGB(0xc5c7d0);
|
||||
UIColor *cellSeparatorColor = UIColorRGB(0xdbdbdb);
|
||||
|
||||
self.clipsToBounds = true;
|
||||
|
||||
if (self.style == TGModernConversationAssociatedInputPanelDarkStyle)
|
||||
{
|
||||
backgroundColor = UIColorRGB(0x171717);
|
||||
bottomColor = backgroundColor;
|
||||
separatorColor = UIColorRGB(0x292929);
|
||||
cellSeparatorColor = separatorColor;
|
||||
}
|
||||
else if (self.style == TGModernConversationAssociatedInputPanelDarkBlurredStyle)
|
||||
{
|
||||
backgroundColor = [UIColor clearColor];
|
||||
separatorColor = UIColorRGBA(0xb2b2b2, 0.7f);
|
||||
cellSeparatorColor = UIColorRGBA(0xb2b2b2, 0.4f);
|
||||
bottomColor = [UIColor clearColor];
|
||||
|
||||
CGFloat backgroundAlpha = 0.8f;
|
||||
if (iosMajorVersion() >= 8)
|
||||
{
|
||||
UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
|
||||
blurEffectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
blurEffectView.frame = self.bounds;
|
||||
[self addSubview:blurEffectView];
|
||||
_effectView = blurEffectView;
|
||||
|
||||
backgroundAlpha = 0.4f;
|
||||
}
|
||||
|
||||
_backgroundView = [[UIView alloc] initWithFrame:self.bounds];
|
||||
_backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
_backgroundView.backgroundColor = [UIColor colorWithWhite:0.0f alpha:backgroundAlpha];
|
||||
[self addSubview:_backgroundView];
|
||||
}
|
||||
|
||||
_bottomView = [[UIView alloc] init];
|
||||
_bottomView.backgroundColor = bottomColor;
|
||||
[self addSubview:_bottomView];
|
||||
|
||||
_tableViewBackground = [[UIView alloc] init];
|
||||
_tableViewBackground.backgroundColor = backgroundColor;
|
||||
[self addSubview:_tableViewBackground];
|
||||
|
||||
_tableView = [[UITableView alloc] init];
|
||||
_tableView.delegate = self;
|
||||
_tableView.dataSource = self;
|
||||
_tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
|
||||
_tableView.tableFooterView = [[UIView alloc] init];
|
||||
if (iosMajorVersion() >= 7)
|
||||
{
|
||||
_tableView.separatorColor = cellSeparatorColor;
|
||||
_tableView.separatorInset = UIEdgeInsetsMake(0.0f, 40.0f, 0.0f, 0.0f);
|
||||
}
|
||||
_tableView.backgroundColor = nil;
|
||||
_tableView.opaque = false;
|
||||
_tableView.showsVerticalScrollIndicator = false;
|
||||
_tableView.showsHorizontalScrollIndicator = false;
|
||||
|
||||
[self addSubview:_tableView];
|
||||
|
||||
_tableViewSeparator = [[UIView alloc] init];
|
||||
_tableViewSeparator.backgroundColor = separatorColor;
|
||||
[self addSubview:_tableViewSeparator];
|
||||
|
||||
_stripeView = [[UIView alloc] init];
|
||||
_stripeView.backgroundColor = separatorColor;
|
||||
[self addSubview:_stripeView];
|
||||
|
||||
if (self.style != TGModernConversationAssociatedInputPanelDarkBlurredStyle)
|
||||
{
|
||||
_separatorView = [[UIView alloc] init];
|
||||
_separatorView.backgroundColor = separatorColor;
|
||||
[self addSubview:_separatorView];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[_disposable dispose];
|
||||
}
|
||||
|
||||
- (void)setPallete:(TGConversationAssociatedInputPanelPallete *)pallete
|
||||
{
|
||||
[super setPallete:pallete];
|
||||
if (self.pallete == nil)
|
||||
return;
|
||||
|
||||
_bottomView.backgroundColor = pallete.barBackgroundColor;
|
||||
_tableViewBackground.backgroundColor = pallete.backgroundColor;
|
||||
_tableViewSeparator.backgroundColor = pallete.barSeparatorColor;
|
||||
_tableView.separatorColor = pallete.separatorColor;
|
||||
_stripeView.backgroundColor = pallete.barSeparatorColor;
|
||||
_separatorView.backgroundColor = pallete.barSeparatorColor;
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)frame
|
||||
{
|
||||
[super setFrame:frame];
|
||||
|
||||
self.alpha = frame.size.height >= FLT_EPSILON;
|
||||
}
|
||||
|
||||
- (bool)fillsAvailableSpace {
|
||||
return true;//iosMajorVersion() >= 9;
|
||||
}
|
||||
|
||||
- (CGFloat)preferredHeight {
|
||||
return [self preferredHeightAndOverlayHeight:NULL];
|
||||
}
|
||||
|
||||
- (CGFloat)preferredHeightAndOverlayHeight:(CGFloat *)overlayHeight
|
||||
{
|
||||
CGFloat height = 0.0f;
|
||||
CGFloat lastHeight = 0.0f;
|
||||
NSInteger lastIndex = MIN([TGViewController isWidescreen] ? 4 : 3, (NSInteger)_codeList.count - 1);
|
||||
for (NSInteger i = 0; i <= lastIndex; i++) {
|
||||
CGFloat rowHeight = [self tableView:_tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
|
||||
if (i == lastIndex) {
|
||||
lastHeight = rowHeight;
|
||||
} else {
|
||||
height += rowHeight;
|
||||
}
|
||||
}
|
||||
|
||||
CGFloat completeHeight = 0.0f;
|
||||
for (NSInteger i = 0; i < (NSInteger)_codeList.count; i++) {
|
||||
CGFloat rowHeight = [self tableView:_tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
|
||||
completeHeight += rowHeight;
|
||||
}
|
||||
|
||||
CGFloat maxHeight = CGFloor(self.frame.size.height * 2.0f / 3.0f);
|
||||
|
||||
height = completeHeight;
|
||||
|
||||
CGFloat overlayHeightValue = 0.0f;
|
||||
if (height + self.barInset < self.frame.size.height) {
|
||||
overlayHeightValue = self.barInset;
|
||||
}
|
||||
|
||||
if (overlayHeight) {
|
||||
*overlayHeight = overlayHeightValue;
|
||||
}
|
||||
|
||||
height += overlayHeightValue;
|
||||
|
||||
if (lastIndex > 0) {
|
||||
return MIN(maxHeight, CGFloor(height));
|
||||
} else {
|
||||
return MIN(maxHeight, height);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setAlphacodeListSignal:(SSignal *)alphacodeListSignal
|
||||
{
|
||||
if (alphacodeListSignal == nil)
|
||||
{
|
||||
[_disposable setDisposable:nil];
|
||||
[self setCodeList:@[]];
|
||||
}
|
||||
else
|
||||
{
|
||||
__weak TGModernConversationAlphacodeAssociatedPanel *weakSelf = self;
|
||||
[_disposable setDisposable:[[alphacodeListSignal deliverOn:[SQueue mainQueue]] startWithNext:^(NSArray *userList)
|
||||
{
|
||||
__strong TGModernConversationAlphacodeAssociatedPanel *strongSelf = weakSelf;
|
||||
if (strongSelf != nil)
|
||||
[strongSelf setCodeList:userList];
|
||||
}]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setCodeList:(NSArray *)codeList
|
||||
{
|
||||
bool wasEmpty = _codeList.count == 0;
|
||||
_codeList = codeList;
|
||||
|
||||
if (iosMajorVersion() >= 7) {
|
||||
_tableView.separatorStyle = _codeList.count <= 1 ? UITableViewCellSeparatorStyleNone : UITableViewCellSeparatorStyleSingleLine;
|
||||
}
|
||||
|
||||
[_tableView reloadData];
|
||||
|
||||
[self setNeedsPreferredHeightUpdate];
|
||||
|
||||
_stripeView.hidden = _codeList.count == 0;
|
||||
_separatorView.hidden = _codeList.count == 0;
|
||||
_bottomView.hidden = _codeList.count == 0;
|
||||
|
||||
[self scrollViewDidScroll:_tableView];
|
||||
|
||||
if (_codeList.count != 0 && wasEmpty) {
|
||||
[self animateIn];
|
||||
} else {
|
||||
[self layoutSubviews];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)__unused tableView numberOfRowsInSection:(NSInteger)__unused section
|
||||
{
|
||||
return _codeList.count;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)__unused tableView heightForRowAtIndexPath:(NSIndexPath *)__unused indexPath {
|
||||
return 41.0f;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
TGAlphacodePanelCell *cell = (TGAlphacodePanelCell *)[tableView dequeueReusableCellWithIdentifier:TGAlphacodePanelCellKind];
|
||||
if (cell == nil)
|
||||
cell = [[TGAlphacodePanelCell alloc] initWithStyle:self.style];
|
||||
cell.pallete = self.pallete;
|
||||
|
||||
TGAlphacodeEntry *entry = _codeList[indexPath.row];
|
||||
[cell setEmoji:entry.emoji label:entry.code];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)__unused tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
TGAlphacodeEntry *entry = _codeList[indexPath.row];
|
||||
if (_alphacodeSelected) {
|
||||
_alphacodeSelected(entry);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
||||
if (scrollView == _tableView) {
|
||||
[self updateTableBackground];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
if (_animatingOut) {
|
||||
return;
|
||||
}
|
||||
|
||||
_backgroundView.frame = CGRectMake(-1000, 0, self.frame.size.width + 2000, self.frame.size.height);
|
||||
_effectView.frame = CGRectMake(-1000, 0, self.frame.size.width + 2000, self.frame.size.height);
|
||||
|
||||
CGFloat separatorHeight = TGScreenPixel;
|
||||
_separatorView.frame = CGRectMake(0.0f, self.frame.size.height - separatorHeight, self.frame.size.width, separatorHeight);
|
||||
|
||||
UIEdgeInsets previousInset = _tableView.contentInset;
|
||||
|
||||
_tableView.frame = CGRectMake(0.0f, 0.0f, self.frame.size.width, self.frame.size.height);
|
||||
|
||||
if ([self fillsAvailableSpace]) {
|
||||
CGFloat overlayHeight = 0.0;
|
||||
CGFloat preferredHeight = [self preferredHeightAndOverlayHeight:&overlayHeight];
|
||||
|
||||
CGFloat topInset = MAX(0.0f, self.frame.size.height - preferredHeight);
|
||||
CGFloat insetDifference = topInset - _tableView.contentInset.top;
|
||||
UIEdgeInsets finalInset = UIEdgeInsetsMake(topInset, 0.0f, MAX(0.0f, overlayHeight - 1.0f / TGScreenScaling()), 0.0f);
|
||||
|
||||
if (_resetOffsetOnLayout) {
|
||||
_resetOffsetOnLayout = false;
|
||||
_tableView.contentInset = finalInset;
|
||||
[_tableView setContentOffset:CGPointMake(0.0f, -_tableView.contentInset.top) animated:false];
|
||||
} else if (ABS(insetDifference) > FLT_EPSILON) {
|
||||
//if (ABS(insetDifference) <= 36.0f + 0.1) {
|
||||
{
|
||||
[self _autoAdjustInsetsForScrollView:_tableView finalInset:finalInset previousInset:previousInset];
|
||||
|
||||
//contentOffset.y -= insetDifference;
|
||||
//_tableView.contentOffset = contentOffset;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_tableView.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
_bottomView.frame = CGRectMake(0.0f, self.frame.size.height, self.frame.size.width, 4.0f);
|
||||
|
||||
[self updateTableBackground];
|
||||
}
|
||||
|
||||
- (void)_autoAdjustInsetsForScrollView:(UIScrollView *)scrollView finalInset:(UIEdgeInsets)finalInset previousInset:(UIEdgeInsets)previousInset
|
||||
{
|
||||
CGPoint contentOffset = scrollView.contentOffset;
|
||||
|
||||
scrollView.contentInset = finalInset;
|
||||
if (iosMajorVersion() <= 8 && scrollView.subviews.count != 0) {
|
||||
if ([NSStringFromClass([scrollView.subviews.firstObject class]) hasPrefix:@"UITableViewWra"]) {
|
||||
CGRect frame = scrollView.subviews.firstObject.frame;
|
||||
frame.origin = CGPointZero;
|
||||
scrollView.subviews.firstObject.frame = frame;
|
||||
}
|
||||
}
|
||||
|
||||
if (!UIEdgeInsetsEqualToEdgeInsets(previousInset, UIEdgeInsetsZero))
|
||||
{
|
||||
CGFloat maxOffset = scrollView.contentSize.height - (scrollView.frame.size.height - finalInset.bottom);
|
||||
|
||||
contentOffset.y += previousInset.top - finalInset.top;
|
||||
contentOffset.y = MAX(-finalInset.top, MIN(contentOffset.y, maxOffset));
|
||||
[scrollView setContentOffset:contentOffset animated:false];
|
||||
}
|
||||
else if (contentOffset.y < finalInset.top)
|
||||
{
|
||||
contentOffset.y = -finalInset.top;
|
||||
[scrollView setContentOffset:contentOffset animated:false];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateTableBackground {
|
||||
if (_animatingOut) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGFloat backgroundOriginY = MAX(0.0f, -_tableView.contentOffset.y);
|
||||
_tableViewBackground.frame = CGRectMake(0.0f, backgroundOriginY, self.frame.size.width, self.frame.size.height - backgroundOriginY);
|
||||
_tableViewSeparator.frame = CGRectMake(0.0f, backgroundOriginY - 0.5f, self.frame.size.width, 0.5f);
|
||||
|
||||
_tableView.scrollIndicatorInsets = UIEdgeInsetsMake(backgroundOriginY, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
self.overlayBarOffset = _tableView.contentOffset.y + _tableView.contentInset.top;
|
||||
if (self.updateOverlayBarOffset) {
|
||||
self.updateOverlayBarOffset(self.overlayBarOffset);
|
||||
}
|
||||
}
|
||||
|
||||
- (CGRect)tableBackgroundFrame {
|
||||
return _tableViewBackground.frame;
|
||||
}
|
||||
|
||||
- (bool)hasSelectedItem
|
||||
{
|
||||
return _tableView.indexPathForSelectedRow != nil;
|
||||
}
|
||||
|
||||
- (void)selectPreviousItem
|
||||
{
|
||||
if ([self tableView:_tableView numberOfRowsInSection:0] == 0)
|
||||
return;
|
||||
|
||||
NSIndexPath *newIndexPath = _tableView.indexPathForSelectedRow;
|
||||
|
||||
if (newIndexPath == nil)
|
||||
newIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
|
||||
else if (newIndexPath.row > 0)
|
||||
newIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row - 1 inSection:0];
|
||||
|
||||
if (_tableView.indexPathForSelectedRow != nil)
|
||||
[_tableView deselectRowAtIndexPath:_tableView.indexPathForSelectedRow animated:false];
|
||||
|
||||
if (newIndexPath != nil)
|
||||
[_tableView selectRowAtIndexPath:newIndexPath animated:false scrollPosition:UITableViewScrollPositionBottom];
|
||||
}
|
||||
|
||||
- (void)selectNextItem
|
||||
{
|
||||
if ([self tableView:_tableView numberOfRowsInSection:0] == 0)
|
||||
return;
|
||||
|
||||
NSIndexPath *newIndexPath = _tableView.indexPathForSelectedRow;
|
||||
|
||||
if (newIndexPath == nil)
|
||||
newIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
|
||||
else if (newIndexPath.row < [self tableView:_tableView numberOfRowsInSection:newIndexPath.section] - 1)
|
||||
newIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row + 1 inSection:0];
|
||||
|
||||
if (_tableView.indexPathForSelectedRow != nil)
|
||||
[_tableView deselectRowAtIndexPath:_tableView.indexPathForSelectedRow animated:false];
|
||||
|
||||
if (newIndexPath != nil)
|
||||
[_tableView selectRowAtIndexPath:newIndexPath animated:false scrollPosition:UITableViewScrollPositionBottom];
|
||||
}
|
||||
|
||||
- (void)commitSelectedItem
|
||||
{
|
||||
if ([self tableView:_tableView numberOfRowsInSection:0] == 0)
|
||||
return;
|
||||
|
||||
NSIndexPath *selectedIndexPath = _tableView.indexPathForSelectedRow;
|
||||
if (selectedIndexPath == nil)
|
||||
selectedIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
|
||||
|
||||
[self tableView:_tableView didSelectRowAtIndexPath:selectedIndexPath];
|
||||
}
|
||||
|
||||
- (void)animateIn {
|
||||
[self layoutSubviews];
|
||||
CGFloat offset = [self preferredHeight];
|
||||
CGRect normalFrame = _tableView.frame;
|
||||
_tableView.frame = CGRectMake(normalFrame.origin.x, normalFrame.origin.y + offset, normalFrame.size.width, normalFrame.size.height);
|
||||
CGRect normalBackgroundFrame = _tableViewBackground.frame;
|
||||
_tableViewBackground.frame = CGRectMake(normalBackgroundFrame.origin.x, normalBackgroundFrame.origin.y + offset, normalBackgroundFrame.size.width, normalBackgroundFrame.size.height);
|
||||
CGRect normalSeparatorFrame = _tableViewSeparator.frame;
|
||||
_tableViewSeparator.frame = CGRectMake(normalSeparatorFrame.origin.x, normalSeparatorFrame.origin.y + offset, normalSeparatorFrame.size.width, normalSeparatorFrame.size.height);
|
||||
[UIView animateWithDuration:0.3 delay:0.0 options:7 << 16 animations:^{
|
||||
_tableView.frame = normalFrame;
|
||||
_tableViewBackground.frame = normalBackgroundFrame;
|
||||
_tableViewSeparator.frame = normalSeparatorFrame;
|
||||
} completion:nil];
|
||||
}
|
||||
|
||||
- (void)animateOut:(void (^)())completion {
|
||||
CGFloat offset = self.frame.size.height - _tableViewBackground.frame.origin.y;
|
||||
CGRect normalFrame = _tableView.frame;
|
||||
CGRect normalBackgroundFrame = _tableViewBackground.frame;
|
||||
CGRect normalSeparatorFrame = _tableViewSeparator.frame;
|
||||
_animatingOut = true;
|
||||
|
||||
[UIView animateWithDuration:0.15 delay:0.0 options:0 animations:^{
|
||||
_tableView.frame = CGRectMake(normalFrame.origin.x, normalFrame.origin.y + offset, normalFrame.size.width, normalFrame.size.height);
|
||||
_tableViewBackground.frame = CGRectMake(normalBackgroundFrame.origin.x, normalBackgroundFrame.origin.y + offset, normalBackgroundFrame.size.width, normalBackgroundFrame.size.height);
|
||||
_tableViewSeparator.frame = CGRectMake(normalSeparatorFrame.origin.x, normalSeparatorFrame.origin.y + offset, normalSeparatorFrame.size.width, normalSeparatorFrame.size.height);
|
||||
} completion:^(__unused BOOL finished) {
|
||||
completion();
|
||||
}];
|
||||
}
|
||||
|
||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
|
||||
if (CGRectContainsPoint(_tableViewBackground.frame, point)) {
|
||||
return [super hitTest:point withEvent:event];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)setBarInset:(CGFloat)barInset animated:(bool)animated {
|
||||
if (ABS(barInset - self.barInset) > FLT_EPSILON) {
|
||||
[super setBarInset:barInset animated:animated];
|
||||
|
||||
if (animated) {
|
||||
[self layoutSubviews];
|
||||
} else {
|
||||
[UIView animateWithDuration:0.3 animations:^{
|
||||
[self layoutSubviews];
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
#import "TGModernConversationAssociatedInputPanel.h"
|
||||
|
||||
@implementation TGModernConversationAssociatedInputPanel
|
||||
|
||||
- (instancetype)initWithStyle:(TGModernConversationAssociatedInputPanelStyle)style
|
||||
{
|
||||
_style = style;
|
||||
return [self initWithFrame:CGRectZero];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil)
|
||||
{
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CGFloat)preferredHeight
|
||||
{
|
||||
return 75.0f;
|
||||
}
|
||||
|
||||
- (bool)displayForTextEntryOnly {
|
||||
return false;
|
||||
}
|
||||
|
||||
- (bool)fillsAvailableSpace {
|
||||
return false;
|
||||
}
|
||||
|
||||
- (void)setNeedsPreferredHeightUpdate
|
||||
{
|
||||
if (_preferredHeightUpdated)
|
||||
_preferredHeightUpdated();
|
||||
}
|
||||
|
||||
- (void)setSendAreaWidth:(CGFloat)__unused sendAreaWidth attachmentAreaWidth:(CGFloat)__unused attachmentAreaWidth
|
||||
{
|
||||
}
|
||||
|
||||
- (void)setContentAreaHeight:(CGFloat)__unused contentAreaHeight {
|
||||
}
|
||||
|
||||
- (bool)hasSelectedItem
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
- (void)selectPreviousItem
|
||||
{
|
||||
}
|
||||
|
||||
- (void)selectNextItem
|
||||
{
|
||||
}
|
||||
|
||||
- (void)commitSelectedItem
|
||||
{
|
||||
}
|
||||
|
||||
- (void)animateIn {
|
||||
}
|
||||
|
||||
- (void)animateOut:(void (^)())completion {
|
||||
if (completion) {
|
||||
completion();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setBarInset:(CGFloat)barInset {
|
||||
[self setBarInset:barInset animated:false];
|
||||
}
|
||||
|
||||
- (void)setBarInset:(CGFloat)barInset animated:(bool)__unused animated {
|
||||
_barInset = barInset;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation TGConversationAssociatedInputPanelPallete
|
||||
|
||||
+ (instancetype)palleteWithDark:(bool)dark backgroundColor:(UIColor *)backgroundColor separatorColor:(UIColor *)separatorColor selectionColor:(UIColor *)selectionColor barBackgroundColor:(UIColor *)barBackgroundColor barSeparatorColor:(UIColor *)barSeparatorColor textColor:(UIColor *)textColor secondaryTextColor:(UIColor *)secondaryTextColor accentColor:(UIColor *)accentColor placeholderBackgroundColor:(UIColor *)placeholderBackgroundColor placeholderIconColor:(UIColor *)placeholderIconColor avatarPlaceholder:(UIImage *)avatarPlaceholder closeIcon:(UIImage *)closeIcon largeCloseIcon:(UIImage *)largeCloseIcon
|
||||
{
|
||||
TGConversationAssociatedInputPanelPallete *pallete = [[TGConversationAssociatedInputPanelPallete alloc] init];
|
||||
pallete->_isDark = dark;
|
||||
pallete->_backgroundColor = backgroundColor;
|
||||
pallete->_separatorColor = separatorColor;
|
||||
pallete->_selectionColor = selectionColor;
|
||||
pallete->_barBackgroundColor = barBackgroundColor;
|
||||
pallete->_barSeparatorColor = barSeparatorColor;
|
||||
pallete->_textColor = textColor;
|
||||
pallete->_secondaryTextColor = secondaryTextColor;
|
||||
pallete->_accentColor = accentColor;
|
||||
pallete->_placeholderBackgroundColor = placeholderBackgroundColor;
|
||||
pallete->_placeholderIconColor = placeholderIconColor;
|
||||
pallete->_avatarPlaceholder = avatarPlaceholder;
|
||||
pallete->_closeIcon = closeIcon;
|
||||
pallete->_largeCloseIcon = largeCloseIcon;
|
||||
return pallete;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -1,267 +0,0 @@
|
|||
#import "TGModernConversationHashtagsAssociatedPanel.h"
|
||||
|
||||
#import "LegacyComponentsInternal.h"
|
||||
#import "TGImageUtils.h"
|
||||
|
||||
#import "TGHashtagPanelCell.h"
|
||||
|
||||
@interface TGModernConversationHashtagsAssociatedPanel () <UITableViewDelegate, UITableViewDataSource>
|
||||
{
|
||||
SMetaDisposable *_disposable;
|
||||
NSArray *_hashtagList;
|
||||
|
||||
UIView *_backgroundView;
|
||||
UIView *_effectView;
|
||||
|
||||
UITableView *_tableView;
|
||||
UIView *_stripeView;
|
||||
UIView *_separatorView;
|
||||
|
||||
UIView *_bottomView;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TGModernConversationHashtagsAssociatedPanel
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil)
|
||||
{
|
||||
_disposable = [[SMetaDisposable alloc] init];
|
||||
|
||||
UIColor *backgroundColor = [UIColor whiteColor];
|
||||
UIColor *bottomColor = UIColorRGBA(0xfafafa, 0.98f);
|
||||
UIColor *separatorColor = UIColorRGB(0xc5c7d0);
|
||||
UIColor *cellSeparatorColor = UIColorRGB(0xdbdbdb);
|
||||
|
||||
if (self.style == TGModernConversationAssociatedInputPanelDarkStyle)
|
||||
{
|
||||
backgroundColor = UIColorRGB(0x171717);
|
||||
bottomColor = backgroundColor;
|
||||
separatorColor = UIColorRGB(0x292929);
|
||||
cellSeparatorColor = separatorColor;
|
||||
}
|
||||
else if (self.style == TGModernConversationAssociatedInputPanelDarkBlurredStyle)
|
||||
{
|
||||
backgroundColor = [UIColor clearColor];
|
||||
bottomColor = [UIColor clearColor];
|
||||
separatorColor = UIColorRGBA(0xb2b2b2, 0.7f);
|
||||
cellSeparatorColor = separatorColor;
|
||||
|
||||
CGFloat backgroundAlpha = 0.8f;
|
||||
if (iosMajorVersion() >= 8)
|
||||
{
|
||||
UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
|
||||
blurEffectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
blurEffectView.frame = self.bounds;
|
||||
[self addSubview:blurEffectView];
|
||||
_effectView = blurEffectView;
|
||||
|
||||
backgroundAlpha = 0.4f;
|
||||
}
|
||||
|
||||
_backgroundView = [[UIView alloc] initWithFrame:self.bounds];
|
||||
_backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
_backgroundView.backgroundColor = [UIColor colorWithWhite:0.0f alpha:backgroundAlpha];
|
||||
[self addSubview:_backgroundView];
|
||||
}
|
||||
|
||||
self.backgroundColor = backgroundColor;
|
||||
|
||||
_bottomView = [[UIView alloc] init];
|
||||
_bottomView.backgroundColor = bottomColor;
|
||||
[self addSubview:_bottomView];
|
||||
|
||||
_tableView = [[UITableView alloc] init];
|
||||
if (@available(iOS 11.0, *)) {
|
||||
_tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
_tableView.delegate = self;
|
||||
_tableView.dataSource = self;
|
||||
_tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
|
||||
_tableView.tableFooterView = [[UIView alloc] init];
|
||||
if (iosMajorVersion() >= 7)
|
||||
{
|
||||
_tableView.separatorColor = cellSeparatorColor;
|
||||
_tableView.separatorInset = UIEdgeInsetsMake(0.0f, 15.0f, 0.0f, 0.0f);
|
||||
}
|
||||
_tableView.backgroundColor = nil;
|
||||
_tableView.rowHeight = 41.0f;
|
||||
_tableView.opaque = false;
|
||||
|
||||
[self addSubview:_tableView];
|
||||
|
||||
_stripeView = [[UIView alloc] init];
|
||||
_stripeView.backgroundColor = separatorColor;
|
||||
[self addSubview:_stripeView];
|
||||
|
||||
if (self.style != TGModernConversationAssociatedInputPanelDarkBlurredStyle)
|
||||
{
|
||||
_separatorView = [[UIView alloc] init];
|
||||
_separatorView.backgroundColor = separatorColor;
|
||||
[self addSubview:_separatorView];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[_disposable dispose];
|
||||
}
|
||||
|
||||
- (void)setPallete:(TGConversationAssociatedInputPanelPallete *)pallete
|
||||
{
|
||||
[super setPallete:pallete];
|
||||
if (self.pallete == nil)
|
||||
return;
|
||||
|
||||
self.backgroundColor = pallete.backgroundColor;
|
||||
_bottomView.backgroundColor = pallete.barBackgroundColor;
|
||||
_tableView.separatorColor = pallete.separatorColor;
|
||||
_stripeView.backgroundColor = pallete.barSeparatorColor;
|
||||
_separatorView.backgroundColor = pallete.barSeparatorColor;
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)frame
|
||||
{
|
||||
[super setFrame:frame];
|
||||
|
||||
self.alpha = frame.size.height >= FLT_EPSILON;
|
||||
}
|
||||
|
||||
- (CGFloat)preferredHeight
|
||||
{
|
||||
return 41.0f * MIN(3.5f, (CGFloat)_hashtagList.count);
|
||||
}
|
||||
|
||||
- (void)setHashtagListSignal:(SSignal *)hashtagListSignal
|
||||
{
|
||||
if (hashtagListSignal == nil)
|
||||
{
|
||||
[_disposable setDisposable:nil];
|
||||
[self setHashtagList:@[]];
|
||||
}
|
||||
else
|
||||
{
|
||||
__weak TGModernConversationHashtagsAssociatedPanel *weakSelf = self;
|
||||
[_disposable setDisposable:[hashtagListSignal startWithNext:^(NSArray *hashtagList)
|
||||
{
|
||||
__strong TGModernConversationHashtagsAssociatedPanel *strongSelf = weakSelf;
|
||||
if (strongSelf != nil)
|
||||
[strongSelf setHashtagList:hashtagList];
|
||||
}]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setHashtagList:(NSArray *)hashtagList
|
||||
{
|
||||
_hashtagList = hashtagList;
|
||||
|
||||
[_tableView reloadData];
|
||||
|
||||
[self setNeedsPreferredHeightUpdate];
|
||||
|
||||
_stripeView.hidden = hashtagList.count == 0;
|
||||
_separatorView.hidden = hashtagList.count == 0;
|
||||
_bottomView.hidden = hashtagList.count == 0;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)__unused tableView numberOfRowsInSection:(NSInteger)__unused section
|
||||
{
|
||||
return _hashtagList.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
TGHashtagPanelCell *cell = (TGHashtagPanelCell *)[tableView dequeueReusableCellWithIdentifier:TGHashtagPanelCellKind];
|
||||
if (cell == nil)
|
||||
cell = [[TGHashtagPanelCell alloc] initWithStyle:self.style];
|
||||
cell.pallete = self.pallete;
|
||||
|
||||
[cell setHashtag:_hashtagList[indexPath.row]];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)__unused tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
NSString *hashtag = _hashtagList[indexPath.row];
|
||||
if (_hashtagSelected)
|
||||
_hashtagSelected(hashtag);
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
_backgroundView.frame = CGRectMake(-1000, 0, self.frame.size.width + 2000, self.frame.size.height);
|
||||
_effectView.frame = CGRectMake(-1000, 0, self.frame.size.width + 2000, self.frame.size.height);
|
||||
|
||||
CGFloat separatorHeight = TGScreenPixel;
|
||||
_stripeView.frame = CGRectMake(0.0f, 0.0f, self.frame.size.width, separatorHeight);
|
||||
_separatorView.frame = CGRectMake(0.0f, self.frame.size.height - separatorHeight, self.frame.size.width, separatorHeight);
|
||||
|
||||
_tableView.frame = CGRectMake(0.0f, separatorHeight, self.frame.size.width, self.frame.size.height - separatorHeight);
|
||||
|
||||
_bottomView.frame = CGRectMake(0.0f, self.frame.size.height, self.frame.size.width, 4.0f);
|
||||
}
|
||||
|
||||
- (bool)hasSelectedItem
|
||||
{
|
||||
return _tableView.indexPathForSelectedRow != nil;
|
||||
}
|
||||
|
||||
- (void)selectPreviousItem
|
||||
{
|
||||
if ([self tableView:_tableView numberOfRowsInSection:0] == 0)
|
||||
return;
|
||||
|
||||
NSIndexPath *newIndexPath = _tableView.indexPathForSelectedRow;
|
||||
|
||||
if (newIndexPath == nil)
|
||||
newIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
|
||||
else if (newIndexPath.row > 0)
|
||||
newIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row - 1 inSection:0];
|
||||
|
||||
if (_tableView.indexPathForSelectedRow != nil)
|
||||
[_tableView deselectRowAtIndexPath:_tableView.indexPathForSelectedRow animated:false];
|
||||
|
||||
if (newIndexPath != nil)
|
||||
[_tableView selectRowAtIndexPath:newIndexPath animated:false scrollPosition:UITableViewScrollPositionBottom];
|
||||
}
|
||||
|
||||
- (void)selectNextItem
|
||||
{
|
||||
if ([self tableView:_tableView numberOfRowsInSection:0] == 0)
|
||||
return;
|
||||
|
||||
NSIndexPath *newIndexPath = _tableView.indexPathForSelectedRow;
|
||||
|
||||
if (newIndexPath == nil)
|
||||
newIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
|
||||
else if (newIndexPath.row < [self tableView:_tableView numberOfRowsInSection:newIndexPath.section] - 1)
|
||||
newIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row + 1 inSection:0];
|
||||
|
||||
if (_tableView.indexPathForSelectedRow != nil)
|
||||
[_tableView deselectRowAtIndexPath:_tableView.indexPathForSelectedRow animated:false];
|
||||
|
||||
if (newIndexPath != nil)
|
||||
[_tableView selectRowAtIndexPath:newIndexPath animated:false scrollPosition:UITableViewScrollPositionBottom];
|
||||
}
|
||||
|
||||
- (void)commitSelectedItem
|
||||
{
|
||||
if ([self tableView:_tableView numberOfRowsInSection:0] == 0)
|
||||
return;
|
||||
|
||||
NSIndexPath *selectedIndexPath = _tableView.indexPathForSelectedRow;
|
||||
if (selectedIndexPath == nil)
|
||||
selectedIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
|
||||
|
||||
[self tableView:_tableView didSelectRowAtIndexPath:selectedIndexPath];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -1,515 +0,0 @@
|
|||
#import "TGModernConversationMentionsAssociatedPanel.h"
|
||||
|
||||
#import "LegacyComponentsInternal.h"
|
||||
|
||||
#import <LegacyComponents/TGImageUtils.h>
|
||||
#import <LegacyComponents/TGFont.h>
|
||||
#import <LegacyComponents/TGViewController.h>
|
||||
|
||||
#import "TGMentionPanelCell.h"
|
||||
|
||||
@interface TGModernConversationMentionsAssociatedPanel () <UITableViewDelegate, UITableViewDataSource>
|
||||
{
|
||||
SMetaDisposable *_disposable;
|
||||
NSArray *_userList;
|
||||
|
||||
UIView *_backgroundView;
|
||||
UIView *_effectView;
|
||||
|
||||
UITableView *_tableView;
|
||||
UIView *_stripeView;
|
||||
UIView *_separatorView;
|
||||
|
||||
UIView *_bottomView;
|
||||
UIView *_tableViewBackground;
|
||||
UIView *_tableViewSeparator;
|
||||
|
||||
bool _resetOffsetOnLayout;
|
||||
bool _animatingOut;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TGModernConversationMentionsAssociatedPanel
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil)
|
||||
{
|
||||
_disposable = [[SMetaDisposable alloc] init];
|
||||
|
||||
UIColor *backgroundColor = [UIColor whiteColor];
|
||||
UIColor *bottomColor = UIColorRGBA(0xfafafa, 0.98f);
|
||||
UIColor *separatorColor = UIColorRGB(0xc5c7d0);
|
||||
UIColor *cellSeparatorColor = UIColorRGB(0xdbdbdb);
|
||||
|
||||
self.clipsToBounds = true;
|
||||
|
||||
if (self.style == TGModernConversationAssociatedInputPanelDarkStyle)
|
||||
{
|
||||
backgroundColor = UIColorRGB(0x171717);
|
||||
bottomColor = backgroundColor;
|
||||
separatorColor = UIColorRGB(0x292929);
|
||||
cellSeparatorColor = separatorColor;
|
||||
}
|
||||
else if (self.style == TGModernConversationAssociatedInputPanelDarkBlurredStyle)
|
||||
{
|
||||
backgroundColor = [UIColor clearColor];
|
||||
separatorColor = UIColorRGBA(0xb2b2b2, 0.7f);
|
||||
cellSeparatorColor = UIColorRGBA(0xb2b2b2, 0.4f);
|
||||
bottomColor = [UIColor clearColor];
|
||||
|
||||
CGFloat backgroundAlpha = 0.8f;
|
||||
if (iosMajorVersion() >= 8)
|
||||
{
|
||||
UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
|
||||
blurEffectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
blurEffectView.frame = self.bounds;
|
||||
[self addSubview:blurEffectView];
|
||||
_effectView = blurEffectView;
|
||||
|
||||
backgroundAlpha = 0.4f;
|
||||
}
|
||||
|
||||
_backgroundView = [[UIView alloc] initWithFrame:self.bounds];
|
||||
_backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
_backgroundView.backgroundColor = [UIColor colorWithWhite:0.0f alpha:backgroundAlpha];
|
||||
[self addSubview:_backgroundView];
|
||||
}
|
||||
|
||||
_bottomView = [[UIView alloc] init];
|
||||
_bottomView.backgroundColor = bottomColor;
|
||||
[self addSubview:_bottomView];
|
||||
|
||||
_tableViewBackground = [[UIView alloc] init];
|
||||
_tableViewBackground.backgroundColor = backgroundColor;
|
||||
[self addSubview:_tableViewBackground];
|
||||
|
||||
_tableView = [[UITableView alloc] init];
|
||||
if (@available(iOS 11.0, *)) {
|
||||
_tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
_tableView.delegate = self;
|
||||
_tableView.dataSource = self;
|
||||
_tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
|
||||
_tableView.tableFooterView = [[UIView alloc] init];
|
||||
if (iosMajorVersion() >= 7)
|
||||
{
|
||||
_tableView.separatorColor = cellSeparatorColor;
|
||||
_tableView.separatorInset = UIEdgeInsetsMake(0.0f, 52.0f, 0.0f, 0.0f);
|
||||
}
|
||||
_tableView.backgroundColor = nil;
|
||||
_tableView.opaque = false;
|
||||
_tableView.showsVerticalScrollIndicator = false;
|
||||
_tableView.showsHorizontalScrollIndicator = false;
|
||||
|
||||
[self addSubview:_tableView];
|
||||
|
||||
_tableViewSeparator = [[UIView alloc] init];
|
||||
_tableViewSeparator.backgroundColor = separatorColor;
|
||||
[self addSubview:_tableViewSeparator];
|
||||
|
||||
_stripeView = [[UIView alloc] init];
|
||||
_stripeView.backgroundColor = separatorColor;
|
||||
[self addSubview:_stripeView];
|
||||
|
||||
if (self.style != TGModernConversationAssociatedInputPanelDarkBlurredStyle)
|
||||
{
|
||||
_separatorView = [[UIView alloc] init];
|
||||
_separatorView.backgroundColor = separatorColor;
|
||||
[self addSubview:_separatorView];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[_disposable dispose];
|
||||
}
|
||||
|
||||
- (void)setPallete:(TGConversationAssociatedInputPanelPallete *)pallete
|
||||
{
|
||||
[super setPallete:pallete];
|
||||
if (self.pallete == nil)
|
||||
return;
|
||||
|
||||
_bottomView.backgroundColor = pallete.barBackgroundColor;
|
||||
_tableViewBackground.backgroundColor = pallete.backgroundColor;
|
||||
_tableViewSeparator.backgroundColor = pallete.barSeparatorColor;
|
||||
_tableView.separatorColor = pallete.separatorColor;
|
||||
_stripeView.backgroundColor = pallete.barSeparatorColor;
|
||||
_separatorView.backgroundColor = pallete.barSeparatorColor;
|
||||
}
|
||||
|
||||
- (void)setInverted:(bool)inverted {
|
||||
if (_inverted != inverted) {
|
||||
_inverted = inverted;
|
||||
|
||||
if (_inverted) {
|
||||
self.transform = CGAffineTransformMakeRotation((CGFloat)M_PI);
|
||||
} else {
|
||||
self.transform = CGAffineTransformIdentity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)frame
|
||||
{
|
||||
[super setFrame:frame];
|
||||
|
||||
self.alpha = frame.size.height >= FLT_EPSILON;
|
||||
}
|
||||
|
||||
- (bool)fillsAvailableSpace {
|
||||
return true;//iosMajorVersion() >= 9;
|
||||
}
|
||||
|
||||
- (CGFloat)preferredHeight {
|
||||
return [self preferredHeightAndOverlayHeight:NULL];
|
||||
}
|
||||
|
||||
- (CGFloat)preferredHeightAndOverlayHeight:(CGFloat *)overlayHeight
|
||||
{
|
||||
CGFloat height = 0.0f;
|
||||
CGFloat lastHeight = 0.0f;
|
||||
NSInteger lastIndex = MIN([TGViewController isWidescreen] ? 4 : 3, (NSInteger)_userList.count - 1);
|
||||
for (NSInteger i = 0; i <= lastIndex; i++) {
|
||||
CGFloat rowHeight = [self tableView:_tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
|
||||
if (i == lastIndex) {
|
||||
lastHeight = rowHeight;
|
||||
} else {
|
||||
height += rowHeight;
|
||||
}
|
||||
}
|
||||
|
||||
CGFloat completeHeight = 0.0f;
|
||||
for (NSInteger i = 0; i < (NSInteger)_userList.count; i++) {
|
||||
CGFloat rowHeight = [self tableView:_tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
|
||||
completeHeight += rowHeight;
|
||||
}
|
||||
|
||||
CGFloat maxHeight = CGFloor(self.frame.size.height * 2.0f / 3.0f);
|
||||
|
||||
height = completeHeight;
|
||||
|
||||
CGFloat overlayHeightValue = 0.0f;
|
||||
if (height + self.barInset < self.frame.size.height) {
|
||||
overlayHeightValue = self.barInset;
|
||||
}
|
||||
|
||||
if (overlayHeight) {
|
||||
*overlayHeight = overlayHeightValue;
|
||||
}
|
||||
|
||||
height += overlayHeightValue;
|
||||
|
||||
if (lastIndex > 0) {
|
||||
return MIN(maxHeight, CGFloor(height));
|
||||
} else {
|
||||
return MIN(maxHeight, height);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setUserListSignal:(SSignal *)userListSignal
|
||||
{
|
||||
if (userListSignal == nil)
|
||||
{
|
||||
[_disposable setDisposable:nil];
|
||||
[self setUserList:@[]];
|
||||
}
|
||||
else
|
||||
{
|
||||
__weak TGModernConversationMentionsAssociatedPanel *weakSelf = self;
|
||||
[_disposable setDisposable:[[userListSignal deliverOn:[SQueue mainQueue]] startWithNext:^(NSArray *userList)
|
||||
{
|
||||
__strong TGModernConversationMentionsAssociatedPanel *strongSelf = weakSelf;
|
||||
if (strongSelf != nil)
|
||||
[strongSelf setUserList:userList];
|
||||
}]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setUserList:(NSArray *)userList
|
||||
{
|
||||
bool wasEmpty = _userList.count == 0;
|
||||
_userList = userList;
|
||||
|
||||
if (iosMajorVersion() >= 7) {
|
||||
_tableView.separatorStyle = _userList.count <= 1 ? UITableViewCellSeparatorStyleNone : UITableViewCellSeparatorStyleSingleLine;
|
||||
}
|
||||
|
||||
[_tableView reloadData];
|
||||
|
||||
[self setNeedsPreferredHeightUpdate];
|
||||
|
||||
_stripeView.hidden = userList.count == 0;
|
||||
_separatorView.hidden = userList.count == 0 || _inverted;
|
||||
_bottomView.hidden = userList.count == 0;
|
||||
|
||||
[self scrollViewDidScroll:_tableView];
|
||||
|
||||
if (_userList.count != 0 && wasEmpty) {
|
||||
[self animateIn];
|
||||
} else {
|
||||
[self layoutSubviews];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)__unused tableView numberOfRowsInSection:(NSInteger)__unused section
|
||||
{
|
||||
return _userList.count;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)__unused tableView heightForRowAtIndexPath:(NSIndexPath *)__unused indexPath {
|
||||
return 41.0f;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
TGMentionPanelCell *cell = (TGMentionPanelCell *)[tableView dequeueReusableCellWithIdentifier:TGMentionPanelCellKind];
|
||||
if (cell == nil) {
|
||||
cell = [[TGMentionPanelCell alloc] initWithStyle:self.style];
|
||||
if (_inverted) {
|
||||
[UIView performWithoutAnimation:^{
|
||||
cell.transform = CGAffineTransformMakeRotation((CGFloat)M_PI);
|
||||
}];
|
||||
} else {
|
||||
cell.transform = CGAffineTransformIdentity;
|
||||
}
|
||||
}
|
||||
cell.pallete = self.pallete;
|
||||
if (iosMajorVersion() >= 7)
|
||||
{
|
||||
if (indexPath.row == 0 && _inverted) {
|
||||
cell.separatorInset = UIEdgeInsetsMake(0.0f, 2000.0f, 0.0f, 0.0f);
|
||||
} else {
|
||||
cell.separatorInset = tableView.separatorInset;
|
||||
}
|
||||
}
|
||||
|
||||
[cell setUser:_userList[indexPath.row]];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)__unused tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
TGUser *user = _userList[indexPath.row];
|
||||
if (_userSelected)
|
||||
_userSelected(user);
|
||||
}
|
||||
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
||||
if (scrollView == _tableView) {
|
||||
[self updateTableBackground];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
if (_animatingOut) {
|
||||
return;
|
||||
}
|
||||
|
||||
_backgroundView.frame = CGRectMake(-1000, 0, self.frame.size.width + 2000, self.frame.size.height);
|
||||
_effectView.frame = CGRectMake(-1000, 0, self.frame.size.width + 2000, self.frame.size.height);
|
||||
|
||||
CGFloat separatorHeight = TGScreenPixel;
|
||||
_separatorView.frame = CGRectMake(0.0f, self.frame.size.height - separatorHeight, self.frame.size.width, separatorHeight);
|
||||
|
||||
UIEdgeInsets previousInset = _tableView.contentInset;
|
||||
|
||||
_tableView.frame = CGRectMake(0.0f, 0.0f, self.frame.size.width, self.frame.size.height);
|
||||
|
||||
if ([self fillsAvailableSpace]) {
|
||||
CGFloat overlayHeight = 0.0;
|
||||
CGFloat preferredHeight = [self preferredHeightAndOverlayHeight:&overlayHeight];
|
||||
|
||||
CGFloat topInset = MAX(0.0f, self.frame.size.height - preferredHeight);
|
||||
CGFloat insetDifference = topInset - _tableView.contentInset.top;
|
||||
UIEdgeInsets finalInset = UIEdgeInsetsMake(topInset, 0.0f, MAX(0.0f, overlayHeight - 1.0f / TGScreenScaling()), 0.0f);
|
||||
|
||||
if (_resetOffsetOnLayout) {
|
||||
_resetOffsetOnLayout = false;
|
||||
_tableView.contentInset = finalInset;
|
||||
[_tableView setContentOffset:CGPointMake(0.0f, -_tableView.contentInset.top) animated:false];
|
||||
} else if (ABS(insetDifference) > FLT_EPSILON) {
|
||||
//if (ABS(insetDifference) <= 36.0f + 0.1) {
|
||||
{
|
||||
[self _autoAdjustInsetsForScrollView:_tableView finalInset:finalInset previousInset:previousInset];
|
||||
|
||||
//contentOffset.y -= insetDifference;
|
||||
//_tableView.contentOffset = contentOffset;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_tableView.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
_bottomView.frame = CGRectMake(0.0f, self.frame.size.height, self.frame.size.width, 4.0f);
|
||||
|
||||
[self updateTableBackground];
|
||||
}
|
||||
|
||||
- (void)_autoAdjustInsetsForScrollView:(UIScrollView *)scrollView finalInset:(UIEdgeInsets)finalInset previousInset:(UIEdgeInsets)previousInset
|
||||
{
|
||||
CGPoint contentOffset = scrollView.contentOffset;
|
||||
|
||||
scrollView.contentInset = finalInset;
|
||||
if (iosMajorVersion() <= 8 && scrollView.subviews.count != 0) {
|
||||
if ([NSStringFromClass([scrollView.subviews.firstObject class]) hasPrefix:@"UITableViewWra"]) {
|
||||
CGRect frame = scrollView.subviews.firstObject.frame;
|
||||
frame.origin = CGPointZero;
|
||||
scrollView.subviews.firstObject.frame = frame;
|
||||
}
|
||||
}
|
||||
|
||||
if (!UIEdgeInsetsEqualToEdgeInsets(previousInset, UIEdgeInsetsZero))
|
||||
{
|
||||
CGFloat maxOffset = scrollView.contentSize.height - (scrollView.frame.size.height - finalInset.bottom);
|
||||
|
||||
contentOffset.y += previousInset.top - finalInset.top;
|
||||
contentOffset.y = MAX(-finalInset.top, MIN(contentOffset.y, maxOffset));
|
||||
[scrollView setContentOffset:contentOffset animated:false];
|
||||
}
|
||||
else if (contentOffset.y < finalInset.top)
|
||||
{
|
||||
contentOffset.y = -finalInset.top;
|
||||
[scrollView setContentOffset:contentOffset animated:false];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateTableBackground {
|
||||
if (_animatingOut) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGFloat backgroundOriginY = MAX(0.0f, -_tableView.contentOffset.y);
|
||||
_tableViewBackground.frame = CGRectMake(0.0f, backgroundOriginY, self.frame.size.width, self.frame.size.height - backgroundOriginY);
|
||||
_tableViewSeparator.frame = CGRectMake(0.0f, backgroundOriginY - 0.5f, self.frame.size.width, 0.5f);
|
||||
|
||||
_tableView.scrollIndicatorInsets = UIEdgeInsetsMake(backgroundOriginY, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
self.overlayBarOffset = _tableView.contentOffset.y + _tableView.contentInset.top;
|
||||
if (self.updateOverlayBarOffset) {
|
||||
self.updateOverlayBarOffset(self.overlayBarOffset);
|
||||
}
|
||||
}
|
||||
|
||||
- (CGRect)tableBackgroundFrame {
|
||||
return _tableViewBackground.frame;
|
||||
}
|
||||
|
||||
- (bool)hasSelectedItem
|
||||
{
|
||||
return _tableView.indexPathForSelectedRow != nil;
|
||||
}
|
||||
|
||||
- (void)selectPreviousItem
|
||||
{
|
||||
if ([self tableView:_tableView numberOfRowsInSection:0] == 0)
|
||||
return;
|
||||
|
||||
NSIndexPath *newIndexPath = _tableView.indexPathForSelectedRow;
|
||||
|
||||
if (newIndexPath == nil)
|
||||
newIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
|
||||
else if (newIndexPath.row > 0)
|
||||
newIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row - 1 inSection:0];
|
||||
|
||||
if (_tableView.indexPathForSelectedRow != nil)
|
||||
[_tableView deselectRowAtIndexPath:_tableView.indexPathForSelectedRow animated:false];
|
||||
|
||||
if (newIndexPath != nil)
|
||||
[_tableView selectRowAtIndexPath:newIndexPath animated:false scrollPosition:UITableViewScrollPositionBottom];
|
||||
}
|
||||
|
||||
- (void)selectNextItem
|
||||
{
|
||||
if ([self tableView:_tableView numberOfRowsInSection:0] == 0)
|
||||
return;
|
||||
|
||||
NSIndexPath *newIndexPath = _tableView.indexPathForSelectedRow;
|
||||
|
||||
if (newIndexPath == nil)
|
||||
newIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
|
||||
else if (newIndexPath.row < [self tableView:_tableView numberOfRowsInSection:newIndexPath.section] - 1)
|
||||
newIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row + 1 inSection:0];
|
||||
|
||||
if (_tableView.indexPathForSelectedRow != nil)
|
||||
[_tableView deselectRowAtIndexPath:_tableView.indexPathForSelectedRow animated:false];
|
||||
|
||||
if (newIndexPath != nil)
|
||||
[_tableView selectRowAtIndexPath:newIndexPath animated:false scrollPosition:UITableViewScrollPositionBottom];
|
||||
}
|
||||
|
||||
- (void)commitSelectedItem
|
||||
{
|
||||
if ([self tableView:_tableView numberOfRowsInSection:0] == 0)
|
||||
return;
|
||||
|
||||
NSIndexPath *selectedIndexPath = _tableView.indexPathForSelectedRow;
|
||||
if (selectedIndexPath == nil)
|
||||
selectedIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
|
||||
|
||||
[self tableView:_tableView didSelectRowAtIndexPath:selectedIndexPath];
|
||||
}
|
||||
|
||||
- (void)animateIn {
|
||||
[self layoutSubviews];
|
||||
CGFloat offset = [self preferredHeight];
|
||||
CGRect normalFrame = _tableView.frame;
|
||||
_tableView.frame = CGRectMake(normalFrame.origin.x, normalFrame.origin.y + offset, normalFrame.size.width, normalFrame.size.height);
|
||||
CGRect normalBackgroundFrame = _tableViewBackground.frame;
|
||||
_tableViewBackground.frame = CGRectMake(normalBackgroundFrame.origin.x, normalBackgroundFrame.origin.y + offset, normalBackgroundFrame.size.width, normalBackgroundFrame.size.height);
|
||||
CGRect normalSeparatorFrame = _tableViewSeparator.frame;
|
||||
_tableViewSeparator.frame = CGRectMake(normalSeparatorFrame.origin.x, normalSeparatorFrame.origin.y + offset, normalSeparatorFrame.size.width, normalSeparatorFrame.size.height);
|
||||
[UIView animateWithDuration:0.3 delay:0.0 options:7 << 16 animations:^{
|
||||
_tableView.frame = normalFrame;
|
||||
_tableViewBackground.frame = normalBackgroundFrame;
|
||||
_tableViewSeparator.frame = normalSeparatorFrame;
|
||||
} completion:nil];
|
||||
}
|
||||
|
||||
- (void)animateOut:(void (^)())completion {
|
||||
CGFloat offset = self.frame.size.height - _tableViewBackground.frame.origin.y;
|
||||
CGRect normalFrame = _tableView.frame;
|
||||
CGRect normalBackgroundFrame = _tableViewBackground.frame;
|
||||
CGRect normalSeparatorFrame = _tableViewSeparator.frame;
|
||||
_animatingOut = true;
|
||||
|
||||
[UIView animateWithDuration:0.15 delay:0.0 options:0 animations:^{
|
||||
_tableView.frame = CGRectMake(normalFrame.origin.x, normalFrame.origin.y + offset, normalFrame.size.width, normalFrame.size.height);
|
||||
_tableViewBackground.frame = CGRectMake(normalBackgroundFrame.origin.x, normalBackgroundFrame.origin.y + offset, normalBackgroundFrame.size.width, normalBackgroundFrame.size.height);
|
||||
_tableViewSeparator.frame = CGRectMake(normalSeparatorFrame.origin.x, normalSeparatorFrame.origin.y + offset, normalSeparatorFrame.size.width, normalSeparatorFrame.size.height);
|
||||
} completion:^(__unused BOOL finished) {
|
||||
completion();
|
||||
}];
|
||||
}
|
||||
|
||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
|
||||
if (CGRectContainsPoint(_tableViewBackground.frame, point)) {
|
||||
return [super hitTest:point withEvent:event];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)setBarInset:(CGFloat)barInset animated:(bool)animated {
|
||||
if (ABS(barInset - self.barInset) > FLT_EPSILON) {
|
||||
[super setBarInset:barInset animated:animated];
|
||||
|
||||
if (animated) {
|
||||
[self layoutSubviews];
|
||||
} else {
|
||||
[UIView animateWithDuration:0.3 animations:^{
|
||||
[self layoutSubviews];
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -326,7 +326,7 @@ static id<TGNavigationBarMusicPlayerProvider> _musicPlayerProvider;
|
|||
}
|
||||
|
||||
[super setFrame:frame];
|
||||
|
||||
|
||||
if (_statusBarBackgroundView != nil && _statusBarBackgroundView.superview != nil)
|
||||
{
|
||||
_statusBarBackgroundView.frame = CGRectMake(0, -self.frame.origin.y, self.frame.size.width, 20);
|
||||
|
|
|
|||
|
|
@ -7,10 +7,6 @@
|
|||
#import "TGSuggestionContext.h"
|
||||
#import "TGPhotoPaintStickersContext.h"
|
||||
|
||||
#import "TGModernConversationMentionsAssociatedPanel.h"
|
||||
#import "TGModernConversationHashtagsAssociatedPanel.h"
|
||||
#import "TGModernConversationAlphacodeAssociatedPanel.h"
|
||||
|
||||
@interface TGPhotoCaptionInputMixin ()
|
||||
{
|
||||
TGObserverProxy *_keyboardWillChangeFrameProxy;
|
||||
|
|
@ -167,165 +163,9 @@
|
|||
|
||||
#pragma mark - Input Panel Delegate
|
||||
|
||||
|
||||
//- (void)inputPanelMentionEntered:(TGMediaPickerCaptionInputPanel *)__unused inputTextPanel mention:(NSString *)mention startOfLine:(bool)__unused startOfLine
|
||||
//{
|
||||
// if (mention == nil)
|
||||
// {
|
||||
// if ([[inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationMentionsAssociatedPanel class]])
|
||||
// [inputTextPanel setAssociatedPanel:nil animated:true];
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// TGModernConversationMentionsAssociatedPanel *panel = nil;
|
||||
// if ([[inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationMentionsAssociatedPanel class]])
|
||||
// panel = (TGModernConversationMentionsAssociatedPanel *)[inputTextPanel associatedPanel];
|
||||
// else
|
||||
// {
|
||||
// panel = [[TGModernConversationMentionsAssociatedPanel alloc] initWithStyle:TGModernConversationAssociatedInputPanelDarkStyle];
|
||||
//
|
||||
// __weak TGPhotoCaptionInputMixin *weakSelf = self;
|
||||
// panel.userSelected = ^(TGUser *user)
|
||||
// {
|
||||
// __strong TGPhotoCaptionInputMixin *strongSelf = weakSelf;
|
||||
// if (strongSelf != nil)
|
||||
// {
|
||||
// if ([[strongSelf->_inputPanel associatedPanel] isKindOfClass:[TGModernConversationMentionsAssociatedPanel class]])
|
||||
// [strongSelf->_inputPanel setAssociatedPanel:nil animated:false];
|
||||
//
|
||||
// if (user.userName.length == 0) {
|
||||
// [strongSelf->_inputPanel replaceMention:[[NSString alloc] initWithFormat:@"%@", user.displayFirstName] username:false userId:user.uid];
|
||||
// } else {
|
||||
// [strongSelf->_inputPanel replaceMention:[[NSString alloc] initWithFormat:@"%@", user.userName] username:true userId:user.uid];
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// SSignal *userListSignal = nil;
|
||||
// if (self.suggestionContext.userListSignal != nil)
|
||||
// userListSignal = self.suggestionContext.userListSignal(mention);
|
||||
//
|
||||
// [panel setUserListSignal:userListSignal];
|
||||
//
|
||||
// [inputTextPanel setAssociatedPanel:panel animated:true];
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//- (void)inputPanelHashtagEntered:(TGMediaPickerCaptionInputPanel *)inputTextPanel hashtag:(NSString *)hashtag
|
||||
//{
|
||||
// if (hashtag == nil)
|
||||
// {
|
||||
// if ([[inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationHashtagsAssociatedPanel class]])
|
||||
// [inputTextPanel setAssociatedPanel:nil animated:true];
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// TGModernConversationHashtagsAssociatedPanel *panel = nil;
|
||||
// if ([[inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationHashtagsAssociatedPanel class]])
|
||||
// panel = (TGModernConversationHashtagsAssociatedPanel *)[inputTextPanel associatedPanel];
|
||||
// else
|
||||
// {
|
||||
// panel = [[TGModernConversationHashtagsAssociatedPanel alloc] initWithStyle:TGModernConversationAssociatedInputPanelDarkStyle];
|
||||
//
|
||||
// __weak TGPhotoCaptionInputMixin *weakSelf = self;
|
||||
// panel.hashtagSelected = ^(NSString *hashtag)
|
||||
// {
|
||||
// __strong TGPhotoCaptionInputMixin *strongSelf = weakSelf;
|
||||
// if (strongSelf != nil)
|
||||
// {
|
||||
// if ([[strongSelf->_inputPanel associatedPanel] isKindOfClass:[TGModernConversationHashtagsAssociatedPanel class]])
|
||||
// [strongSelf->_inputPanel setAssociatedPanel:nil animated:false];
|
||||
//
|
||||
// [strongSelf->_inputPanel replaceHashtag:hashtag];
|
||||
// }
|
||||
// };
|
||||
// [inputTextPanel setAssociatedPanel:panel animated:true];
|
||||
// }
|
||||
//
|
||||
// SSignal *hashtagListSignal = nil;
|
||||
// if (self.suggestionContext.hashtagListSignal != nil)
|
||||
// hashtagListSignal = self.suggestionContext.hashtagListSignal(hashtag);
|
||||
//
|
||||
// [panel setHashtagListSignal:hashtagListSignal];
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//- (void)inputPanelAlphacodeEntered:(TGMediaPickerCaptionInputPanel *)inputTextPanel alphacode:(NSString *)alphacode
|
||||
//{
|
||||
// if (alphacode == nil)
|
||||
// {
|
||||
// if ([[inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationAlphacodeAssociatedPanel class]])
|
||||
// [inputTextPanel setAssociatedPanel:nil animated:true];
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// TGModernConversationAlphacodeAssociatedPanel *panel = nil;
|
||||
// if ([[inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationAlphacodeAssociatedPanel class]])
|
||||
// panel = ((TGModernConversationAlphacodeAssociatedPanel *)[inputTextPanel associatedPanel]);
|
||||
// else
|
||||
// {
|
||||
// panel = [[TGModernConversationAlphacodeAssociatedPanel alloc] initWithStyle:TGModernConversationAssociatedInputPanelDarkStyle];
|
||||
// __weak TGPhotoCaptionInputMixin *weakSelf = self;
|
||||
// panel.alphacodeSelected = ^(TGAlphacodeEntry *entry)
|
||||
// {
|
||||
// __strong TGPhotoCaptionInputMixin *strongSelf = weakSelf;
|
||||
// if (strongSelf != nil)
|
||||
// {
|
||||
// if ([[strongSelf->_inputPanel associatedPanel] isKindOfClass:[TGModernConversationAlphacodeAssociatedPanel class]])
|
||||
// {
|
||||
// [strongSelf->_inputPanel setAssociatedPanel:nil animated:false];
|
||||
// }
|
||||
//
|
||||
// NSString *codeText = entry.emoji;
|
||||
//
|
||||
// [strongSelf appendAlphacode:[codeText stringByAppendingString:@" "]];
|
||||
// }
|
||||
// };
|
||||
// [inputTextPanel setAssociatedPanel:panel animated:true];
|
||||
// }
|
||||
//
|
||||
// SSignal *alphacodeListSignal = nil;
|
||||
// if (self.suggestionContext.alphacodeSignal != nil)
|
||||
// alphacodeListSignal = self.suggestionContext.alphacodeSignal(alphacode, inputTextPanel.inputField.textInputMode.primaryLanguage);
|
||||
//
|
||||
// [panel setAlphacodeListSignal:alphacodeListSignal];
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//- (void)appendAlphacode:(NSString *)alphacode
|
||||
//{
|
||||
// NSString *currentText = [_inputPanel inputField].text;
|
||||
// NSRange selectRange = NSMakeRange(0, 0);
|
||||
//
|
||||
// if (currentText.length == 0)
|
||||
// currentText = alphacode;
|
||||
// else
|
||||
// {
|
||||
// NSInteger caretIndex = [_inputPanel textCaretPosition];
|
||||
//
|
||||
// for (NSInteger i = caretIndex - 1; i >= 0; i--)
|
||||
// {
|
||||
// if ([currentText characterAtIndex:i] == ':') {
|
||||
// currentText = [currentText stringByReplacingCharactersInRange:NSMakeRange(i, caretIndex - i) withString:alphacode];
|
||||
// selectRange = NSMakeRange(i + alphacode.length, 0);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// [[_inputPanel inputField] setAttributedText:[[NSAttributedString alloc] initWithString:currentText] animated:false];
|
||||
// [[_inputPanel inputField] selectRange:selectRange force:true];
|
||||
//
|
||||
// [_inputPanel inputField].internalTextView.enableFirstResponder = true;
|
||||
//}
|
||||
//
|
||||
- (void)setContentAreaHeight:(CGFloat)contentAreaHeight
|
||||
{
|
||||
_contentAreaHeight = contentAreaHeight;
|
||||
|
||||
CGFloat finalHeight = _contentAreaHeight - _keyboardHeight;
|
||||
// [_inputPanel setContentAreaHeight:finalHeight];
|
||||
}
|
||||
|
||||
- (UIView *)_parentView
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ swift_library(
|
|||
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
|
||||
"//submodules/StickerResources:StickerResources",
|
||||
"//submodules/TextFormat:TextFormat",
|
||||
"//submodules/AttachmentUI:AttachmentUI",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import MimeTypes
|
|||
import LocalMediaResources
|
||||
import LegacyUI
|
||||
import TextFormat
|
||||
import AttachmentUI
|
||||
|
||||
public func guessMimeTypeByFileExtension(_ ext: String) -> String {
|
||||
return TGMimeTypeMap.mimeType(forExtension: ext) ?? "application/binary"
|
||||
|
|
@ -54,8 +55,6 @@ public func configureLegacyAssetPicker(_ controller: TGMediaAssetsController, co
|
|||
done?(time)
|
||||
}
|
||||
}
|
||||
controller.dismissalBlock = {
|
||||
}
|
||||
controller.selectionLimitExceeded = {
|
||||
presentSelectionLimitExceeded()
|
||||
}
|
||||
|
|
@ -69,6 +68,52 @@ public func configureLegacyAssetPicker(_ controller: TGMediaAssetsController, co
|
|||
}
|
||||
}
|
||||
|
||||
public class LegacyAssetPickerContext: AttachmentMediaPickerContext {
|
||||
private weak var controller: TGMediaAssetsController?
|
||||
|
||||
public var selectionCount: Signal<Int, NoError> {
|
||||
return Signal { [weak self] subscriber in
|
||||
let disposable = self?.controller?.selectionContext.selectionChangedSignal().start(next: { [weak self] value in
|
||||
subscriber.putNext(Int(self?.controller?.selectionContext.count() ?? 0))
|
||||
}, error: { _ in }, completed: { })
|
||||
return ActionDisposable {
|
||||
disposable?.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var caption: Signal<NSAttributedString?, NoError> {
|
||||
return Signal { [weak self] subscriber in
|
||||
let disposable = self?.controller?.editingContext.forcedCaption().start(next: { caption in
|
||||
if let caption = caption as? NSAttributedString {
|
||||
subscriber.putNext(caption)
|
||||
} else {
|
||||
subscriber.putNext(nil)
|
||||
}
|
||||
}, error: { _ in }, completed: { })
|
||||
return ActionDisposable {
|
||||
disposable?.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public init(controller: TGMediaAssetsController) {
|
||||
self.controller = controller
|
||||
}
|
||||
|
||||
public func setCaption(_ caption: NSAttributedString) {
|
||||
self.controller?.editingContext.setForcedCaption(caption, skipUpdate: true)
|
||||
}
|
||||
|
||||
public func send(silently: Bool) {
|
||||
self.controller?.send(silently)
|
||||
}
|
||||
|
||||
public func schedule() {
|
||||
self.controller?.schedule()
|
||||
}
|
||||
}
|
||||
|
||||
public func legacyAssetPicker(context: AccountContext, presentationData: PresentationData, editingMedia: Bool, fileMode: Bool, peer: Peer?, saveEditedPhotos: Bool, allowGrouping: Bool, selectionLimit: Int) -> Signal<(LegacyComponentsContext) -> TGMediaAssetsController, Void> {
|
||||
let isSecretChat = (peer?.id.namespace._internalGetInt32Value() ?? 0) == Namespaces.Peer.SecretChat._internalGetInt32Value()
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ swift_library(
|
|||
"//submodules/DeviceAccess:DeviceAccess",
|
||||
"//submodules/LegacyComponents:LegacyComponents",
|
||||
"//submodules/StickerResources:StickerResources",
|
||||
"//submodules/AttachmentUI:AttachmentUI",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import Display
|
|||
import SwiftSignalKit
|
||||
import LegacyComponents
|
||||
import TelegramPresentationData
|
||||
import AttachmentUI
|
||||
|
||||
public enum LegacyControllerPresentation {
|
||||
case custom
|
||||
|
|
@ -375,7 +376,7 @@ public final class LegacyControllerContext: NSObject, LegacyComponentsContext {
|
|||
}
|
||||
}
|
||||
|
||||
open class LegacyController: ViewController, PresentableController {
|
||||
open class LegacyController: ViewController, PresentableController, AttachmentContainable {
|
||||
public private(set) var legacyController: UIViewController!
|
||||
private let presentation: LegacyControllerPresentation
|
||||
|
||||
|
|
@ -390,6 +391,8 @@ open class LegacyController: ViewController, PresentableController {
|
|||
|
||||
fileprivate var validLayout: ContainerViewLayout?
|
||||
|
||||
public var parentInsets: UIEdgeInsets = UIEdgeInsets()
|
||||
|
||||
public var controllerLoaded: (() -> Void)?
|
||||
public var presentationCompleted: (() -> Void)?
|
||||
|
||||
|
|
@ -402,6 +405,8 @@ open class LegacyController: ViewController, PresentableController {
|
|||
|
||||
public var disposables = DisposableSet()
|
||||
|
||||
open var requestAttachmentMenuExpansion: () -> Void = {}
|
||||
|
||||
public init(presentation: LegacyControllerPresentation, theme: PresentationTheme? = nil, strings: PresentationStrings? = nil, initialLayout: ContainerViewLayout? = nil) {
|
||||
self.sizeClass.set(SSignal.single(UIUserInterfaceSizeClass.compact.rawValue as NSNumber))
|
||||
self.presentation = presentation
|
||||
|
|
@ -453,6 +458,10 @@ open class LegacyController: ViewController, PresentableController {
|
|||
}
|
||||
|
||||
if self.controllerNode.controllerView == nil {
|
||||
if self.controllerNode.frame.width == 0.0, let layout = self.validLayout {
|
||||
self.controllerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width - self.parentInsets.left - self.parentInsets.right, height: layout.size.height))
|
||||
}
|
||||
|
||||
self.controllerNode.controllerView = self.legacyController.view
|
||||
if let legacyController = self.legacyController as? TGViewController {
|
||||
legacyController.ignoreAppearEvents = true
|
||||
|
|
@ -554,10 +563,12 @@ open class LegacyController: ViewController, PresentableController {
|
|||
orientation = .landscapeRight
|
||||
}
|
||||
|
||||
legacyTelegramController.intrinsicSize = layout.size
|
||||
let size = CGSize(width: layout.size.width - layout.intrinsicInsets.left - layout.intrinsicInsets.right, height: layout.size.height)
|
||||
|
||||
legacyTelegramController.intrinsicSize = size
|
||||
legacyTelegramController._updateInset(for: orientation, force: false, notify: true)
|
||||
if self.enableContainerLayoutUpdates {
|
||||
legacyTelegramController.layoutController(for: layout.size, duration: duration)
|
||||
legacyTelegramController.layoutController(for: size, duration: duration)
|
||||
}
|
||||
}
|
||||
let updatedSizeClass: UIUserInterfaceSizeClass
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@ final class LegacyControllerNode: ASDisplayNode {
|
|||
|
||||
var controllerView: UIView? {
|
||||
didSet {
|
||||
if let controllerView = self.controllerView, let containerLayout = self.containerLayout {
|
||||
controllerView.frame = CGRect(origin: CGPoint(), size: containerLayout.size)
|
||||
if let controllerView = self.controllerView, let layout = self.containerLayout {
|
||||
let size = CGSize(width: layout.size.width - layout.intrinsicInsets.left - layout.intrinsicInsets.right, height: layout.size.height)
|
||||
controllerView.frame = CGRect(origin: CGPoint(x: layout.intrinsicInsets.left, y: 0.0), size: size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,13 +22,14 @@ final class LegacyControllerNode: ASDisplayNode {
|
|||
return UITracingLayerView()
|
||||
})
|
||||
|
||||
self.clipsToBounds = true
|
||||
// self.clipsToBounds = true
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.containerLayout = layout
|
||||
let size = CGSize(width: layout.size.width - layout.intrinsicInsets.left - layout.intrinsicInsets.right, height: layout.size.height)
|
||||
if let controllerView = self.controllerView {
|
||||
controllerView.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
controllerView.frame = CGRect(origin: CGPoint(x: layout.intrinsicInsets.left, y: 0.0), size: size)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ swift_library(
|
|||
"//submodules/TelegramNotices:TelegramNotices",
|
||||
"//submodules/TooltipUI:TooltipUI",
|
||||
"//submodules/UndoUI:UndoUI",
|
||||
"//submodules/AttachmentUI:AttachmentUI",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import AppBundle
|
|||
import CoreLocation
|
||||
import PresentationDataUtils
|
||||
import DeviceAccess
|
||||
import AttachmentUI
|
||||
|
||||
public enum LocationPickerMode {
|
||||
case share(peer: Peer?, selfPeer: Peer?, hasLiveLocation: Bool)
|
||||
|
|
@ -51,7 +52,7 @@ class LocationPickerInteraction {
|
|||
}
|
||||
}
|
||||
|
||||
public final class LocationPickerController: ViewController {
|
||||
public final class LocationPickerController: ViewController, AttachmentContainable {
|
||||
private var controllerNode: LocationPickerControllerNode {
|
||||
return self.displayNode as! LocationPickerControllerNode
|
||||
}
|
||||
|
|
@ -69,6 +70,8 @@ public final class LocationPickerController: ViewController {
|
|||
private var permissionDisposable: Disposable?
|
||||
|
||||
private var interaction: LocationPickerInteraction?
|
||||
|
||||
public var requestAttachmentMenuExpansion: () -> Void = {}
|
||||
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, mode: LocationPickerMode, completion: @escaping (TelegramMediaMap, String?) -> Void) {
|
||||
self.context = context
|
||||
|
|
@ -326,6 +329,8 @@ public final class LocationPickerController: ViewController {
|
|||
}
|
||||
|
||||
@objc private func searchPressed() {
|
||||
self.requestAttachmentMenuExpansion()
|
||||
|
||||
self.interaction?.openSearch()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -831,7 +831,8 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
|
|||
}
|
||||
}
|
||||
|
||||
let topInset: CGFloat = floor((layout.size.height - navigationHeight) / 2.0 + navigationHeight)
|
||||
// let topInset: CGFloat = floor((layout.size.height - navigationHeight) / 2.0 + navigationHeight)
|
||||
let topInset: CGFloat = 240.0
|
||||
let overlap: CGFloat = 6.0
|
||||
let headerHeight: CGFloat
|
||||
if isPickingLocation, let actionHeight = actionHeight {
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ final class LocationSearchContainerNode: ASDisplayNode {
|
|||
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: CGSize(width: layout.size.width, height: layout.size.height - topInset)))
|
||||
|
||||
self.listNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: UIEdgeInsets(top: topInset, left: 0.0, bottom: layout.intrinsicInsets.bottom, right: 0.0), duration: 0.0, curve: .Default(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: UIEdgeInsets(top: topInset, left: layout.intrinsicInsets.left, bottom: layout.intrinsicInsets.bottom, right: layout.intrinsicInsets.right), duration: 0.0, curve: .Default(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
|
||||
let padding: CGFloat = 16.0
|
||||
let emptyTitleSize = self.emptyResultsTitleNode.updateLayout(CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - padding * 2.0, height: CGFloat.greatestFiniteMagnitude))
|
||||
|
|
|
|||
20
submodules/Pasteboard/BUILD
Normal file
20
submodules/Pasteboard/BUILD
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "Pasteboard",
|
||||
module_name = "Pasteboard",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/TextFormat:TextFormat",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
|
|
@ -23,13 +23,6 @@ private func rtfStringWithAppliedEntities(_ text: String, entities: [MessageText
|
|||
}
|
||||
}
|
||||
|
||||
func chatInputStateStringFromRTF(_ data: Data, type: NSAttributedString.DocumentType) -> NSAttributedString? {
|
||||
if let attributedString = try? NSAttributedString(data: data, options: [NSAttributedString.DocumentReadingOptionKey.documentType: type], documentAttributes: nil) {
|
||||
return chatInputStateString(attributedString: attributedString)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func chatInputStateString(attributedString: NSAttributedString) -> NSAttributedString? {
|
||||
let string = NSMutableAttributedString(string: attributedString.string)
|
||||
attributedString.enumerateAttributes(in: NSRange(location: 0, length: attributedString.length), options: [], using: { attributes, range, _ in
|
||||
|
|
@ -62,7 +55,14 @@ private func chatInputStateString(attributedString: NSAttributedString) -> NSAtt
|
|||
return string
|
||||
}
|
||||
|
||||
func storeMessageTextInPasteboard(_ text: String, entities: [MessageTextEntity]?) {
|
||||
public func chatInputStateStringFromRTF(_ data: Data, type: NSAttributedString.DocumentType) -> NSAttributedString? {
|
||||
if let attributedString = try? NSAttributedString(data: data, options: [NSAttributedString.DocumentReadingOptionKey.documentType: type], documentAttributes: nil) {
|
||||
return chatInputStateString(attributedString: attributedString)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public func storeMessageTextInPasteboard(_ text: String, entities: [MessageTextEntity]?) {
|
||||
var items: [String: Any] = [:]
|
||||
items[kUTTypeUTF8PlainText as String] = text
|
||||
|
||||
|
|
@ -72,14 +72,14 @@ func storeMessageTextInPasteboard(_ text: String, entities: [MessageTextEntity]?
|
|||
UIPasteboard.general.items = [items]
|
||||
}
|
||||
|
||||
func storeAttributedTextInPasteboard(_ text: NSAttributedString) {
|
||||
public func storeAttributedTextInPasteboard(_ text: NSAttributedString) {
|
||||
if let inputText = chatInputStateString(attributedString: text) {
|
||||
let entities = generateChatInputTextEntities(inputText)
|
||||
storeMessageTextInPasteboard(inputText.string, entities: entities)
|
||||
}
|
||||
}
|
||||
|
||||
func storeInputTextInPasteboard(_ text: NSAttributedString) {
|
||||
public func storeInputTextInPasteboard(_ text: NSAttributedString) {
|
||||
let entities = generateChatInputTextEntities(text)
|
||||
storeMessageTextInPasteboard(text.string, entities: entities)
|
||||
}
|
||||
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
@interface RMIntroViewController : UIViewController<UIScrollViewDelegate, GLKViewDelegate>
|
||||
{
|
||||
EAGLContext *context;
|
||||
EAGLContext *_context;
|
||||
|
||||
GLKView *_glkView;
|
||||
|
||||
|
|
|
|||
|
|
@ -286,7 +286,7 @@ float t_local(float start_value, float end_value, float start_time, float durati
|
|||
|
||||
|
||||
|
||||
static int ribbonLength = 86.5;
|
||||
static int ribbonLength = 86;
|
||||
static int starsFar=500;
|
||||
|
||||
static float scroll_offset;
|
||||
|
|
|
|||
|
|
@ -29,14 +29,6 @@ typedef enum {
|
|||
iPadPro = 6
|
||||
} DeviceScreen;
|
||||
|
||||
static void TGDispatchOnMainThread(dispatch_block_t block) {
|
||||
if ([NSThread isMainThread]) {
|
||||
block();
|
||||
} else {
|
||||
dispatch_async(dispatch_get_main_queue(), block);
|
||||
}
|
||||
}
|
||||
|
||||
@interface UIScrollView (CurrentPage)
|
||||
- (int)currentPage;
|
||||
- (void)setPage:(NSInteger)page;
|
||||
|
|
@ -242,8 +234,8 @@ static void TGDispatchOnMainThread(dispatch_block_t block) {
|
|||
|
||||
if (/*[[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground*/true && !_isOpenGLLoaded)
|
||||
{
|
||||
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
|
||||
if (!context)
|
||||
_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
|
||||
if (!_context)
|
||||
NSLog(@"Failed to create ES context");
|
||||
|
||||
bool isIpad = ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad);
|
||||
|
|
@ -256,7 +248,7 @@ static void TGDispatchOnMainThread(dispatch_block_t block) {
|
|||
if (isIpad)
|
||||
height += 138 / 2;
|
||||
|
||||
_glkView = [[GLKView alloc] initWithFrame:CGRectMake(self.view.bounds.size.width / 2 - size / 2, height, size, size) context:context];
|
||||
_glkView = [[GLKView alloc] initWithFrame:CGRectMake(self.view.bounds.size.width / 2 - size / 2, height, size, size) context:_context];
|
||||
_glkView.backgroundColor = _backgroundColor;
|
||||
_glkView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
|
||||
_glkView.drawableDepthFormat = GLKViewDrawableDepthFormat24;
|
||||
|
|
@ -284,7 +276,7 @@ static void TGDispatchOnMainThread(dispatch_block_t block) {
|
|||
[EAGLContext setCurrentContext:nil];
|
||||
|
||||
_glkView.context = nil;
|
||||
context = nil;
|
||||
_context = nil;
|
||||
[_glkView removeFromSuperview];
|
||||
_glkView = nil;
|
||||
_isOpenGLLoaded = false;
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ public enum PresentationResourceKey: Int32 {
|
|||
case itemListBlockDestructiveIcon
|
||||
case itemListAddDeviceIcon
|
||||
case itemListResetIcon
|
||||
case itemListImageIcon
|
||||
case itemListCloudIcon
|
||||
|
||||
case itemListVoiceCallIcon
|
||||
case itemListVideoCallIcon
|
||||
|
|
|
|||
|
|
@ -222,6 +222,18 @@ public struct PresentationResourcesItemList {
|
|||
})
|
||||
}
|
||||
|
||||
public static func imageIcon(_ theme: PresentationTheme) -> UIImage? {
|
||||
return theme.image(PresentationResourceKey.itemListImageIcon.rawValue, { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Attach Menu/Image"), color: theme.list.itemAccentColor)
|
||||
})
|
||||
}
|
||||
|
||||
public static func cloudIcon(_ theme: PresentationTheme) -> UIImage? {
|
||||
return theme.image(PresentationResourceKey.itemListCloudIcon.rawValue, { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Attach Menu/Cloud"), color: theme.list.itemAccentColor)
|
||||
})
|
||||
}
|
||||
|
||||
public static func cornersImage(_ theme: PresentationTheme, top: Bool, bottom: Bool) -> UIImage? {
|
||||
if !top && !bottom {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -257,6 +257,12 @@ swift_library(
|
|||
"//submodules/SoftwareVideo:SoftwareVideo",
|
||||
"//submodules/ManagedFile:ManagedFile",
|
||||
"//submodules/FetchManagerImpl:FetchManagerImpl",
|
||||
"//submodules/AttachmentUI:AttachmentUI",
|
||||
"//submodules/AttachmentTextInputPanelNode:AttachmentTextInputPanelNode",
|
||||
"//submodules/ChatPresentationInterfaceState:ChatPresentationInterfaceState",
|
||||
"//submodules/Pasteboard:Pasteboard",
|
||||
"//submodules/ChatSendMessageActionUI:ChatSendMessageActionUI",
|
||||
"//submodules/ChatTextLinkEditUI:ChatTextLinkEditUI",
|
||||
] + select({
|
||||
"@build_bazel_rules_apple//apple:ios_armv7": [],
|
||||
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
||||
|
|
|
|||
12
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Camera.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Camera.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Property 1=Camera.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
118
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Camera.imageset/Property 1=Camera.pdf
vendored
Normal file
118
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Camera.imageset/Property 1=Camera.pdf
vendored
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 14.000000 17.000000 cm
|
||||
1.000000 1.000000 1.000000 scn
|
||||
0.000000 16.899923 m
|
||||
0.000000 17.921806 0.000000 18.432747 0.062100 18.861015 c
|
||||
0.443433 21.490837 2.509163 23.556566 5.138984 23.937901 c
|
||||
5.567253 24.000000 6.078194 24.000000 7.100077 24.000000 c
|
||||
7.197224 24.000000 l
|
||||
7.389089 24.000000 7.485021 24.000000 7.575428 24.005465 c
|
||||
8.449500 24.058290 9.256863 24.490376 9.785665 25.188347 c
|
||||
9.840356 25.260532 9.893565 25.340347 9.999977 25.499966 c
|
||||
10.000000 25.500000 l
|
||||
10.000046 25.500069 l
|
||||
10.000060 25.500093 l
|
||||
10.106449 25.659672 10.159652 25.739475 10.214336 25.811653 c
|
||||
10.743138 26.509623 11.550501 26.941710 12.424573 26.994535 c
|
||||
12.514979 27.000000 12.610912 27.000000 12.802777 27.000000 c
|
||||
19.197224 27.000000 l
|
||||
19.389088 27.000000 19.485020 27.000000 19.575428 26.994535 c
|
||||
20.449501 26.941710 21.256863 26.509623 21.785666 25.811653 c
|
||||
21.840359 25.739460 21.893574 25.659641 22.000000 25.500000 c
|
||||
22.106426 25.340359 22.159641 25.260540 22.214334 25.188347 c
|
||||
22.743137 24.490377 23.550499 24.058289 24.424572 24.005465 c
|
||||
24.514980 24.000000 24.610912 24.000000 24.802776 24.000000 c
|
||||
24.899923 24.000000 l
|
||||
25.921803 24.000000 26.432743 24.000000 26.861012 23.937901 c
|
||||
29.490833 23.556566 31.556562 21.490837 31.937897 18.861015 c
|
||||
31.999996 18.432747 31.999996 17.921806 31.999996 16.899923 c
|
||||
31.999996 9.600000 l
|
||||
31.999996 6.239685 31.999996 4.559526 31.346035 3.276056 c
|
||||
30.770796 2.147085 29.852911 1.229200 28.723940 0.653961 c
|
||||
27.440470 0.000000 25.760311 0.000000 22.399996 0.000000 c
|
||||
9.599999 0.000000 l
|
||||
6.239685 0.000000 4.559527 0.000000 3.276057 0.653961 c
|
||||
2.147084 1.229200 1.229201 2.147085 0.653961 3.276056 c
|
||||
0.000000 4.559526 0.000000 6.239685 0.000000 9.600000 c
|
||||
0.000000 16.899923 l
|
||||
h
|
||||
16.000000 16.500000 m
|
||||
13.514719 16.500000 11.500000 14.485281 11.500000 12.000000 c
|
||||
11.500000 9.514719 13.514719 7.500000 16.000000 7.500000 c
|
||||
18.485281 7.500000 20.500000 9.514719 20.500000 12.000000 c
|
||||
20.500000 14.485281 18.485281 16.500000 16.000000 16.500000 c
|
||||
h
|
||||
8.500000 12.000000 m
|
||||
8.500000 16.142136 11.857864 19.500000 16.000000 19.500000 c
|
||||
20.142136 19.500000 23.500000 16.142136 23.500000 12.000000 c
|
||||
23.500000 7.857864 20.142136 4.500000 16.000000 4.500000 c
|
||||
11.857864 4.500000 8.500000 7.857864 8.500000 12.000000 c
|
||||
h
|
||||
26.000000 16.000000 m
|
||||
27.104570 16.000000 28.000000 16.895432 28.000000 18.000000 c
|
||||
28.000000 19.104568 27.104570 20.000000 26.000000 20.000000 c
|
||||
24.895430 20.000000 24.000000 19.104568 24.000000 18.000000 c
|
||||
24.000000 16.895432 24.895430 16.000000 26.000000 16.000000 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
2589
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 60.000000 60.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000002679 00000 n
|
||||
0000002702 00000 n
|
||||
0000002875 00000 n
|
||||
0000002949 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
3008
|
||||
%%EOF
|
||||
12
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Cloud.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Cloud.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "cloud_30.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
213
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Cloud.imageset/cloud_30.pdf
vendored
Normal file
213
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Cloud.imageset/cloud_30.pdf
vendored
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< /Type /XObject
|
||||
/Length 2 0 R
|
||||
/Group << /Type /Group
|
||||
/S /Transparency
|
||||
>>
|
||||
/Subtype /Form
|
||||
/Resources << >>
|
||||
/BBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||
>>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 2.500000 5.569031 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
20.853670 11.223368 m
|
||||
20.197720 11.332699 l
|
||||
20.167723 11.152725 20.212988 10.968333 20.322916 10.822711 c
|
||||
20.432844 10.677088 20.597776 10.583029 20.779081 10.562564 c
|
||||
20.853670 11.223368 l
|
||||
h
|
||||
20.858553 11.252664 m
|
||||
21.514503 11.143333 l
|
||||
21.520527 11.179465 21.523552 11.216033 21.523552 11.252664 c
|
||||
20.858553 11.252664 l
|
||||
h
|
||||
15.873398 17.663544 m
|
||||
16.037714 18.307922 l
|
||||
16.037458 18.307989 l
|
||||
15.873398 17.663544 l
|
||||
h
|
||||
8.432284 14.406836 m
|
||||
8.123861 13.817684 l
|
||||
8.445732 13.649182 8.843261 13.770190 9.016658 14.089451 c
|
||||
8.432284 14.406836 l
|
||||
h
|
||||
3.232294 11.457733 m
|
||||
3.459931 10.832909 l
|
||||
3.709621 10.923876 3.881181 11.154763 3.896227 11.420082 c
|
||||
3.232294 11.457733 l
|
||||
h
|
||||
20.345879 1.926858 m
|
||||
20.345879 1.261856 l
|
||||
20.347536 1.261860 l
|
||||
20.345879 1.926858 l
|
||||
h
|
||||
21.509621 11.114037 m
|
||||
21.514503 11.143333 l
|
||||
20.202602 11.361995 l
|
||||
20.197720 11.332699 l
|
||||
21.509621 11.114037 l
|
||||
h
|
||||
21.523552 11.252664 m
|
||||
21.523552 14.584707 19.261030 17.485977 16.037714 18.307922 c
|
||||
15.709081 17.019163 l
|
||||
18.344908 16.347027 20.193552 13.975069 20.193552 11.252664 c
|
||||
21.523552 11.252664 l
|
||||
h
|
||||
16.037458 18.307989 m
|
||||
12.807217 19.130329 9.437991 17.651901 7.847911 14.724220 c
|
||||
9.016658 14.089451 l
|
||||
10.317089 16.483826 13.070669 17.690840 15.709337 17.019098 c
|
||||
16.037458 18.307989 l
|
||||
h
|
||||
8.740708 14.995987 m
|
||||
6.028514 16.415833 2.742277 14.562114 2.568361 11.495386 c
|
||||
3.896227 11.420082 l
|
||||
4.015268 13.519165 6.265923 14.790322 8.123861 13.817684 c
|
||||
8.740708 14.995987 l
|
||||
h
|
||||
3.004657 12.082559 m
|
||||
0.798734 11.278893 -0.665000 9.183575 -0.665000 6.843657 c
|
||||
0.665000 6.843657 l
|
||||
0.665000 8.624672 1.779289 10.220613 3.459931 10.832909 c
|
||||
3.004657 12.082559 l
|
||||
h
|
||||
-0.665000 6.843657 m
|
||||
-0.665000 3.761652 1.834792 1.261858 4.916798 1.261858 c
|
||||
4.916798 2.591858 l
|
||||
2.569331 2.591858 0.665000 4.496190 0.665000 6.843657 c
|
||||
-0.665000 6.843657 l
|
||||
h
|
||||
4.916798 1.261858 m
|
||||
20.345879 1.261858 l
|
||||
20.345879 2.591858 l
|
||||
4.916798 2.591858 l
|
||||
4.916798 1.261858 l
|
||||
h
|
||||
20.347536 1.261860 m
|
||||
27.062166 1.278605 27.596050 11.131536 20.928259 11.884171 c
|
||||
20.779081 10.562564 l
|
||||
25.790516 9.996892 25.377172 2.604407 20.344219 2.591856 c
|
||||
20.347536 1.261860 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
2195
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
<< /Type /XObject
|
||||
/Length 4 0 R
|
||||
/Group << /Type /Group
|
||||
/S /Transparency
|
||||
>>
|
||||
/Subtype /Form
|
||||
/Resources << >>
|
||||
/BBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||
>>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
0.000000 30.000000 m
|
||||
30.000000 30.000000 l
|
||||
30.000000 0.000000 l
|
||||
0.000000 0.000000 l
|
||||
0.000000 30.000000 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
232
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /XObject << /X1 1 0 R >>
|
||||
/ExtGState << /E1 << /SMask << /Type /Mask
|
||||
/G 3 0 R
|
||||
/S /Alpha
|
||||
>>
|
||||
/Type /ExtGState
|
||||
>> >>
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Length 7 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
/E1 gs
|
||||
/X1 Do
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
7 0 obj
|
||||
46
|
||||
endobj
|
||||
|
||||
8 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||
/Resources 5 0 R
|
||||
/Contents 6 0 R
|
||||
/Parent 9 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
9 0 obj
|
||||
<< /Kids [ 8 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
10 0 obj
|
||||
<< /Pages 9 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 11
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000002453 00000 n
|
||||
0000002476 00000 n
|
||||
0000002956 00000 n
|
||||
0000002978 00000 n
|
||||
0000003276 00000 n
|
||||
0000003378 00000 n
|
||||
0000003399 00000 n
|
||||
0000003572 00000 n
|
||||
0000003646 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 10 0 R
|
||||
/Size 11
|
||||
>>
|
||||
startxref
|
||||
3706
|
||||
%%EOF
|
||||
12
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Contact.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Contact.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Union-2.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
79
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Contact.imageset/Union-2.pdf
vendored
Normal file
79
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Contact.imageset/Union-2.pdf
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||
1.000000 1.000000 1.000000 scn
|
||||
12.473795 18.000000 m
|
||||
15.787504 18.000000 18.473795 20.686291 18.473795 24.000000 c
|
||||
18.473795 27.313709 15.787504 30.000000 12.473795 30.000000 c
|
||||
9.160086 30.000000 6.473795 27.313709 6.473795 24.000000 c
|
||||
6.473795 20.686291 9.160086 18.000000 12.473795 18.000000 c
|
||||
h
|
||||
12.473970 14.000000 m
|
||||
17.620707 14.000000 22.118784 11.222771 24.551340 7.085182 c
|
||||
25.277521 5.850006 24.987329 4.309250 23.837667 3.454098 c
|
||||
21.863237 1.985462 18.145235 0.000000 12.473970 0.000000 c
|
||||
6.802708 0.000000 3.084706 1.985462 1.110274 3.454098 c
|
||||
-0.039390 4.309246 -0.329582 5.850006 0.396601 7.085182 c
|
||||
2.829157 11.222771 7.327235 14.000000 12.473970 14.000000 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
770
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 24.947937 30.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000000860 00000 n
|
||||
0000000882 00000 n
|
||||
0000001055 00000 n
|
||||
0000001129 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
1188
|
||||
%%EOF
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
||||
12
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/File.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/File.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Subtract.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
96
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/File.imageset/Subtract.pdf
vendored
Normal file
96
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/File.imageset/Subtract.pdf
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||
1.000000 1.000000 1.000000 scn
|
||||
14.400000 0.000000 m
|
||||
9.600000 0.000000 l
|
||||
6.239685 0.000000 4.559527 0.000000 3.276057 0.653961 c
|
||||
2.147084 1.229200 1.229201 2.147085 0.653961 3.276056 c
|
||||
0.000000 4.559526 0.000000 6.239685 0.000000 9.600000 c
|
||||
0.000000 20.400002 l
|
||||
0.000000 23.760315 0.000000 25.440472 0.653961 26.723944 c
|
||||
1.229201 27.852915 2.147084 28.770800 3.276057 29.346039 c
|
||||
4.559527 30.000000 6.239685 30.000000 9.600000 30.000000 c
|
||||
11.341846 30.000000 l
|
||||
12.345142 30.000000 12.846791 30.000000 13.317464 29.884233 c
|
||||
13.734708 29.781607 14.132692 29.612423 14.496114 29.383186 c
|
||||
14.906072 29.124596 15.254162 28.763371 15.950342 28.040918 c
|
||||
22.208498 21.546604 l
|
||||
22.208509 21.546591 l
|
||||
22.870174 20.859959 23.201010 20.516640 23.437363 20.119482 c
|
||||
23.646940 19.767319 23.801153 19.385040 23.894608 18.986031 c
|
||||
24.000000 18.536037 24.000000 18.059254 24.000000 17.105690 c
|
||||
24.000000 9.600000 l
|
||||
24.000000 6.239685 24.000000 4.559526 23.346039 3.276056 c
|
||||
22.770800 2.147085 21.852915 1.229200 20.723944 0.653961 c
|
||||
19.440474 0.000000 17.760315 0.000000 14.400000 0.000000 c
|
||||
h
|
||||
21.023287 16.875000 m
|
||||
14.187500 16.875000 l
|
||||
13.082931 16.875000 12.187500 17.770432 12.187500 18.875000 c
|
||||
12.187500 25.710787 l
|
||||
12.187500 26.601692 13.264641 27.047859 13.894606 26.417894 c
|
||||
21.730392 18.582108 l
|
||||
22.360359 17.952141 21.914192 16.875000 21.023287 16.875000 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
1447
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 24.000000 30.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000001537 00000 n
|
||||
0000001560 00000 n
|
||||
0000001733 00000 n
|
||||
0000001807 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
1866
|
||||
%%EOF
|
||||
12
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Gallery.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Gallery.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Union.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
123
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Gallery.imageset/Union.pdf
vendored
Normal file
123
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Gallery.imageset/Union.pdf
vendored
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 0.000000 -0.000050 cm
|
||||
1.000000 1.000000 1.000000 scn
|
||||
11.100000 28.000050 m
|
||||
11.035790 28.000050 l
|
||||
9.410112 28.000065 8.098883 28.000076 7.037065 27.913322 c
|
||||
5.943791 27.823997 4.983509 27.635279 4.095072 27.182598 c
|
||||
2.683856 26.463547 1.536502 25.316193 0.817452 23.904978 c
|
||||
0.364771 23.016541 0.176052 22.056259 0.086728 20.962984 c
|
||||
-0.000027 19.901167 -0.000015 18.589939 0.000000 16.964260 c
|
||||
0.000000 16.900049 l
|
||||
0.000000 11.100050 l
|
||||
0.000000 11.035839 l
|
||||
-0.000015 9.410160 -0.000027 8.098932 0.086728 7.037113 c
|
||||
0.176052 5.943840 0.364771 4.983559 0.817452 4.095121 c
|
||||
1.536502 2.683905 2.683856 1.536551 4.095072 0.817501 c
|
||||
4.983509 0.364819 5.943791 0.176100 7.037065 0.086777 c
|
||||
8.098874 0.000023 9.410091 0.000034 11.035753 0.000050 c
|
||||
11.100000 0.000050 l
|
||||
16.900000 0.000050 l
|
||||
16.964247 0.000050 l
|
||||
18.589909 0.000034 19.901125 0.000023 20.962936 0.086777 c
|
||||
22.056210 0.176100 23.016491 0.364819 23.904928 0.817501 c
|
||||
25.316145 1.536551 26.463499 2.683905 27.182549 4.095121 c
|
||||
27.635231 4.983559 27.823950 5.943840 27.913273 7.037113 c
|
||||
28.000027 8.098925 28.000015 9.410141 28.000000 11.035803 c
|
||||
28.000000 11.100050 l
|
||||
28.000000 16.900049 l
|
||||
28.000000 16.964296 l
|
||||
28.000015 18.589958 28.000027 19.901175 27.913273 20.962984 c
|
||||
27.823950 22.056259 27.635231 23.016541 27.182549 23.904978 c
|
||||
26.463499 25.316193 25.316145 26.463547 23.904928 27.182598 c
|
||||
23.016491 27.635279 22.056210 27.823997 20.962936 27.913322 c
|
||||
19.901117 28.000076 18.589890 28.000065 16.964211 28.000050 c
|
||||
16.900000 28.000050 l
|
||||
11.100000 28.000050 l
|
||||
h
|
||||
3.093551 7.093601 m
|
||||
3.170904 6.300156 3.303499 5.824047 3.490471 5.457092 c
|
||||
3.921902 4.610363 4.610314 3.921951 5.457044 3.490520 c
|
||||
5.852077 3.289240 6.373610 3.150980 7.281361 3.076813 c
|
||||
8.206622 3.001217 9.395091 3.000050 11.100000 3.000050 c
|
||||
16.900000 3.000050 l
|
||||
18.604908 3.000050 19.793379 3.001217 20.718641 3.076813 c
|
||||
21.626392 3.150980 22.147924 3.289240 22.542957 3.490520 c
|
||||
23.389687 3.921951 24.078098 4.610363 24.509529 5.457092 c
|
||||
24.710810 5.852125 24.849070 6.373657 24.923237 7.281408 c
|
||||
24.963232 7.770926 24.982393 8.334116 24.991571 9.008478 c
|
||||
19.060661 14.939390 l
|
||||
18.474873 15.525175 17.525127 15.525177 16.939339 14.939390 c
|
||||
11.000000 9.000050 l
|
||||
9.060661 10.939388 l
|
||||
8.474873 11.525177 7.525128 11.525177 6.939342 10.939392 c
|
||||
3.093551 7.093601 l
|
||||
h
|
||||
9.000000 16.000050 m
|
||||
10.656855 16.000050 12.000000 17.343195 12.000000 19.000050 c
|
||||
12.000000 20.656902 10.656855 22.000050 9.000000 22.000050 c
|
||||
7.343146 22.000050 6.000001 20.656902 6.000001 19.000050 c
|
||||
6.000001 17.343195 7.343146 16.000050 9.000000 16.000050 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
2627
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 28.000000 28.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000002717 00000 n
|
||||
0000002740 00000 n
|
||||
0000002913 00000 n
|
||||
0000002987 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
3046
|
||||
%%EOF
|
||||
12
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Image.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Image.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "image_30.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
160
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Image.imageset/image_30.pdf
vendored
Normal file
160
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Image.imageset/image_30.pdf
vendored
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 5.335022 5.335083 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
7.065000 19.329956 m
|
||||
7.035460 19.329956 l
|
||||
5.940372 19.329960 5.077796 19.329964 4.383656 19.273251 c
|
||||
3.675523 19.215395 3.084329 19.095215 2.547134 18.821501 c
|
||||
1.669358 18.374252 0.955704 17.660599 0.508455 16.782822 c
|
||||
0.234740 16.245626 0.114562 15.654433 0.056705 14.946301 c
|
||||
-0.000008 14.252161 -0.000005 13.389584 0.000000 12.294496 c
|
||||
0.000000 12.264956 l
|
||||
0.000000 7.064956 l
|
||||
0.000000 7.035416 l
|
||||
-0.000005 5.940329 -0.000008 5.077752 0.056705 4.383612 c
|
||||
0.114562 3.675479 0.234740 3.084284 0.508455 2.547091 c
|
||||
0.955704 1.669313 1.669358 0.955660 2.547134 0.508410 c
|
||||
3.084329 0.234695 3.675523 0.114517 4.383656 0.056660 c
|
||||
5.077746 -0.000051 5.940250 -0.000048 7.035228 -0.000044 c
|
||||
7.035275 -0.000044 l
|
||||
7.035323 -0.000044 l
|
||||
7.035370 -0.000044 l
|
||||
7.065000 -0.000044 l
|
||||
12.265000 -0.000044 l
|
||||
12.294630 -0.000044 l
|
||||
12.294676 -0.000044 l
|
||||
12.294722 -0.000044 l
|
||||
12.294767 -0.000044 l
|
||||
13.389750 -0.000048 14.252252 -0.000051 14.946344 0.056660 c
|
||||
15.654477 0.114517 16.245672 0.234695 16.782866 0.508410 c
|
||||
17.660643 0.955660 18.374296 1.669313 18.821547 2.547091 c
|
||||
19.095261 3.084284 19.215439 3.675479 19.273296 4.383612 c
|
||||
19.330008 5.077704 19.330004 5.940207 19.330000 7.035190 c
|
||||
19.330000 7.035235 l
|
||||
19.330000 7.035281 l
|
||||
19.330000 7.035327 l
|
||||
19.330000 7.064957 l
|
||||
19.330000 12.264956 l
|
||||
19.330000 12.294586 l
|
||||
19.330000 12.294634 l
|
||||
19.330000 12.294681 l
|
||||
19.330000 12.294727 l
|
||||
19.330004 13.389706 19.330008 14.252210 19.273296 14.946301 c
|
||||
19.215439 15.654433 19.095261 16.245626 18.821547 16.782822 c
|
||||
18.374296 17.660599 17.660643 18.374252 16.782866 18.821501 c
|
||||
16.245672 19.095215 15.654477 19.215395 14.946344 19.273251 c
|
||||
14.252204 19.329964 13.389627 19.329960 12.294539 19.329956 c
|
||||
12.264999 19.329956 l
|
||||
7.065000 19.329956 l
|
||||
h
|
||||
3.150942 17.636463 m
|
||||
3.469394 17.798721 3.866076 17.896530 4.491960 17.947668 c
|
||||
5.125607 17.999439 5.933921 17.999956 7.065000 17.999956 c
|
||||
12.264999 17.999956 l
|
||||
13.396078 17.999956 14.204392 17.999439 14.838039 17.947668 c
|
||||
15.463923 17.896530 15.860606 17.798721 16.179058 17.636463 c
|
||||
16.806580 17.316725 17.316771 16.806536 17.636507 16.179014 c
|
||||
17.798767 15.860562 17.896576 15.463881 17.947712 14.837996 c
|
||||
17.999483 14.204350 18.000000 13.396034 18.000000 12.264956 c
|
||||
18.000000 7.064957 l
|
||||
18.000000 6.309554 17.999769 5.698115 17.984194 5.186526 c
|
||||
15.466902 7.867050 l
|
||||
14.693245 8.690873 13.378143 8.669041 12.632261 7.819986 c
|
||||
11.520865 6.554860 l
|
||||
6.709533 11.633489 l
|
||||
5.937637 12.448267 4.633699 12.427576 3.888045 11.588715 c
|
||||
1.330000 8.710914 l
|
||||
1.330000 12.264956 l
|
||||
1.330000 13.396034 1.330517 14.204349 1.382288 14.837996 c
|
||||
1.433425 15.463881 1.531234 15.860562 1.693493 16.179014 c
|
||||
2.013231 16.806536 2.523421 17.316725 3.150942 17.636463 c
|
||||
h
|
||||
14.497394 6.956580 m
|
||||
17.768957 3.472878 l
|
||||
17.730608 3.355118 17.686632 3.249275 17.636507 3.150898 c
|
||||
17.316771 2.523376 16.806580 2.013186 16.179058 1.693449 c
|
||||
16.167240 1.687428 16.155315 1.681494 16.143274 1.675650 c
|
||||
12.439418 5.585277 l
|
||||
13.631460 6.942204 l
|
||||
13.859314 7.201574 14.261054 7.208245 14.497394 6.956580 c
|
||||
h
|
||||
1.330082 6.709091 m
|
||||
1.330711 5.761070 1.336117 5.057030 1.382288 4.491917 c
|
||||
1.433425 3.866033 1.531234 3.469350 1.693493 3.150898 c
|
||||
2.013231 2.523376 2.523421 2.013186 3.150942 1.693449 c
|
||||
3.469394 1.531189 3.866076 1.433380 4.491960 1.382244 c
|
||||
5.125607 1.330473 5.933922 1.329956 7.065000 1.329956 c
|
||||
12.265000 1.329956 l
|
||||
13.261761 1.329956 14.007863 1.330357 14.604662 1.365883 c
|
||||
5.744016 10.718788 l
|
||||
5.508215 10.967690 5.109884 10.961369 4.882100 10.705111 c
|
||||
1.330082 6.709091 l
|
||||
h
|
||||
14.040000 12.164956 m
|
||||
15.075534 12.164956 15.915000 13.004422 15.915000 14.039956 c
|
||||
15.915000 15.075490 15.075534 15.914956 14.040000 15.914956 c
|
||||
13.004466 15.914956 12.165000 15.075490 12.165000 14.039956 c
|
||||
12.165000 13.004422 13.004466 12.164956 14.040000 12.164956 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
3879
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000003969 00000 n
|
||||
0000003992 00000 n
|
||||
0000004165 00000 n
|
||||
0000004239 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
4298
|
||||
%%EOF
|
||||
12
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Location.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Location.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Subtract-2.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
77
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Location.imageset/Subtract-2.pdf
vendored
Normal file
77
submodules/TelegramUI/Images.xcassets/Chat/Attach Menu/Location.imageset/Subtract-2.pdf
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||
1.000000 1.000000 1.000000 scn
|
||||
12.000000 0.000000 m
|
||||
15.627419 0.000000 24.000000 11.372581 24.000000 20.000000 c
|
||||
24.000000 26.627417 18.627419 32.000000 12.000000 32.000000 c
|
||||
5.372583 32.000000 0.000000 26.627417 0.000000 20.000000 c
|
||||
0.000000 11.372581 8.372583 0.000000 12.000000 0.000000 c
|
||||
h
|
||||
12.000000 15.000000 m
|
||||
14.761425 15.000000 17.000000 17.238577 17.000000 20.000000 c
|
||||
17.000000 22.761423 14.761425 25.000000 12.000000 25.000000 c
|
||||
9.238577 25.000000 7.000000 22.761423 7.000000 20.000000 c
|
||||
7.000000 17.238577 9.238577 15.000000 12.000000 15.000000 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
656
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 24.000000 32.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000000746 00000 n
|
||||
0000000768 00000 n
|
||||
0000000941 00000 n
|
||||
0000001015 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
1074
|
||||
%%EOF
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue