Various fixes

This commit is contained in:
Ilya Laktyushin 2022-07-22 17:46:09 +03:00
parent 237f86f7ca
commit e1b4af1461
27 changed files with 329 additions and 111 deletions

View File

@ -322,7 +322,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
private var spoilersRevealed = false
private var emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?
public var emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?
private let animationCache: AnimationCache
private let animationRenderer: MultiAnimationRenderer

View File

@ -675,6 +675,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
}, schedule: { [weak textInputPanelNode] in
textInputPanelNode?.sendMessage(.schedule)
})
controller.emojiViewProvider = textInputPanelNode.emojiViewProvider
strongSelf.presentInGlobalOverlay(controller)
}, openScheduledMessages: {
}, openPeersNearby: {

View File

@ -489,6 +489,9 @@ private func availablePaymentMethods(form: BotPaymentForm, current: BotCheckoutP
methods.append(.savedCredentials(savedCredentials))
}
}
if !form.additionalPaymentMethods.isEmpty {
methods.append(contentsOf: form.additionalPaymentMethods.map { .other($0) })
}
return methods
}
@ -1416,6 +1419,9 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
}
})
return
case let .other(method):
let _ = method
return
}
}

View File

@ -17,6 +17,7 @@ enum BotCheckoutPaymentMethod: Equatable {
case savedCredentials(BotPaymentSavedCredentials)
case webToken(BotCheckoutPaymentWebToken)
case applePay
case other(BotPaymentMethod)
var title: String {
switch self {
@ -29,6 +30,8 @@ enum BotCheckoutPaymentMethod: Equatable {
return token.title
case .applePay:
return "Apple Pay"
case let .other(method):
return method.title
}
}
}
@ -68,6 +71,9 @@ final class BotCheckoutPaymentMethodSheetController: ActionSheetController {
case .applePay:
title = "Apple Pay"
icon = UIImage(bundleImageName: "Bot Payments/ApplePayLogo")?.precomposed()
case let .other(method):
title = method.title
icon = nil
}
let value: Bool?
if let currentMethod = currentMethod {

View File

@ -4,18 +4,23 @@ import SwiftSignalKit
public enum ChatTextInputAccessoryItem: Equatable {
public enum Key: Hashable {
case keyboard
case stickers
case inputButtons
case input
case botInput
case commands
case silentPost
case messageAutoremoveTimeout
case scheduledMessages
}
public enum InputMode: Hashable {
case keyboard
case stickers(isEnabled: Bool, isEmoji: Bool)
case inputButtons
case stickers
case emoji
case bot
}
case input(isEnabled: Bool, inputMode: InputMode)
case botInput(isEnabled: Bool, inputMode: InputMode)
case commands
case silentPost(Bool)
case messageAutoremoveTimeout(Int32?)
@ -23,12 +28,10 @@ public enum ChatTextInputAccessoryItem: Equatable {
public var key: Key {
switch self {
case .keyboard:
return .keyboard
case .stickers:
return .stickers
case .inputButtons:
return .inputButtons
case .input:
return .input
case .botInput:
return .botInput
case .commands:
return .commands
case .silentPost:

View File

@ -19,6 +19,7 @@ swift_library(
"//submodules/ChatPresentationInterfaceState:ChatPresentationInterfaceState",
"//submodules/ContextUI:ContextUI",
"//submodules/AppBundle:AppBundle",
"//submodules/TextFormat:TextFormat",
],
visibility = [
"//visibility:public",

View File

@ -8,6 +8,7 @@ import AccountContext
import ContextUI
import TelegramCore
import ChatPresentationInterfaceState
import TextFormat
public final class ChatSendMessageActionSheetController: ViewController {
private var controllerNode: ChatSendMessageActionSheetControllerNode {
@ -33,6 +34,8 @@ public final class ChatSendMessageActionSheetController: ViewController {
private let hapticFeedback = HapticFeedback()
public var emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, interfaceState: ChatPresentationInterfaceState, gesture: ContextGesture, sourceSendButton: ASDisplayNode, textInputNode: EditableTextNode, attachment: Bool = false, completion: @escaping () -> Void, sendMessage: @escaping (Bool) -> Void, schedule: @escaping () -> Void) {
self.context = context
self.interfaceState = interfaceState
@ -79,13 +82,18 @@ public final class ChatSendMessageActionSheetController: ViewController {
var reminders = false
var isSecret = false
var canSchedule = false
var hasEntityKeyboard = false
if case let .peer(peerId) = self.interfaceState.chatLocation {
reminders = peerId == context.account.peerId
isSecret = peerId.namespace == Namespaces.Peer.SecretChat
canSchedule = !isSecret
}
self.displayNode = ChatSendMessageActionSheetControllerNode(context: self.context, presentationData: self.presentationData, reminders: reminders, gesture: gesture, sourceSendButton: self.sourceSendButton, textInputNode: self.textInputNode, attachment: self.attachment, forwardedCount: forwardedCount, send: { [weak self] in
if case .media = self.interfaceState.inputMode {
hasEntityKeyboard = true
}
self.displayNode = ChatSendMessageActionSheetControllerNode(context: self.context, presentationData: self.presentationData, reminders: reminders, gesture: gesture, sourceSendButton: self.sourceSendButton, textInputNode: self.textInputNode, attachment: self.attachment, forwardedCount: forwardedCount, hasEntityKeyboard: hasEntityKeyboard, send: { [weak self] in
self?.sendMessage(false)
self?.dismiss(cancel: false)
}, sendSilently: { [weak self] in

View File

@ -9,6 +9,7 @@ import TelegramPresentationData
import AccountContext
import AppBundle
import ContextUI
import TextFormat
private let leftInset: CGFloat = 16.0
private let rightInset: CGFloat = 16.0
@ -158,6 +159,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
private let textInputNode: EditableTextNode
private let attachment: Bool
private let forwardedCount: Int?
private let hasEntityKeyboard: Bool
private let send: (() -> Void)?
private let cancel: (() -> Void)?
@ -183,7 +185,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
private var animateInputField = false
init(context: AccountContext, presentationData: PresentationData, reminders: Bool, gesture: ContextGesture, sourceSendButton: ASDisplayNode, textInputNode: EditableTextNode, attachment: Bool, forwardedCount: Int?, send: (() -> Void)?, sendSilently: (() -> Void)?, schedule: (() -> Void)?, cancel: (() -> Void)?) {
init(context: AccountContext, presentationData: PresentationData, reminders: Bool, gesture: ContextGesture, sourceSendButton: ASDisplayNode, textInputNode: EditableTextNode, attachment: Bool, forwardedCount: Int?, hasEntityKeyboard: Bool, send: (() -> Void)?, sendSilently: (() -> Void)?, schedule: (() -> Void)?, cancel: (() -> Void)?) {
self.context = context
self.presentationData = presentationData
self.sourceSendButton = sourceSendButton
@ -191,6 +193,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
self.textInputNode = textInputNode
self.attachment = attachment
self.forwardedCount = forwardedCount
self.hasEntityKeyboard = hasEntityKeyboard
self.send = send
self.cancel = cancel
@ -422,7 +425,11 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
let fromFrame = CGRect(origin: CGPoint(), size: CGSize(width: initialWidth, height: self.textFieldFrame.height + 2.0))
let delta = (fromFrame.height - self.messageClipNode.bounds.height) / 2.0
let inputHeight = layout.inputHeight ?? 0.0
var inputHeight = layout.inputHeight ?? 0.0
if self.hasEntityKeyboard {
inputHeight = layout.standardInputHeight
}
var clipDelta = delta
if inputHeight.isZero || layout.isNonExclusive {
clipDelta -= self.contentContainerNode.frame.height + 16.0
@ -531,7 +538,11 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
let delta = (toFrame.height - self.messageClipNode.bounds.height) / 2.0
if cancel && self.animateInputField {
let inputHeight = layout.inputHeight ?? 0.0
var inputHeight = layout.inputHeight ?? 0.0
if self.hasEntityKeyboard {
inputHeight = layout.standardInputHeight
}
var clipDelta = delta
if inputHeight.isZero || layout.isNonExclusive {
clipDelta -= self.contentContainerNode.frame.height + 16.0
@ -589,8 +600,12 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
let menuHeightWithInset = contentSize.height + 16.0
let insets = layout.insets(options: [.statusBar, .input])
let inputHeight = layout.inputHeight ?? 0.0
var insets = layout.insets(options: [.statusBar, .input])
var inputHeight = layout.inputHeight ?? 0.0
if self.hasEntityKeyboard {
insets.bottom = max(insets.bottom, layout.standardInputHeight)
inputHeight = layout.standardInputHeight
}
let contentOffset = self.scrollNode.view.contentOffset.y

View File

@ -143,6 +143,7 @@ public final class LottieAnimationComponent: Component {
}
strongSelf.didPlayToCompletion = true
let _ = strongSelf.update(component: component, availableSize: availableSize, transition: transition)
strongSelf.currentCompletion = nil
}
animationView.loopMode = .playOnce
} else {
@ -175,9 +176,9 @@ public final class LottieAnimationComponent: Component {
updatePlayback = true
}
}
}
} else {
self.component = component
}
if updateColors, let animationView = self.animationView {
if let value = component.colors["__allcolors__"] {

View File

@ -122,12 +122,14 @@ open class TooltipController: ViewController, StandalonePresentableController {
private let dismissImmediatelyOnLayoutUpdate: Bool
private var timeoutTimer: SwiftSignalKit.Timer?
private var padding: CGFloat
private var layout: ContainerViewLayout?
private var initialArrowOnBottom: Bool
public var dismissed: ((Bool) -> Void)?
public init(content: TooltipControllerContent, baseFontSize: CGFloat, timeout: Double = 2.0, dismissByTapOutside: Bool = false, dismissByTapOutsideSource: Bool = false, dismissImmediatelyOnLayoutUpdate: Bool = false, arrowOnBottom: Bool = true) {
public init(content: TooltipControllerContent, baseFontSize: CGFloat, timeout: Double = 2.0, dismissByTapOutside: Bool = false, dismissByTapOutsideSource: Bool = false, dismissImmediatelyOnLayoutUpdate: Bool = false, arrowOnBottom: Bool = true, padding: CGFloat = 8.0) {
self.content = content
self.baseFontSize = baseFontSize
self.timeout = timeout
@ -135,6 +137,7 @@ open class TooltipController: ViewController, StandalonePresentableController {
self.dismissByTapOutsideSource = dismissByTapOutsideSource
self.dismissImmediatelyOnLayoutUpdate = dismissImmediatelyOnLayoutUpdate
self.initialArrowOnBottom = arrowOnBottom
self.padding = padding
super.init(navigationBarPresentationData: nil)
@ -153,6 +156,7 @@ open class TooltipController: ViewController, StandalonePresentableController {
self.displayNode = TooltipControllerNode(content: self.content, baseFontSize: self.baseFontSize, dismiss: { [weak self] tappedInside in
self?.dismiss(tappedInside: tappedInside)
}, dismissByTapOutside: self.dismissByTapOutside, dismissByTapOutsideSource: self.dismissByTapOutsideSource)
self.controllerNode.padding = self.padding
self.controllerNode.arrowOnBottom = self.initialArrowOnBottom
self.displayNodeDidLoad()
}

View File

@ -19,6 +19,8 @@ final class TooltipControllerNode: ASDisplayNode {
var sourceRect: CGRect?
var arrowOnBottom: Bool = true
var padding: CGFloat = 8.0
private var dismissedByTouchOutside = false
private var dismissByTapOutsideSource = false
@ -122,7 +124,7 @@ final class TooltipControllerNode: ASDisplayNode {
}
self.arrowOnBottom = arrowOnBottom
let horizontalOrigin: CGFloat = floor(min(max(8.0, sourceRect.midX - contentSize.width / 2.0), layout.size.width - contentSize.width - 8.0))
let horizontalOrigin: CGFloat = floor(min(max(self.padding, sourceRect.midX - contentSize.width / 2.0), layout.size.width - contentSize.width - self.padding))
transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(x: horizontalOrigin, y: verticalOrigin), size: contentSize))
self.containerNode.relativeArrowPosition = (sourceRect.midX - horizontalOrigin, arrowOnBottom)

View File

@ -373,8 +373,17 @@ private final class GiftComponent: CombinedComponent {
)
let size = CGSize(width: context.availableSize.width, height: insets.top + title.size.height + spacing + subtitle.size.height + insets.bottom)
let distance = context.availableSize.width - insets.left - insets.right - label.size.width - subtitle.size.width - discountSize.width - 7.0
let labelOriginY: CGFloat
if distance > 8.0 {
labelOriginY = size.height / 2.0
} else {
labelOriginY = insets.top + title.size.height / 2.0
}
context.add(label
.position(CGPoint(x: context.availableSize.width - insets.right - label.size.width / 2.0, y: size.height / 2.0))
.position(CGPoint(x: context.availableSize.width - insets.right - label.size.width / 2.0, y: labelOriginY))
)
context.add(check

View File

@ -348,6 +348,12 @@ final class StickerPackEmojisItemNode: GridItemNode {
self.visibleItemLayers[id] = nil
}
}
for id in self.visibleItemPlaceholderViews.keys {
if !validIds.contains(id) {
self.visibleItemPlaceholderViews[id]?.removeFromSuperview()
self.visibleItemPlaceholderViews[id] = nil
}
}
}
private func updateShimmerIfNeeded() {

View File

@ -120,6 +120,21 @@ public struct BotPaymentForm : Equatable {
public let nativeProvider: BotPaymentNativeProvider?
public let savedInfo: BotPaymentRequestedInfo?
public let savedCredentials: BotPaymentSavedCredentials?
public let additionalPaymentMethods: [BotPaymentMethod]
}
public struct BotPaymentMethod: Equatable {
public let url: String
public let title: String
}
extension BotPaymentMethod {
init(apiPaymentFormMethod: Api.PaymentFormMethod) {
switch apiPaymentFormMethod {
case let .paymentFormMethod(url, title):
self.init(url: url, title: title)
}
}
}
public enum BotPaymentFormRequestError {
@ -270,7 +285,6 @@ func _internal_fetchBotPaymentForm(postbox: Postbox, network: Network, source: B
let _ = title
let _ = description
let _ = photo
let _ = additionalMethods
var peers: [Peer] = []
for user in apiUsers {
@ -297,7 +311,9 @@ func _internal_fetchBotPaymentForm(postbox: Postbox, network: Network, source: B
parsedSavedCredentials = .card(id: id, title: title)
}
}
return BotPaymentForm(id: id, canSaveCredentials: (flags & (1 << 2)) != 0, passwordMissing: (flags & (1 << 3)) != 0, invoice: parsedInvoice, paymentBotId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(botId)), providerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(providerId)), url: url, nativeProvider: parsedNativeProvider, savedInfo: parsedSavedInfo, savedCredentials: parsedSavedCredentials)
let additionalPaymentMethods = additionalMethods?.map({ BotPaymentMethod(apiPaymentFormMethod: $0) }) ?? []
return BotPaymentForm(id: id, canSaveCredentials: (flags & (1 << 2)) != 0, passwordMissing: (flags & (1 << 3)) != 0, invoice: parsedInvoice, paymentBotId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(botId)), providerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(providerId)), url: url, nativeProvider: parsedNativeProvider, savedInfo: parsedSavedInfo, savedCredentials: parsedSavedCredentials, additionalPaymentMethods: additionalPaymentMethods)
}
}
|> mapError { _ -> BotPaymentFormRequestError in }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1127,6 +1127,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let packReference = packReferences.first, let strongSelf = self else {
return
}
strongSelf.chatDisplayNode.dismissTextInput()
let controller = StickerPackScreen(context: context, updatedPresentationData: strongSelf.updatedPresentationData, mainStickerPack: packReference, stickerPacks: Array(packReferences), parentNavigationController: strongSelf.effectiveNavigationController)
strongSelf.present(controller, in: .window(.root))
}
@ -8446,6 +8447,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.controllerInteraction?.scheduleCurrentMessage()
}
})
controller.emojiViewProvider = strongSelf.chatDisplayNode.textInputPanelNode?.emojiViewProvider
strongSelf.sendMessageActionsController = controller
if layout.isNonExclusive {
strongSelf.present(controller, in: .window(.root))
@ -15526,7 +15528,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let tooltipController = self.mediaRecordingModeTooltipController {
tooltipController.updateContent(.text(text), animated: true, extendTimer: true)
} else if let rect = rect {
let tooltipController = TooltipController(content: .text(text), baseFontSize: self.presentationData.listsFontSize.baseDisplaySize)
let tooltipController = TooltipController(content: .text(text), baseFontSize: self.presentationData.listsFontSize.baseDisplaySize, padding: 2.0)
self.mediaRecordingModeTooltipController = tooltipController
tooltipController.dismissed = { [weak self, weak tooltipController] _ in
if let strongSelf = self, let tooltipController = tooltipController, strongSelf.mediaRecordingModeTooltipController === tooltipController {
@ -15547,7 +15549,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return
}
self.sendingOptionsTooltipController?.dismiss()
let tooltipController = TooltipController(content: .text(self.presentationData.strings.Conversation_SendingOptionsTooltip), baseFontSize: self.presentationData.listsFontSize.baseDisplaySize, timeout: 3.0, dismissByTapOutside: true, dismissImmediatelyOnLayoutUpdate: true)
let tooltipController = TooltipController(content: .text(self.presentationData.strings.Conversation_SendingOptionsTooltip), baseFontSize: self.presentationData.listsFontSize.baseDisplaySize, timeout: 3.0, dismissByTapOutside: true, dismissImmediatelyOnLayoutUpdate: true, padding: 2.0)
self.sendingOptionsTooltipController = tooltipController
tooltipController.dismissed = { [weak self, weak tooltipController] _ in
if let strongSelf = self, let tooltipController = tooltipController, strongSelf.sendingOptionsTooltipController === tooltipController {

View File

@ -314,13 +314,13 @@ func inputTextPanelStateForChatPresentationInterfaceState(_ chatPresentationInte
switch chatPresentationInterfaceState.inputMode {
case .media:
accessoryItems.append(.keyboard)
accessoryItems.append(.input(isEnabled: true, inputMode: .keyboard))
return ChatTextInputPanelState(accessoryItems: accessoryItems, contextPlaceholder: contextPlaceholder, mediaRecordingState: chatPresentationInterfaceState.inputTextPanelState.mediaRecordingState)
case .inputButtons:
return ChatTextInputPanelState(accessoryItems: [.keyboard], contextPlaceholder: contextPlaceholder, mediaRecordingState: chatPresentationInterfaceState.inputTextPanelState.mediaRecordingState)
return ChatTextInputPanelState(accessoryItems: [.botInput(isEnabled: true, inputMode: .keyboard)], contextPlaceholder: contextPlaceholder, mediaRecordingState: chatPresentationInterfaceState.inputTextPanelState.mediaRecordingState)
case .none, .text:
if let _ = chatPresentationInterfaceState.interfaceState.editMessage {
accessoryItems.append(.stickers(isEnabled: true, isEmoji: true))
accessoryItems.append(.input(isEnabled: true, inputMode: .emoji))
return ChatTextInputPanelState(accessoryItems: accessoryItems, contextPlaceholder: contextPlaceholder, mediaRecordingState: chatPresentationInterfaceState.inputTextPanelState.mediaRecordingState)
} else {
@ -371,13 +371,13 @@ func inputTextPanelStateForChatPresentationInterfaceState(_ chatPresentationInte
}
if stickersEnabled {
accessoryItems.append(.stickers(isEnabled: true, isEmoji: stickersAreEmoji))
accessoryItems.append(.input(isEnabled: true, inputMode: stickersAreEmoji ? .emoji : .stickers))
} else {
accessoryItems.append(.stickers(isEnabled: true, isEmoji: true))
accessoryItems.append(.input(isEnabled: true, inputMode: .emoji))
}
if isTextEmpty, let message = chatPresentationInterfaceState.keyboardButtonsMessage, let _ = message.visibleButtonKeyboardMarkup, chatPresentationInterfaceState.interfaceState.messageActionsState.dismissedButtonKeyboardMessageId != message.id {
accessoryItems.append(.inputButtons)
accessoryItems.append(.botInput(isEnabled: true, inputMode: .bot))
}
}
return ChatTextInputPanelState(accessoryItems: accessoryItems, contextPlaceholder: contextPlaceholder, mediaRecordingState: chatPresentationInterfaceState.inputTextPanelState.mediaRecordingState)

View File

@ -10,6 +10,8 @@ import AccountContext
import ChatInterfaceState
import AudioBlob
import ChatPresentationInterfaceState
import ComponentFlow
import LottieAnimationComponent
private let offsetThreshold: CGFloat = 10.0
private let dismissOffsetThreshold: CGFloat = 70.0
@ -185,7 +187,7 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto
private var modeTimeoutTimer: SwiftSignalKit.Timer?
private let innerIconView: UIImageView
private let animationView: ComponentView<Empty>
private var recordingOverlay: ChatTextInputAudioRecordingOverlay?
private var startTouchLocation: CGPoint?
@ -288,7 +290,7 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto
init(theme: PresentationTheme, strings: PresentationStrings, presentController: @escaping (ViewController) -> Void) {
self.theme = theme
self.strings = strings
self.innerIconView = UIImageView()
self.animationView = ComponentView<Empty>()
self.presentController = presentController
super.init(frame: CGRect())
@ -297,8 +299,6 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto
self.pallete = legacyInputMicPalette(from: theme)
self.insertSubview(self.innerIconView, at: 0)
self.disablesInteractiveTransitionGestureRecognizer = true
self.updateMode(mode: self.mode, animated: false, force: true)
@ -318,51 +318,81 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto
}
private func updateMode(mode: ChatTextInputMediaRecordingButtonMode, animated: Bool, force: Bool) {
let previousMode = self.mode
if mode != self.mode || force {
self.mode = mode
if animated {
let previousView = UIImageView(image: self.innerIconView.image)
previousView.frame = self.innerIconView.frame
self.addSubview(previousView)
previousView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
previousView.layer.animateScale(from: 1.0, to: 0.3, duration: 0.15, removeOnCompletion: false, completion: { [weak previousView] _ in
previousView?.removeFromSuperview()
})
self.updateAnimation(previousMode: previousMode)
}
}
private func updateAnimation(previousMode: ChatTextInputMediaRecordingButtonMode) {
let image: UIImage?
switch self.mode {
case .audio:
self.icon = PresentationResourcesChat.chatInputPanelVoiceActiveButtonImage(self.theme)
self.innerIconView.image = PresentationResourcesChat.chatInputPanelVoiceButtonImage(self.theme)
image = PresentationResourcesChat.chatInputPanelVoiceButtonImage(self.theme)
case .video:
self.icon = PresentationResourcesChat.chatInputPanelVideoActiveButtonImage(self.theme)
self.innerIconView.image = PresentationResourcesChat.chatInputPanelVideoButtonImage(self.theme)
}
if let image = self.innerIconView.image {
let size = self.bounds.size
let iconSize = image.size
self.innerIconView.frame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)
image = PresentationResourcesChat.chatInputPanelVoiceButtonImage(self.theme)
}
if animated {
self.innerIconView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, removeOnCompletion: false)
self.innerIconView.layer.animateSpring(from: 0.4 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.4)
let size = self.bounds.size
let iconSize: CGSize
if let image = image {
iconSize = image.size
} else {
iconSize = size
}
let animationFrame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)
let animationName: String
switch self.mode {
case .audio:
animationName = "anim_videoToMic"
case .video:
animationName = "anim_micToVideo"
}
var animationMode: LottieAnimationComponent.AnimationItem.Mode = .still(position: .end)
if previousMode != mode {
animationMode = .animating(loop: false)
}
let colorKeys = ["__allcolors__"]
var colors: [String: UIColor] = [:]
for colorKey in colorKeys {
colors[colorKey] = self.theme.chat.inputPanel.panelControlColor
}
let _ = animationView.update(
transition: .immediate,
component: AnyComponent(LottieAnimationComponent(
animation: LottieAnimationComponent.AnimationItem(
name: animationName,
mode: animationMode
),
colors: colors,
size: animationFrame.size
)),
environment: {},
containerSize: animationFrame.size
)
// self.innerIconView.frame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)
if let view = animationView.view {
view.isUserInteractionEnabled = false
if view.superview == nil {
self.insertSubview(view, at: 0)
}
view.frame = animationFrame
}
}
func updateTheme(theme: PresentationTheme) {
self.theme = theme
switch self.mode {
case .audio:
self.icon = PresentationResourcesChat.chatInputPanelVoiceActiveButtonImage(self.theme)
self.innerIconView.image = PresentationResourcesChat.chatInputPanelVoiceButtonImage(self.theme)
case .video:
self.icon = PresentationResourcesChat.chatInputPanelVideoActiveButtonImage(self.theme)
self.innerIconView.image = PresentationResourcesChat.chatInputPanelVideoButtonImage(self.theme)
}
self.updateAnimation(previousMode: self.mode)
self.pallete = legacyInputMicPalette(from: theme)
self.micDecorationValue?.setColor(self.theme.chat.inputPanel.actionControlFillColor)
@ -467,8 +497,8 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto
micDecoration.isHidden = false
micDecoration.startAnimating()
innerIconView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
innerIconView.layer.animateScale(from: 1.0, to: 0.3, duration: 0.15, removeOnCompletion: false)
self.animationView.view?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
self.animationView.view?.layer.animateScale(from: 1.0, to: 0.3, duration: 0.15, removeOnCompletion: false)
}
override func animateOut(_ toSmallSize: Bool) {
@ -480,8 +510,8 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto
micDecoration.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.03, delay: 0.15, removeOnCompletion: false)
} else {
micDecoration.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, removeOnCompletion: false)
innerIconView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, removeOnCompletion: false)
innerIconView.layer.animateScale(from: 0.3, to: 1.0, duration: 0.15, removeOnCompletion: false)
self.animationView.view?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, removeOnCompletion: false)
self.animationView.view?.layer.animateScale(from: 0.3, to: 1.0, duration: 0.15, removeOnCompletion: false)
}
}
@ -490,8 +520,10 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto
let size = self.bounds.size
if size != self.previousSize {
self.previousSize = size
let iconSize = self.innerIconView.bounds.size
self.innerIconView.frame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)
if let view = self.animationView.view {
let iconSize = view.bounds.size
view.frame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)
}
}
}
}

View File

@ -59,9 +59,12 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode {
self.iconImageNode.isUserInteractionEnabled = false
self.addSubnode(self.iconImageNode)
if case .stickers = item {
switch item {
case .input, .botInput:
self.iconImageNode.isHidden = true
self.animationView = ComponentView<Empty>()
default:
break
}
if let text = text {
@ -116,12 +119,15 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode {
static func imageAndInsets(item: ChatTextInputAccessoryItem, theme: PresentationTheme, strings: PresentationStrings) -> (UIImage?, String?, String, CGFloat, UIEdgeInsets) {
switch item {
case let .input(isEnabled, inputMode), let .botInput(isEnabled, inputMode):
switch inputMode {
case .keyboard:
return (PresentationResourcesChat.chatInputTextFieldKeyboardImage(theme), nil, strings.VoiceOver_Keyboard, 1.0, UIEdgeInsets())
case let .stickers(enabled, _):
return (PresentationResourcesChat.chatInputTextFieldStickersImage(theme), nil, strings.VoiceOver_Stickers, enabled ? 1.0 : 0.4, UIEdgeInsets())
case .inputButtons:
case .stickers, .emoji:
return (PresentationResourcesChat.chatInputTextFieldStickersImage(theme), nil, strings.VoiceOver_Stickers, isEnabled ? 1.0 : 0.4, UIEdgeInsets())
case .bot:
return (PresentationResourcesChat.chatInputTextFieldInputButtonsImage(theme), nil, strings.VoiceOver_BotKeyboard, 1.0, UIEdgeInsets())
}
case .commands:
return (PresentationResourcesChat.chatInputTextFieldCommandsImage(theme), nil, strings.VoiceOver_BotCommands, 1.0, UIEdgeInsets())
case let .silentPost(value):
@ -143,7 +149,7 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode {
static func calculateWidth(item: ChatTextInputAccessoryItem, image: UIImage?, text: String?, strings: PresentationStrings) -> CGFloat {
switch item {
case .keyboard, .stickers, .inputButtons, .silentPost, .commands, .scheduledMessages:
case .input, .botInput, .silentPost, .commands, .scheduledMessages:
return 32.0
case let .messageAutoremoveTimeout(timeout):
var imageWidth = (image?.size.width ?? 0.0) + CGFloat(8.0)
@ -156,6 +162,7 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode {
}
func updateLayout(item: ChatTextInputAccessoryItem, size: CGSize) {
let previousItem = self.item
self.item = item
if let image = self.iconImageNode.image {
@ -165,26 +172,112 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode {
let imageFrame = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0) - bottomInset), size: image.size)
self.iconImageNode.frame = imageFrame
if case let .stickers(_, isEmoji) = item, let animationView = self.animationView {
if let animationView = self.animationView {
let animationFrame = imageFrame.insetBy(dx: -4.0, dy: -4.0)
var colors: [String: UIColor] = [:]
let colorKeys: [String] = [
var previousInputMode: ChatTextInputAccessoryItem.InputMode?
var inputMode: ChatTextInputAccessoryItem.InputMode?
switch previousItem {
case let .input(_, itemInputMode), let .botInput(_, itemInputMode):
previousInputMode = itemInputMode
default:
break
}
switch item {
case let .input(_, itemInputMode), let .botInput(_, itemInputMode):
inputMode = itemInputMode
default:
break
}
let emojiColorKeys = [
"Ellipse 33.Ellipse 33.Stroke 1",
"Ellipse 34.Ellipse 34.Stroke 1",
"Oval.Oval.Fill 1",
"Oval 2.Oval.Fill 1",
"Path 85.Path 85.Stroke 1"
]
var colorKeys: [String] = ["__allcolors__"]
let animationName: String
var animationMode: LottieAnimationComponent.AnimationItem.Mode = .still(position: .end)
if let inputMode = inputMode {
switch inputMode {
case .keyboard:
if let previousInputMode = previousInputMode {
if case .stickers = previousInputMode {
animationName = "anim_stickerToKey"
animationMode = .animating(loop: false)
} else if case .emoji = previousInputMode {
animationName = "anim_smileToKey"
animationMode = .animating(loop: false)
} else if case .bot = previousInputMode {
animationName = "anim_botToKey"
animationMode = .animating(loop: false)
} else {
animationName = "anim_stickerToKey"
}
} else {
animationName = "anim_stickerToKey"
}
case .stickers:
if let previousInputMode = previousInputMode {
if case .keyboard = previousInputMode {
animationName = "anim_keyToSticker"
animationMode = .animating(loop: false)
} else if case .emoji = previousInputMode {
animationName = "anim_smileToSticker"
animationMode = .animating(loop: false)
colorKeys = emojiColorKeys
} else {
animationName = "anim_keyToSticker"
}
} else {
animationName = "anim_keyToSticker"
}
case .emoji:
if let previousInputMode = previousInputMode {
if case .keyboard = previousInputMode {
animationName = "anim_keyToSmile"
animationMode = .animating(loop: false)
} else if case .stickers = previousInputMode {
animationName = "anim_stickerToSmile"
animationMode = .animating(loop: false)
colorKeys = emojiColorKeys
} else {
animationName = "anim_keyToSmile"
}
} else {
animationName = "anim_keyToSmile"
}
case .bot:
if let previousInputMode = previousInputMode {
if case .keyboard = previousInputMode {
animationName = "anim_keyToBot"
animationMode = .animating(loop: false)
} else {
animationName = "anim_keyToBot"
}
} else {
animationName = "anim_keyToBot"
}
}
} else {
animationName = ""
}
var colors: [String: UIColor] = [:]
for colorKey in colorKeys {
colors[colorKey] = self.theme.chat.inputPanel.inputControlColor
}
let _ = animationView.update(
transition: .immediate,
component: AnyComponent(LottieAnimationComponent(
animation: LottieAnimationComponent.AnimationItem(
name: !isEmoji ? "anim_stickertosmile" : "anim_smiletosticker",
mode: .animateTransitionFromPrevious
name: animationName,
mode: animationMode
),
colors: colors,
size: animationFrame.size
@ -193,6 +286,7 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode {
containerSize: animationFrame.size
)
if let view = animationView.view {
view.isUserInteractionEnabled = false
if view.superview == nil {
self.view.addSubview(view)
}
@ -586,7 +680,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
private var touchDownGestureRecognizer: TouchDownGestureRecognizer?
private var emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?
var emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?
init(presentationInterfaceState: ChatPresentationInterfaceState, presentationContext: ChatPresentationContext?, presentController: @escaping (ViewController) -> Void) {
self.presentationInterfaceState = presentationInterfaceState
@ -2942,20 +3036,23 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
for (item, currentButton) in self.accessoryItemButtons {
if currentButton === button {
switch item {
case let .stickers(enabled, _):
if enabled {
self.interfaceInteraction?.openStickers()
} else {
self.interfaceInteraction?.displayRestrictedInfo(.stickers, .tooltip)
}
case let .input(isEnabled, inputMode), let .botInput(isEnabled, inputMode):
switch inputMode {
case .keyboard:
self.interfaceInteraction?.updateInputModeAndDismissedButtonKeyboardMessageId({ state in
return (.text, state.keyboardButtonsMessage?.id)
})
case .inputButtons:
case .stickers, .emoji:
if isEnabled {
self.interfaceInteraction?.openStickers()
} else {
self.interfaceInteraction?.displayRestrictedInfo(.stickers, .tooltip)
}
case .bot:
self.interfaceInteraction?.updateInputModeAndDismissedButtonKeyboardMessageId({ state in
return (.inputButtons, nil)
})
}
case .commands:
self.interfaceInteraction?.updateTextInputStateAndMode { _, inputMode in
return (ChatTextInputState(inputText: NSAttributedString(string: "/")), .text)
@ -3011,7 +3108,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
func frameForStickersButton() -> CGRect? {
for (item, button) in self.accessoryItemButtons {
if case .stickers = item {
if case let .input(_, inputMode) = item, case .stickers = inputMode {
return button.frame.insetBy(dx: 0.0, dy: 6.0)
}
}

View File

@ -329,6 +329,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
}, schedule: { [weak textInputPanelNode] in
textInputPanelNode?.sendMessage(.schedule)
})
controller.emojiViewProvider = textInputPanelNode.emojiViewProvider
strongSelf.presentInGlobalOverlay(controller, nil)
}, openScheduledMessages: {
}, openPeersNearby: {