Merge branch 'attach-menu'

This commit is contained in:
Ilya Laktyushin 2022-02-13 04:27:41 +03:00
commit ec9373188e
190 changed files with 4755 additions and 5510 deletions

View file

@ -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";

View file

@ -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 }
}

View 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",
],
)

View file

@ -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
}
}

View file

@ -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
}

View 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",
],
)

View 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
}
}

View 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)
}
}

View 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)
}
}

View 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",
],
)

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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 {

View 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",
],
)

View file

@ -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

View file

@ -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) {

View 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",
],
)

View file

@ -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)?

View file

@ -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 {

View file

@ -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

View file

@ -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:

View file

@ -20,6 +20,8 @@ swift_library(
"//submodules/PresentationDataUtils:PresentationDataUtils",
"//submodules/TextFormat:TextFormat",
"//submodules/ObjCRuntimeUtils:ObjCRuntimeUtils",
"//submodules/AttachmentUI:AttachmentUI",
"//submodules/TextInputMenu:TextInputMenu",
],
visibility = [
"//visibility:public",

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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()
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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)
}
}

View file

@ -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"
}
}

View file

@ -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)

View file

@ -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 {

View file

@ -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)

View file

@ -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()

View file

@ -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

View file

@ -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

View file

@ -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>

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -1,5 +1,5 @@
#import <Foundation/Foundation.h>
#import <LegacyComponents/TGMediaPickerCaptionInputPanel.h>
#import <UIKit/UIKit.h>
@class TGSuggestionContext;
@protocol TGPhotoPaintStickersContext;

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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)

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -28,6 +28,7 @@ swift_library(
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
"//submodules/StickerResources:StickerResources",
"//submodules/TextFormat:TextFormat",
"//submodules/AttachmentUI:AttachmentUI",
],
visibility = [
"//visibility:public",

View file

@ -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()

View file

@ -20,6 +20,7 @@ swift_library(
"//submodules/DeviceAccess:DeviceAccess",
"//submodules/LegacyComponents:LegacyComponents",
"//submodules/StickerResources:StickerResources",
"//submodules/AttachmentUI:AttachmentUI",
],
visibility = [
"//visibility:public",

View file

@ -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

View file

@ -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)
}
}

View file

@ -42,6 +42,7 @@ swift_library(
"//submodules/TelegramNotices:TelegramNotices",
"//submodules/TooltipUI:TooltipUI",
"//submodules/UndoUI:UndoUI",
"//submodules/AttachmentUI:AttachmentUI",
],
visibility = [
"//visibility:public",

View file

@ -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()
}
}

View file

@ -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 {

View file

@ -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))

View 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",
],
)

View file

@ -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)
}

View file

@ -35,7 +35,7 @@
@interface RMIntroViewController : UIViewController<UIScrollViewDelegate, GLKViewDelegate>
{
EAGLContext *context;
EAGLContext *_context;
GLKView *_glkView;

View file

@ -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;

View file

@ -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;

View file

@ -65,6 +65,8 @@ public enum PresentationResourceKey: Int32 {
case itemListBlockDestructiveIcon
case itemListAddDeviceIcon
case itemListResetIcon
case itemListImageIcon
case itemListCloudIcon
case itemListVoiceCallIcon
case itemListVideoCallIcon

View file

@ -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

View file

@ -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,

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Property 1=Camera.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View 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

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "cloud_30.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View 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

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Union-2.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View 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

View file

@ -0,0 +1,9 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"provides-namespace" : true
}
}

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Subtract.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View 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

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Union.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View 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

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "image_30.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View 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

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Subtract-2.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View 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