Various Fixes

This commit is contained in:
Ilya Laktyushin 2021-06-17 23:14:23 +03:00
parent 0e225c0226
commit d646ee3492
17 changed files with 338 additions and 97 deletions

View File

@ -527,92 +527,100 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, UIScroll
self.cancel?()
}
private func createStickerSet(title: String, shortName: String) {
guard let stickerPack = self.stickerPack else {
return
}
var stickers: [ImportSticker] = []
for item in self.currentItems {
var dimensions = PixelDimensions(width: 512, height: 512)
if case let .image(data) = item.stickerItem.content, let image = UIImage(data: data) {
dimensions = PixelDimensions(image.size)
}
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
self.context.account.postbox.mediaBox.storeResourceData(resource.id, data: item.stickerItem.data)
stickers.append(ImportSticker(resource: resource, emojis: item.stickerItem.emojis, dimensions: dimensions))
}
var thumbnailSticker: ImportSticker?
if let thumbnail = stickerPack.thumbnail {
var dimensions = PixelDimensions(width: 512, height: 512)
if case let .image(data) = thumbnail.content, let image = UIImage(data: data) {
dimensions = PixelDimensions(image.size)
}
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
self.context.account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnail.data)
thumbnailSticker = ImportSticker(resource: resource, emojis: [], dimensions: dimensions)
}
self.progress = (0.0, 0, Int32(stickers.count))
self.radialStatus.transitionToState(.progress(color: self.presentationData.theme.list.itemAccentColor, lineWidth: 6.0, value: max(0.01, 0.0), cancelEnabled: false, animateRotation: false), animated: false, synchronous: true, completion: {})
if let (layout, navigationBarHeight) = self.containerLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.2, curve: .easeInOut))
}
self.disposable.set((self.context.engine.stickers.createStickerSet(title: title, shortName: shortName, stickers: stickers, thumbnail: thumbnailSticker, isAnimated: stickerPack.isAnimated)
|> deliverOnMainQueue).start(next: { [weak self] status in
if let strongSelf = self {
if case let .complete(info, items) = status {
if let (_, _, count) = strongSelf.progress {
strongSelf.progress = (1.0, count, count)
if let (layout, navigationBarHeight) = strongSelf.containerLayout {
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
}
}
let _ = strongSelf.context.engine.stickers.addStickerPackInteractively(info: info, items: items).start()
strongSelf.radialCheck.transitionToState(.progress(color: .clear, lineWidth: 6.0, value: 1.0, cancelEnabled: false, animateRotation: false), animated: false, synchronous: true, completion: {})
strongSelf.radialCheck.transitionToState(.check(strongSelf.presentationData.theme.list.itemAccentColor), animated: true, synchronous: true, completion: {})
strongSelf.radialStatus.layer.animateScale(from: 1.0, to: 1.05, duration: 0.07, delay: 0.0, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false, additive: false, completion: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.radialStatus.layer.animateScale(from: 1.05, to: 1.0, duration: 0.07, delay: 0.0, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, additive: false)
})
strongSelf.radialStatusBackground.layer.animateScale(from: 1.0, to: 1.05, duration: 0.07, delay: 0.0, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false, additive: false, completion: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.radialStatusBackground.layer.animateScale(from: 1.05, to: 1.0, duration: 0.07, delay: 0.0, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, additive: false)
})
strongSelf.radialCheck.layer.animateScale(from: 1.0, to: 1.05, duration: 0.07, delay: 0.0, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false, additive: false, completion: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.radialCheck.layer.animateScale(from: 1.05, to: 1.0, duration: 0.07, delay: 0.0, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, additive: false)
})
strongSelf.radialStatusText.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
strongSelf.radialStatusText.layer.animateScale(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
strongSelf.cancelButtonNode.isUserInteractionEnabled = false
Queue.mainQueue().after(1.0) {
strongSelf.presentInGlobalOverlay?(UndoOverlayController(presentationData: strongSelf.presentationData, content: .stickersModified(title: strongSelf.presentationData.strings.StickerPackActionInfo_AddedTitle, text: strongSelf.presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, context: strongSelf.context), elevatedLayout: false, action: { _ in return true}), nil)
strongSelf.dismiss?()
}
} else if case let .progress(progress, count, total) = status {
strongSelf.progress = (CGFloat(progress), count, total)
strongSelf.radialStatus.transitionToState(.progress(color: strongSelf.presentationData.theme.list.itemAccentColor, lineWidth: 6.0, value: max(0.01, CGFloat(progress)), cancelEnabled: false, animateRotation: false), animated: true, synchronous: true, completion: {})
if let (layout, navigationBarHeight) = strongSelf.containerLayout {
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
}
}
}
}, error: { [weak self] error in
if let strongSelf = self {
}
}))
}
@objc func installActionButtonPressed() {
let controller = importStickerPackTitleController(sharedContext: self.context.sharedContext, account: self.context.account, title: self.presentationData.strings.ImportStickerPack_ChooseName, text: self.presentationData.strings.ImportStickerPack_ChooseNameDescription, placeholder: "", doneButtonTitle: nil, value: nil, maxLength: 128, apply: { [weak self] title in
if let strongSelf = self, let stickerPack = strongSelf.stickerPack, var title = title {
title = title.trimmingTrailingSpaces()
let shortName = title.replacingOccurrences(of: " ", with: "") + "_by_laktyushin"
var stickers: [ImportSticker] = []
for item in strongSelf.currentItems {
var dimensions = PixelDimensions(width: 512, height: 512)
if case let .image(data) = item.stickerItem.content, let image = UIImage(data: data) {
dimensions = PixelDimensions(image.size)
}
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
strongSelf.context.account.postbox.mediaBox.storeResourceData(resource.id, data: item.stickerItem.data)
stickers.append(ImportSticker(resource: resource, emojis: item.stickerItem.emojis, dimensions: dimensions))
}
var thumbnailSticker: ImportSticker?
if let thumbnail = stickerPack.thumbnail {
var dimensions = PixelDimensions(width: 512, height: 512)
if case let .image(data) = thumbnail.content, let image = UIImage(data: data) {
dimensions = PixelDimensions(image.size)
}
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
strongSelf.context.account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnail.data)
thumbnailSticker = ImportSticker(resource: resource, emojis: [], dimensions: dimensions)
}
strongSelf.progress = (0.0, 0, Int32(stickers.count))
strongSelf.radialStatus.transitionToState(.progress(color: strongSelf.presentationData.theme.list.itemAccentColor, lineWidth: 6.0, value: max(0.01, 0.0), cancelEnabled: false, animateRotation: false), animated: false, synchronous: true, completion: {})
if let (layout, navigationBarHeight) = strongSelf.containerLayout {
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.2, curve: .easeInOut))
}
strongSelf.disposable.set((createStickerSet(account: strongSelf.context.account, title: title, shortName: shortName, stickers: stickers, thumbnail: thumbnailSticker, isAnimated: stickerPack.isAnimated)
|> deliverOnMainQueue).start(next: { [weak self] status in
if let strongSelf = self {
if case let .complete(info, items) = status {
if let (_, _, count) = strongSelf.progress {
strongSelf.progress = (1.0, count, count)
if let (layout, navigationBarHeight) = strongSelf.containerLayout {
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
}
}
let _ = strongSelf.context.engine.stickers.addStickerPackInteractively(info: info, items: items).start()
strongSelf.radialCheck.transitionToState(.progress(color: .clear, lineWidth: 6.0, value: 1.0, cancelEnabled: false, animateRotation: false), animated: false, synchronous: true, completion: {})
strongSelf.radialCheck.transitionToState(.check(strongSelf.presentationData.theme.list.itemAccentColor), animated: true, synchronous: true, completion: {})
strongSelf.radialStatus.layer.animateScale(from: 1.0, to: 1.05, duration: 0.07, delay: 0.0, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false, additive: false, completion: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.radialStatus.layer.animateScale(from: 1.05, to: 1.0, duration: 0.07, delay: 0.0, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, additive: false)
})
strongSelf.radialStatusBackground.layer.animateScale(from: 1.0, to: 1.05, duration: 0.07, delay: 0.0, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false, additive: false, completion: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.radialStatusBackground.layer.animateScale(from: 1.05, to: 1.0, duration: 0.07, delay: 0.0, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, additive: false)
})
strongSelf.radialCheck.layer.animateScale(from: 1.0, to: 1.05, duration: 0.07, delay: 0.0, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false, additive: false, completion: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.radialCheck.layer.animateScale(from: 1.05, to: 1.0, duration: 0.07, delay: 0.0, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, additive: false)
})
strongSelf.radialStatusText.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
strongSelf.radialStatusText.layer.animateScale(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
strongSelf.cancelButtonNode.isUserInteractionEnabled = false
Queue.mainQueue().after(1.0) {
strongSelf.presentInGlobalOverlay?(UndoOverlayController(presentationData: strongSelf.presentationData, content: .stickersModified(title: strongSelf.presentationData.strings.StickerPackActionInfo_AddedTitle, text: strongSelf.presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, context: strongSelf.context), elevatedLayout: false, action: { _ in return true}), nil)
strongSelf.dismiss?()
}
} else if case let .progress(progress, count, total) = status {
strongSelf.progress = (CGFloat(progress), count, total)
strongSelf.radialStatus.transitionToState(.progress(color: strongSelf.presentationData.theme.list.itemAccentColor, lineWidth: 6.0, value: max(0.01, CGFloat(progress)), cancelEnabled: false, animateRotation: false), animated: true, synchronous: true, completion: {})
if let (layout, navigationBarHeight) = strongSelf.containerLayout {
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
}
}
}
}, error: { error in
if let strongSelf = self {
}
}))
}
})
self.present?(controller, nil)

View File

@ -15,6 +15,7 @@ private final class ImportStickerPackTitleInputFieldNode: ASDisplayNode, ASEdita
private let backgroundNode: ASImageNode
private let textInputNode: EditableTextNode
private let placeholderNode: ASTextNode
private let prefixNode: ASTextNode
private let clearButton: HighlightableButtonNode
var updateHeight: (() -> Void)?
@ -45,6 +46,12 @@ private final class ImportStickerPackTitleInputFieldNode: ASDisplayNode, ASEdita
}
}
var prefix: String = "" {
didSet {
self.prefixNode.attributedText = NSAttributedString(string: self.prefix, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputTextColor)
}
}
private let maxLength: Int
init(theme: PresentationTheme, placeholder: String, maxLength: Int, returnKeyType: UIReturnKeyType = .done) {
@ -74,6 +81,11 @@ private final class ImportStickerPackTitleInputFieldNode: ASDisplayNode, ASEdita
self.placeholderNode.displaysAsynchronously = false
self.placeholderNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor)
self.prefixNode = ASTextNode()
self.prefixNode.isUserInteractionEnabled = false
self.prefixNode.displaysAsynchronously = false
self.prefixNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor)
self.clearButton = HighlightableButtonNode()
self.clearButton.imageNode.displaysAsynchronously = false
self.clearButton.imageNode.displayWithoutProcessing = true
@ -88,6 +100,7 @@ private final class ImportStickerPackTitleInputFieldNode: ASDisplayNode, ASEdita
self.addSubnode(self.backgroundNode)
self.addSubnode(self.textInputNode)
self.addSubnode(self.placeholderNode)
self.addSubnode(self.prefixNode)
self.addSubnode(self.clearButton)
self.clearButton.addTarget(self, action: #selector(self.clearPressed), forControlEvents: .touchUpInside)
@ -466,3 +479,49 @@ func importStickerPackTitleController(sharedContext: SharedAccountContext, accou
}
return controller
}
func importStickerPackShortNameController(sharedContext: SharedAccountContext, account: Account, title: String, text: String, placeholder: String, doneButtonTitle: String? = nil, value: String?, maxLength: Int, apply: @escaping (String?) -> Void) -> AlertController {
let presentationData = sharedContext.currentPresentationData.with { $0 }
var dismissImpl: ((Bool) -> Void)?
var applyImpl: (() -> Void)?
let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
dismissImpl?(true)
}), TextAlertAction(type: .defaultAction, title: doneButtonTitle ?? presentationData.strings.Common_Done, action: {
applyImpl?()
})]
let contentNode = ImportStickerPackTitleAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, title: title, text: text, placeholder: placeholder, value: value, maxLength: maxLength)
contentNode.complete = {
applyImpl?()
}
applyImpl = { [weak contentNode] in
guard let contentNode = contentNode else {
return
}
dismissImpl?(true)
let previousValue = value ?? ""
let newValue = contentNode.value.trimmingCharacters(in: .whitespacesAndNewlines)
apply(previousValue != newValue || value == nil ? newValue : nil)
}
let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode)
let presentationDataDisposable = sharedContext.presentationData.start(next: { [weak controller, weak contentNode] presentationData in
controller?.theme = AlertControllerTheme(presentationData: presentationData)
contentNode?.inputFieldNode.updateTheme(presentationData.theme)
})
controller.dismissed = {
presentationDataDisposable.dispose()
}
dismissImpl = { [weak controller, weak contentNode] animated in
contentNode?.inputFieldNode.deactivateInput()
if animated {
controller?.dismissAnimated()
} else {
controller?.dismiss()
}
}
return controller
}

View File

@ -445,7 +445,7 @@ static CGRect viewFrame(UIView *view)
[UIView animateWithDuration:0.2 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState | animationCurveOption animations:^
{
CGAffineTransform transform = CGAffineTransformScale(transform, 0.25f, 0.25f);
CGAffineTransform transform = CGAffineTransformMakeScale(0.25, 0.25);
_cancelButton.transform = transform;
_cancelButton.alpha = 0.0f;
} completion:nil];

View File

@ -653,6 +653,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-427863538] = { return Api.InputStickerSet.parse_inputStickerSetDice($0) }
dict[-1231326505] = { return Api.messages.ChatAdminsWithInvites.parse_chatAdminsWithInvites($0) }
dict[-1729618630] = { return Api.BotInfo.parse_botInfo($0) }
dict[-2046910401] = { return Api.stickers.SuggestedShortName.parse_suggestedShortName($0) }
dict[-1519637954] = { return Api.updates.State.parse_state($0) }
dict[537022650] = { return Api.User.parse_userEmpty($0) }
dict[-1820043071] = { return Api.User.parse_user($0) }
@ -1405,6 +1406,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.BotInfo:
_1.serialize(buffer, boxed)
case let _1 as Api.stickers.SuggestedShortName:
_1.serialize(buffer, boxed)
case let _1 as Api.updates.State:
_1.serialize(buffer, boxed)
case let _1 as Api.User:

View File

@ -621,6 +621,44 @@ public struct upload {
}
}
public extension Api {
public struct stickers {
public enum SuggestedShortName: TypeConstructorDescription {
case suggestedShortName(shortName: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .suggestedShortName(let shortName):
if boxed {
buffer.appendInt32(-2046910401)
}
serializeString(shortName, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .suggestedShortName(let shortName):
return ("suggestedShortName", [("shortName", shortName)])
}
}
public static func parse_suggestedShortName(_ reader: BufferReader) -> SuggestedShortName? {
var _1: String?
_1 = parseString(reader)
let _c1 = _1 != nil
if _c1 {
return Api.stickers.SuggestedShortName.suggestedShortName(shortName: _1!)
}
else {
return nil
}
}
}
}
}
public extension Api {
public struct storage {
public enum FileType: TypeConstructorDescription {
case fileUnknown
@ -6373,6 +6411,20 @@ public extension Api {
return result
})
}
public static func suggestShortName(title: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.stickers.SuggestedShortName>) {
let buffer = Buffer()
buffer.appendInt32(1303364867)
serializeString(title, buffer: buffer, boxed: false)
return (FunctionDescription(name: "stickers.suggestShortName", parameters: [("title", title)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stickers.SuggestedShortName? in
let reader = BufferReader(buffer)
var result: Api.stickers.SuggestedShortName?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.stickers.SuggestedShortName
}
return result
})
}
}
public struct account {
public static func registerDevice(flags: Int32, tokenType: Int32, token: String, appSandbox: Api.Bool, secret: Buffer, otherUids: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {

View File

@ -78,7 +78,6 @@ public enum CreateStickerSetError {
case generic
}
public struct ImportSticker {
let resource: MediaResource
let emojis: [String]
@ -96,7 +95,7 @@ public enum CreateStickerSetStatus {
case complete(StickerPackCollectionInfo, [ItemCollectionItem])
}
public func createStickerSet(account: Account, title: String, shortName: String, stickers: [ImportSticker], thumbnail: ImportSticker?, isAnimated: Bool) -> Signal<CreateStickerSetStatus, CreateStickerSetError> {
func _internal_createStickerSet(account: Account, title: String, shortName: String, stickers: [ImportSticker], thumbnail: ImportSticker?, isAnimated: Bool) -> Signal<CreateStickerSetStatus, CreateStickerSetError> {
return account.postbox.loadedPeerWithId(account.peerId)
|> castError(CreateStickerSetError.self)
|> mapToSignal { peer -> Signal<CreateStickerSetStatus, CreateStickerSetError> in
@ -209,3 +208,35 @@ public func createStickerSet(account: Account, title: String, shortName: String,
}
}
}
func _internal_getStickerSetShortNameSuggestion(account: Account, title: String) -> Signal<String?, NoError> {
return account.network.request(Api.functions.stickers.suggestShortName(title: title))
|> map (Optional.init)
|> `catch` { _ in
return .single(nil)
}
|> map { result in
guard let result = result else {
return nil
}
switch result {
case let .suggestedShortName(shortName):
return shortName
}
}
}
func _internal_stickerSetShortNameAvailability(account: Account, shortName: String) -> Signal<AddressNameAvailability, NoError> {
return account.network.request(Api.functions.stickers.checkShortName(shortName: shortName))
|> map { result -> AddressNameAvailability in
switch result {
case .boolTrue:
return .available
case .boolFalse:
return .taken
}
}
|> `catch` { error -> Signal<AddressNameAvailability, NoError> in
return .single(.invalid)
}
}

View File

@ -69,5 +69,28 @@ public extension TelegramEngine {
public func stickerPacksAttachedToMedia(media: AnyMediaReference) -> Signal<[StickerPackReference], NoError> {
return _internal_stickerPacksAttachedToMedia(account: self.account, media: media)
}
public func createStickerSet(title: String, shortName: String, stickers: [ImportSticker], thumbnail: ImportSticker?, isAnimated: Bool) -> Signal<CreateStickerSetStatus, CreateStickerSetError> {
return _internal_createStickerSet(account: self.account, title: title, shortName: shortName, stickers: stickers, thumbnail: thumbnail, isAnimated: isAnimated)
}
public func getStickerSetShortNameSuggestion(title: String) -> Signal<String?, NoError> {
return _internal_getStickerSetShortNameSuggestion(account: self.account, title: title)
}
public func validateStickerSetShortNameInteractive(shortName: String) -> Signal<AddressNameValidationStatus, NoError> {
if let error = _internal_checkAddressNameFormat(shortName) {
return .single(.invalidFormat(error))
} else {
return .single(.checking)
|> then(
_internal_stickerSetShortNameAvailability(account: self.account, shortName: shortName)
|> delay(0.3, queue: Queue.concurrentDefaultQueue())
|> map { result -> AddressNameValidationStatus in
.availability(result)
}
)
}
}
}
}

View File

@ -4927,7 +4927,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if canSendMessagesToChat(strongSelf.presentationInterfaceState) {
let _ = strongSelf.presentVoiceMessageDiscardAlert(action: {
if let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(messageId) {
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageId(message.id) }).updatedSearch(nil) }, completion: completion)
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageId(message.id) }).updatedSearch(nil).updatedShowCommands(false) }, completion: completion)
strongSelf.updateItemNodesSearchTextHighlightStates()
strongSelf.chatDisplayNode.ensureInputViewFocused()
} else {
@ -4988,6 +4988,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
updated = updated.updatedInputMode({ _ in
return .text
})
updated = updated.updatedShowCommands(false)
return updated
}, completion: completion)
@ -4999,7 +5000,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}, beginMessageSelection: { [weak self] messageIds, completion in
if let strongSelf = self, strongSelf.isNodeLoaded {
let _ = strongSelf.presentVoiceMessageDiscardAlert(action: {
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withUpdatedSelectedMessages(messageIds) } }, completion: completion)
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withUpdatedSelectedMessages(messageIds) }.updatedShowCommands(false) }, completion: completion)
if let selectionState = strongSelf.presentationInterfaceState.interfaceState.selectionState {
let count = selectionState.selectedIds.count

View File

@ -1889,7 +1889,10 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
func frameForVisibleArea() -> CGRect {
let rect = CGRect(origin: CGPoint(x: self.visibleAreaInset.left, y: self.visibleAreaInset.top), size: CGSize(width: self.bounds.size.width - self.visibleAreaInset.left - self.visibleAreaInset.right, height: self.bounds.size.height - self.visibleAreaInset.top - self.visibleAreaInset.bottom))
var rect = CGRect(origin: CGPoint(x: self.visibleAreaInset.left, y: self.visibleAreaInset.top), size: CGSize(width: self.bounds.size.width - self.visibleAreaInset.left - self.visibleAreaInset.right, height: self.bounds.size.height - self.visibleAreaInset.top - self.visibleAreaInset.bottom))
if let inputContextPanelNode = self.inputContextPanelNode, let topItemFrame = inputContextPanelNode.topItemFrame {
rect.size.height = topItemFrame.minY
}
if let containerNode = self.containerNode {
return containerNode.view.convert(rect, to: self.view)
} else {

View File

@ -34,4 +34,8 @@ class ChatInputContextPanelNode: ASDisplayNode {
func animateOut(completion: @escaping () -> Void) {
completion()
}
var topItemFrame: CGRect? {
return nil
}
}

View File

@ -257,6 +257,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
private var searchActivityIndicator: ActivityIndicator?
var audioRecordingInfoContainerNode: ASDisplayNode?
var audioRecordingDotNode: AnimationNode?
var audioRecordingDotNodeDismissed = false
var audioRecordingTimeNode: ChatTextInputAudioRecordingTimeNode?
var audioRecordingCancelIndicator: ChatTextInputAudioRecordingCancelIndicator?
var animatingBinNode: AnimationNode?
@ -479,11 +480,11 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
if let strongSelf = self {
if let strongSelf = self {
if highlighted {
strongSelf.menuButton.layer.removeAnimation(forKey: "opacity")
strongSelf.menuButton.alpha = 0.4
let transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .spring)
transition.updateTransformScale(node: strongSelf.menuButton, scale: 0.85)
} else {
strongSelf.menuButton.alpha = 1.0
strongSelf.menuButton.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
let transition: ContainedViewLayoutTransition = .animated(duration: 0.5, curve: .spring)
transition.updateTransformScale(node: strongSelf.menuButton, scale: 1.0)
}
}
}
@ -1033,7 +1034,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
var hasMenuButton = false
var menuButtonExpanded = false
if let peer = interfaceState.renderedPeer?.peer as? TelegramUser, let _ = peer.botInfo, interfaceState.hasBotCommands {
if let peer = interfaceState.renderedPeer?.peer as? TelegramUser, let _ = peer.botInfo, interfaceState.hasBotCommands && interfaceState.editMessageState == nil {
hasMenuButton = true
var inputHasText = false
@ -1231,6 +1232,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
self.audioRecordingDotNode?.removeFromSupernode()
audioRecordingDotNode = AnimationNode(animation: "BinRed")
self.audioRecordingDotNode = audioRecordingDotNode
self.audioRecordingDotNodeDismissed = false
self.insertSubnode(audioRecordingDotNode, belowSubnode: self.menuButton)
self.animatingBinNode?.removeFromSupernode()
self.animatingBinNode = nil
@ -1269,7 +1271,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
audioRecordingCancelIndicator.layer.animateAlpha(from: CGFloat(audioRecordingCancelIndicator.layer.presentation()?.opacity ?? 1), to: 0, duration: 0.15, delay: 0, removeOnCompletion: false)
}
} else {
let update = self.actionButtons.micButton.audioRecorder != nil
var update = self.actionButtons.micButton.audioRecorder != nil || self.actionButtons.micButton.videoRecordingStatus != nil
self.actionButtons.micButton.audioRecorder = nil
self.actionButtons.micButton.videoRecordingStatus = nil
transition.updateAlpha(layer: self.textInputBackgroundNode.layer, alpha: 1.0)
@ -1302,7 +1304,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
self?.attachmentButton.layer.animateScale(from: 0.3, to: 1.0, duration: 0.15, delay: 0, removeOnCompletion: false)
}
if update {
if update && !self.audioRecordingDotNodeDismissed {
audioRecordingDotNode.layer.removeAllAnimations()
}
@ -1311,15 +1313,20 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
self.audioRecordingDotNode?.removeFromSupernode()
self.audioRecordingDotNode = nil
} else {
if !self.audioRecordingDotNodeDismissed {
audioRecordingDotNode.layer.removeAllAnimations()
}
audioRecordingDotNode.completion = dismissDotNode
audioRecordingDotNode.play()
update = true
}
} else {
dismissDotNode()
}
if update {
if update && !self.audioRecordingDotNodeDismissed {
self.audioRecordingDotNode?.layer.animatePosition(from: CGPoint(), to: CGPoint(x: leftMenuInset, y: 0.0), duration: 0.15, removeOnCompletion: false, additive: true)
self.audioRecordingDotNodeDismissed = true
}
}
@ -2174,6 +2181,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
}
@objc func menuButtonPressed() {
self.hapticFeedback.impact(.light)
self.interfaceInteraction?.updateShowCommands { value in
return !value
}

View File

@ -242,4 +242,14 @@ final class CommandChatInputContextPanelNode: ChatInputContextPanelNode {
let listViewFrame = self.listView.frame
return self.listView.hitTest(CGPoint(x: point.x - listViewFrame.minX, y: point.y - listViewFrame.minY), with: event)
}
override var topItemFrame: CGRect? {
var topItemFrame: CGRect?
self.listView.forEachItemNode { itemNode in
if topItemFrame == nil {
topItemFrame = itemNode.frame
}
}
return topItemFrame
}
}

View File

@ -260,4 +260,14 @@ final class CommandMenuChatInputContextPanelNode: ChatInputContextPanelNode {
let listViewFrame = self.listView.frame
return self.listView.hitTest(CGPoint(x: point.x - listViewFrame.minX, y: point.y - listViewFrame.minY), with: event)
}
override var topItemFrame: CGRect? {
var topItemFrame: CGRect?
self.listView.forEachItemNode { itemNode in
if topItemFrame == nil {
topItemFrame = itemNode.frame
}
}
return topItemFrame
}
}

View File

@ -259,5 +259,14 @@ final class HashtagChatInputContextPanelNode: ChatInputContextPanelNode {
let listViewFrame = self.listView.frame
return self.listView.hitTest(CGPoint(x: point.x - listViewFrame.minX, y: point.y - listViewFrame.minY), with: event)
}
override var topItemFrame: CGRect? {
var topItemFrame: CGRect?
self.listView.forEachItemNode { itemNode in
if topItemFrame == nil {
topItemFrame = itemNode.frame
}
}
return topItemFrame
}
}

View File

@ -287,4 +287,14 @@ final class MentionChatInputContextPanelNode: ChatInputContextPanelNode {
let listViewFrame = self.listView.frame
return self.listView.hitTest(CGPoint(x: point.x - listViewFrame.minX, y: point.y - listViewFrame.minY), with: event)
}
override var topItemFrame: CGRect? {
var topItemFrame: CGRect?
self.listView.forEachItemNode { itemNode in
if topItemFrame == nil {
topItemFrame = itemNode.frame
}
}
return topItemFrame
}
}

View File

@ -358,7 +358,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listNodeSize, insets: insets, duration: duration, curve: curve)
self.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets)
if let replacementHistoryNode = replacementHistoryNode {
if let replacementHistoryNode = self.replacementHistoryNode {
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listNodeSize, insets: insets, duration: 0.0, curve: .Default(duration: nil))
replacementHistoryNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets)
}

View File

@ -366,4 +366,14 @@ final class VerticalListContextResultsChatInputContextPanelNode: ChatInputContex
strongSelf.updateInternalResults(mergedResults)
}))
}
override var topItemFrame: CGRect? {
var topItemFrame: CGRect?
self.listView.forEachItemNode { itemNode in
if topItemFrame == nil {
topItemFrame = itemNode.frame
}
}
return topItemFrame
}
}