UI improvements

This commit is contained in:
Ali 2023-01-24 16:39:27 +01:00
parent e943444b48
commit a7c9ee2de7
4 changed files with 310 additions and 9 deletions

View File

@ -233,6 +233,31 @@ public final class EmojiSuggestionsComponent: Component {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
public func item(at point: CGPoint) -> (CALayer, TelegramMediaFile)? {
let location = self.convert(point, to: self.scrollView)
if self.scrollView.bounds.contains(location) {
var closestFile: (file: TelegramMediaFile, layer: CALayer, distance: CGFloat)?
for (_, itemLayer) in self.visibleLayers {
guard let file = itemLayer.file else {
continue
}
let distance = abs(location.x - itemLayer.position.x)
if let (_, _, currentDistance) = closestFile {
if distance < currentDistance {
closestFile = (file, itemLayer, distance)
}
} else {
closestFile = (file, itemLayer, distance)
}
}
if let (file, itemLayer, _) = closestFile {
return (itemLayer, file)
}
}
return nil
}
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state { if case .ended = recognizer.state {
let location = recognizer.location(in: self.scrollView) let location = recognizer.location(in: self.scrollView)

View File

@ -196,7 +196,7 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState
} }
} }
if isMember && !channel.hasPermission(.sendSomething) && !channel.flags.contains(.isGigagroup) { if case .group = channel.info, isMember && !channel.hasPermission(.sendSomething) && !channel.flags.contains(.isGigagroup) {
if let currentPanel = (currentPanel as? ChatRestrictedInputPanelNode) ?? (currentSecondaryPanel as? ChatRestrictedInputPanelNode) { if let currentPanel = (currentPanel as? ChatRestrictedInputPanelNode) ?? (currentSecondaryPanel as? ChatRestrictedInputPanelNode) {
return (currentPanel, nil) return (currentPanel, nil)
} else { } else {

View File

@ -30,17 +30,17 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode {
self.presentationInterfaceState = interfaceState self.presentationInterfaceState = interfaceState
} }
let bannedPermission: (Int32, Bool)? var bannedPermission: (Int32, Bool)?
if let channel = interfaceState.renderedPeer?.peer as? TelegramChannel { if let channel = interfaceState.renderedPeer?.peer as? TelegramChannel {
bannedPermission = channel.hasBannedPermission(.banSendText) if let value = channel.hasBannedPermission(.banSendText) {
} else if let group = interfaceState.renderedPeer?.peer as? TelegramGroup { bannedPermission = value
if group.hasBannedPermission(.banSendText) { } else if !channel.hasPermission(.sendSomething) {
bannedPermission = (Int32.max, false)
}
} else if let group = interfaceState.renderedPeer?.peer as? TelegramGroup {
if !group.hasPermission(.sendSomething) {
bannedPermission = (Int32.max, false) bannedPermission = (Int32.max, false)
} else {
bannedPermission = nil
} }
} else {
bannedPermission = nil
} }
var iconImage: UIImage? var iconImage: UIImage?

View File

@ -30,6 +30,9 @@ import ComponentFlow
import EmojiSuggestionsComponent import EmojiSuggestionsComponent
import AudioToolbox import AudioToolbox
import ChatControllerInteraction import ChatControllerInteraction
import UndoUI
import PremiumUI
import StickerPeekUI
private let accessoryButtonFont = Font.medium(14.0) private let accessoryButtonFont = Font.medium(14.0)
private let counterFont = Font.with(size: 14.0, design: .regular, traits: [.monospacedNumbers]) private let counterFont = Font.with(size: 14.0, design: .regular, traits: [.monospacedNumbers])
@ -2571,6 +2574,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
viewForOverlayContent.addSubview(currentEmojiSuggestionView) viewForOverlayContent.addSubview(currentEmojiSuggestionView)
currentEmojiSuggestionView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) currentEmojiSuggestionView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
self.installEmojiSuggestionPreviewGesture(hostView: currentEmojiSuggestionView)
} }
let globalPosition = textInputNode.textView.convert(currentEmojiSuggestion.localPosition, to: self.view) let globalPosition = textInputNode.textView.convert(currentEmojiSuggestion.localPosition, to: self.view)
@ -2693,6 +2698,277 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
} }
} }
private func installEmojiSuggestionPreviewGesture(hostView: UIView) {
let peekRecognizer = PeekControllerGestureRecognizer(contentAtPoint: { [weak self] point in
guard let self else {
return nil
}
return self.emojiSuggestionPeekContentAtPoint(point: point)
}, present: { [weak self] content, sourceView, sourceRect in
guard let strongSelf = self, let context = strongSelf.context else {
return nil
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let controller = PeekController(presentationData: presentationData, content: content, sourceView: {
return (sourceView, sourceRect)
})
//strongSelf.peekController = controller
strongSelf.interfaceInteraction?.presentController(controller, nil)
return controller
}, updateContent: { [weak self] content in
guard let strongSelf = self else {
return
}
let _ = strongSelf
})
hostView.addGestureRecognizer(peekRecognizer)
}
private func emojiSuggestionPeekContentAtPoint(point: CGPoint) -> Signal<(UIView, CGRect, PeekControllerContent)?, NoError>? {
guard let presentationInterfaceState = self.presentationInterfaceState else {
return nil
}
guard let chatPeerId = presentationInterfaceState.renderedPeer?.peer?.id else {
return nil
}
guard let context = self.context else {
return nil
}
var maybeFile: TelegramMediaFile?
var maybeItemLayer: CALayer?
if let currentEmojiSuggestionView = self.currentEmojiSuggestionView?.componentView as? EmojiSuggestionsComponent.View {
if let (itemLayer, file) = currentEmojiSuggestionView.item(at: point) {
maybeFile = file
maybeItemLayer = itemLayer
}
}
guard let file = maybeFile else {
return nil
}
guard let itemLayer = maybeItemLayer else {
return nil
}
let _ = chatPeerId
let _ = file
let _ = itemLayer
var collectionId: ItemCollectionId?
for attribute in file.attributes {
if case let .CustomEmoji(_, _, _, packReference) = attribute {
switch packReference {
case let .id(id, _):
collectionId = ItemCollectionId(namespace: Namespaces.ItemCollection.CloudEmojiPacks, id: id)
default:
break
}
}
}
var bubbleUpEmojiOrStickersets: [ItemCollectionId] = []
if let collectionId {
bubbleUpEmojiOrStickersets.append(collectionId)
}
let accountPeerId = context.account.peerId
let _ = bubbleUpEmojiOrStickersets
let _ = context
let _ = accountPeerId
return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: accountPeerId))
|> map { peer -> Bool in
var hasPremium = false
if case let .user(user) = peer, user.isPremium {
hasPremium = true
}
return hasPremium
}
|> deliverOnMainQueue
|> map { [weak self, weak itemLayer] hasPremium -> (UIView, CGRect, PeekControllerContent)? in
guard let strongSelf = self, let itemLayer = itemLayer else {
return nil
}
let _ = strongSelf
let _ = itemLayer
var menuItems: [ContextMenuItem] = []
menuItems.removeAll()
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let _ = presentationData
var isLocked = false
if !hasPremium {
isLocked = file.isPremiumEmoji
if isLocked && chatPeerId == context.account.peerId {
isLocked = false
}
}
if let interaction = strongSelf.interfaceInteraction {
let _ = interaction
let sendEmoji: (TelegramMediaFile) -> Void = { file in
guard let self else {
return
}
guard let controller = (self.interfaceInteraction?.chatController() as? ChatControllerImpl) else {
return
}
var text = "."
var emojiAttribute: ChatTextInputTextCustomEmojiAttribute?
loop: for attribute in file.attributes {
switch attribute {
case let .CustomEmoji(_, _, displayText, stickerPackReference):
text = displayText
var packId: ItemCollectionId?
if case let .id(id, _) = stickerPackReference {
packId = ItemCollectionId(namespace: Namespaces.ItemCollection.CloudEmojiPacks, id: id)
}
emojiAttribute = ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: packId, fileId: file.fileId.id, file: file)
break loop
default:
break
}
}
if let emojiAttribute {
controller.controllerInteraction?.sendEmoji(text, emojiAttribute, true)
}
}
let setStatus: (TelegramMediaFile) -> Void = { file in
guard let self, let context = self.context else {
return
}
guard let controller = (self.interfaceInteraction?.chatController() as? ChatControllerImpl) else {
return
}
let _ = context.engine.accountData.setEmojiStatus(file: file, expirationDate: nil).start()
var animateInAsReplacement = false
animateInAsReplacement = false
/*if let currentUndoOverlayController = strongSelf.currentUndoOverlayController {
currentUndoOverlayController.dismissWithCommitActionAndReplacementAnimation()
strongSelf.currentUndoOverlayController = nil
animateInAsReplacement = true
}*/
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
//TODO:localize
let undoController = UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, title: nil, text: "Your emoji status has been updated.", undoText: nil, customAction: nil), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in return false })
//strongSelf.currentUndoOverlayController = controller
controller.controllerInteraction?.presentController(undoController, nil)
}
let copyEmoji: (TelegramMediaFile) -> Void = { file in
var text = "."
var emojiAttribute: ChatTextInputTextCustomEmojiAttribute?
loop: for attribute in file.attributes {
switch attribute {
case let .CustomEmoji(_, _, displayText, _):
text = displayText
emojiAttribute = ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: file.fileId.id, file: file)
break loop
default:
break
}
}
if let _ = emojiAttribute {
storeMessageTextInPasteboard(text, entities: [MessageTextEntity(range: 0 ..< (text as NSString).length, type: .CustomEmoji(stickerPack: nil, fileId: file.fileId.id))])
}
}
//TODO:localize
menuItems.append(.action(ContextMenuActionItem(text: "Send Emoji", icon: { theme in
if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Download"), color: theme.actionSheet.primaryTextColor) {
return generateImage(image.size, rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
if let cgImage = image.cgImage {
context.draw(cgImage, in: CGRect(origin: CGPoint(), size: size))
}
})
} else {
return nil
}
}, action: { _, f in
sendEmoji(file)
f(.default)
})))
//TODO:localize
menuItems.append(.action(ContextMenuActionItem(text: "Set as Status", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Smile"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in
f(.default)
guard let strongSelf = self else {
return
}
if hasPremium {
setStatus(file)
} else {
var replaceImpl: ((ViewController) -> Void)?
let controller = PremiumDemoScreen(context: context, subject: .animatedEmoji, action: {
let controller = PremiumIntroScreen(context: context, source: .animatedEmoji)
replaceImpl?(controller)
})
replaceImpl = { [weak controller] c in
controller?.replace(with: c)
}
strongSelf.interfaceInteraction?.getNavigationController()?.pushViewController(controller)
}
})))
//TODO:localize
menuItems.append(.action(ContextMenuActionItem(text: "Copy Emoji", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in
copyEmoji(file)
f(.default)
})))
}
if menuItems.isEmpty {
return nil
}
let content = StickerPreviewPeekContent(context: context, theme: presentationData.theme, strings: presentationData.strings, item: .pack(file), isLocked: isLocked, menu: menuItems, openPremiumIntro: { [weak self] in
guard let self else {
return
}
guard let interfaceInteraction = self.interfaceInteraction else {
return
}
let _ = self
let _ = interfaceInteraction
let controller = PremiumIntroScreen(context: context, source: .stickers)
//let _ = controller
interfaceInteraction.getNavigationController()?.pushViewController(controller)
})
let _ = content
//return nil
return (strongSelf.view, itemLayer.convert(itemLayer.bounds, to: strongSelf.view.layer), content)
}
}
private func updateTextNodeText(animated: Bool) { private func updateTextNodeText(animated: Bool) {
var inputHasText = false var inputHasText = false
var hideMicButton = false var hideMicButton = false