mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
857122fe72
@ -7157,14 +7157,14 @@ Sorry for the inconvenience.";
|
|||||||
"ForcedPasswordSetup.Intro.DismissActionOK" = "Yes, I’m sure";
|
"ForcedPasswordSetup.Intro.DismissActionOK" = "Yes, I’m sure";
|
||||||
|
|
||||||
"Login.ShortCallTitle" = "Within a few seconds you should\nreceive a short call from:";
|
"Login.ShortCallTitle" = "Within a few seconds you should\nreceive a short call from:";
|
||||||
"Login.CodePhonePatternInfoText" = "Please enter the last digits\nof the missed call number.";
|
"Login.CodePhonePatternInfoText" = "Please enter the last digits\nof the number that called.";
|
||||||
"Login.EnterMissingDigits" = "Enter the missing digits";
|
"Login.EnterMissingDigits" = "Enter the missing digits";
|
||||||
|
|
||||||
"Channel.AdminLogFilter.EventsSentMessages" = "Sent Messages";
|
"Channel.AdminLogFilter.EventsSentMessages" = "Sent Messages";
|
||||||
|
|
||||||
"Contacts.AddContact" = "Add Contact";
|
"Contacts.AddContact" = "Add Contact";
|
||||||
|
|
||||||
"Conversation.LargeEmojiDisabledInfo" = "You have disabled large emoji, so they appear small and have no effects in chat.";
|
"Conversation.LargeEmojiDisabledInfo" = "You have disabled large emoji, so they appear small and have no effects in the chat.";
|
||||||
"Conversation.LargeEmojiEnable" = "Enable Large Emoji";
|
"Conversation.LargeEmojiEnable" = "Enable Large Emoji";
|
||||||
"Conversation.LargeEmojiEnabled" = "Large emoji enabled.";
|
"Conversation.LargeEmojiEnabled" = "Large emoji enabled.";
|
||||||
|
|
||||||
@ -7176,16 +7176,16 @@ Sorry for the inconvenience.";
|
|||||||
|
|
||||||
"Conversation.ContextMenuTranslate" = "Translate";
|
"Conversation.ContextMenuTranslate" = "Translate";
|
||||||
|
|
||||||
"ClearCache.ClearDescription" = "All media will stay in the Telegram cloud and can be re-downloaded if you need it again.";
|
"ClearCache.ClearDescription" = "All media will stay in the Telegram cloud and can be re-downloaded if you need them again.";
|
||||||
|
|
||||||
"ChatSettings.StickersAndReactions" = "Stickers and Emoji";
|
"ChatSettings.StickersAndReactions" = "Stickers and Emoji";
|
||||||
|
|
||||||
"Localization.TranslateMessages" = "Translate Messages";
|
"Localization.TranslateMessages" = "Translate Messages";
|
||||||
"Localization.ShowTranslate" = "Show Translate Button";
|
"Localization.ShowTranslate" = "Show Translate Button";
|
||||||
"Localization.ShowTranslateInfo" = "Show 'Translate' button in the message action menu.";
|
"Localization.ShowTranslateInfo" = "Show a 'Translate' button in the message action menu.";
|
||||||
"Localization.DoNotTranslate" = "Do Not Translate";
|
"Localization.DoNotTranslate" = "Do Not Translate";
|
||||||
"Localization.DoNotTranslateInfo" = "Do not show 'Translate' button in the message action menu for this language.";
|
"Localization.DoNotTranslateInfo" = "Do not show the 'Translate' button in the message action menu for this language.";
|
||||||
"Localization.DoNotTranslateManyInfo" = "Do not show 'Translate' button in the message action menu for this languages.";
|
"Localization.DoNotTranslateManyInfo" = "Do not show the 'Translate' button in the message action menu for these languages.";
|
||||||
"Localization.InterfaceLanguage" = "Interface Language";
|
"Localization.InterfaceLanguage" = "Interface Language";
|
||||||
|
|
||||||
"DoNotTranslate.Title" = "Do Not Translate";
|
"DoNotTranslate.Title" = "Do Not Translate";
|
||||||
@ -7197,8 +7197,8 @@ Sorry for the inconvenience.";
|
|||||||
"Contacts.QrCode.MyCode" = "My QR Code";
|
"Contacts.QrCode.MyCode" = "My QR Code";
|
||||||
"Contacts.QrCode.NoCodeFound" = "No valid QR code found in the image. Please try again.";
|
"Contacts.QrCode.NoCodeFound" = "No valid QR code found in the image. Please try again.";
|
||||||
|
|
||||||
"AccessDenied.QrCode" = "Telegram needs access to your photo library to scan QR codes.\n\nPlease go to Settings > Privacy > Photos and set Telegram to ON.";
|
"AccessDenied.QrCode" = "Telegram needs access to your photo library to scan QR codes.\n\nOpen your device's Settings > Privacy > Photos and set Telegram to ON.";
|
||||||
"AccessDenied.QrCamera" = "Telegram needs access to your camera to scan QR codes.\n\nPlease go to Settings > Privacy > Camera and set Telegram to ON.";
|
"AccessDenied.QrCamera" = "Telegram needs access to your camera to scan QR codes.\n\nOpen your device's Settings > Privacy > Camera and set Telegram to ON.";
|
||||||
|
|
||||||
"Share.ShareToInstagramStories" = "Share to Instagram Stories";
|
"Share.ShareToInstagramStories" = "Share to Instagram Stories";
|
||||||
|
|
||||||
@ -7213,7 +7213,7 @@ Sorry for the inconvenience.";
|
|||||||
|
|
||||||
"Settings.QuickReactionSetup.NavigationTitle" = "Quick Reaction";
|
"Settings.QuickReactionSetup.NavigationTitle" = "Quick Reaction";
|
||||||
"Settings.QuickReactionSetup.Title" = "Quick Reaction";
|
"Settings.QuickReactionSetup.Title" = "Quick Reaction";
|
||||||
"Settings.QuickReactionSetup.DemoHeader" = "DOUBLE TAP ON MESSAGE TO REACT";
|
"Settings.QuickReactionSetup.DemoHeader" = "DOUBLE TAP ON A MESSAGE TO REACT";
|
||||||
"Settings.QuickReactionSetup.DemoInfo" = "You can double tap on message for a quick reaction.";
|
"Settings.QuickReactionSetup.DemoInfo" = "You can double tap on message for a quick reaction.";
|
||||||
"Settings.QuickReactionSetup.ReactionListHeader" = "QUICK REACTION";
|
"Settings.QuickReactionSetup.ReactionListHeader" = "QUICK REACTION";
|
||||||
"Settings.QuickReactionSetup.DemoMessageAuthor" = "Dino";
|
"Settings.QuickReactionSetup.DemoMessageAuthor" = "Dino";
|
||||||
|
@ -195,10 +195,10 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
private let selectionHighlightNode: ASDisplayNode
|
private let selectionHighlightNode: ASDisplayNode
|
||||||
private let itemNodes: [ItemNode]
|
private let itemNodes: [ItemNode]
|
||||||
|
|
||||||
private struct ScrollToTabReaction {
|
struct ScrollToTabReaction {
|
||||||
var value: String?
|
var value: String?
|
||||||
}
|
}
|
||||||
private var scrollToTabReaction: ScrollToTabReaction?
|
var scrollToTabReaction: ScrollToTabReaction?
|
||||||
|
|
||||||
var action: ((String?) -> Void)?
|
var action: ((String?) -> Void)?
|
||||||
|
|
||||||
@ -211,6 +211,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
if #available(iOS 11.0, *) {
|
if #available(iOS 11.0, *) {
|
||||||
self.scrollNode.view.contentInsetAdjustmentBehavior = .never
|
self.scrollNode.view.contentInsetAdjustmentBehavior = .never
|
||||||
}
|
}
|
||||||
|
self.scrollNode.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||||
|
|
||||||
self.itemNodes = reactions.map { reaction, count in
|
self.itemNodes = reactions.map { reaction, count in
|
||||||
return ItemNode(context: context, availableReactions: availableReactions, reaction: reaction, count: count)
|
return ItemNode(context: context, availableReactions: availableReactions, reaction: reaction, count: count)
|
||||||
@ -275,7 +276,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
self.scrollToTabReaction = nil
|
self.scrollToTabReaction = nil
|
||||||
for itemNode in self.itemNodes {
|
for itemNode in self.itemNodes {
|
||||||
if itemNode.reaction == scrollToTabReaction.value {
|
if itemNode.reaction == scrollToTabReaction.value {
|
||||||
self.scrollNode.view.scrollRectToVisible(itemNode.frame.insetBy(dx: -sideInset, dy: 0.0), animated: transition.isAnimated)
|
self.scrollNode.view.scrollRectToVisible(itemNode.frame.insetBy(dx: -sideInset - 8.0, dy: 0.0), animated: transition.isAnimated)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -458,6 +459,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
|
|
||||||
private let scrollNode: ASScrollNode
|
private let scrollNode: ASScrollNode
|
||||||
private var ignoreScrolling: Bool = false
|
private var ignoreScrolling: Bool = false
|
||||||
|
private var animateIn: Bool = false
|
||||||
|
|
||||||
private var presentationData: PresentationData?
|
private var presentationData: PresentationData?
|
||||||
private var currentSize: CGSize?
|
private var currentSize: CGSize?
|
||||||
@ -519,6 +521,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
animateIn = true
|
animateIn = true
|
||||||
}
|
}
|
||||||
strongSelf.state = updatedState
|
strongSelf.state = updatedState
|
||||||
|
strongSelf.animateIn = true
|
||||||
strongSelf.requestUpdate(strongSelf, animateIn ? .animated(duration: 0.2, curve: .easeInOut) : .immediate)
|
strongSelf.requestUpdate(strongSelf, animateIn ? .animated(duration: 0.2, curve: .easeInOut) : .immediate)
|
||||||
if animateIn {
|
if animateIn {
|
||||||
for (_, itemNode) in strongSelf.itemNodes {
|
for (_, itemNode) in strongSelf.itemNodes {
|
||||||
@ -623,7 +626,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
for (id, placeholderLayer) in self.placeholderLayers {
|
for (id, placeholderLayer) in self.placeholderLayers {
|
||||||
if !validPlaceholderIds.contains(id) {
|
if !validPlaceholderIds.contains(id) {
|
||||||
removePlaceholderIds.append(id)
|
removePlaceholderIds.append(id)
|
||||||
if animated {
|
if animated || self.animateIn {
|
||||||
placeholderLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak placeholderLayer] _ in
|
placeholderLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak placeholderLayer] _ in
|
||||||
placeholderLayer?.removeFromSuperlayer()
|
placeholderLayer?.removeFromSuperlayer()
|
||||||
})
|
})
|
||||||
@ -641,7 +644,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(presentationData: PresentationData, constrainedSize: CGSize, transition: ContainedViewLayoutTransition) -> (size: CGSize, apparentHeight: CGFloat) {
|
func update(presentationData: PresentationData, constrainedSize: CGSize, transition: ContainedViewLayoutTransition) -> (height: CGFloat, apparentHeight: CGFloat) {
|
||||||
let itemHeight: CGFloat = 44.0
|
let itemHeight: CGFloat = 44.0
|
||||||
|
|
||||||
if self.presentationData?.theme !== presentationData.theme {
|
if self.presentationData?.theme !== presentationData.theme {
|
||||||
@ -697,18 +700,22 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
|
|
||||||
self.updateVisibleItems(animated: transition.isAnimated, syncronousLoad: !transition.isAnimated)
|
self.updateVisibleItems(animated: transition.isAnimated, syncronousLoad: !transition.isAnimated)
|
||||||
|
|
||||||
|
self.animateIn = false
|
||||||
|
|
||||||
var apparentHeight = -self.scrollNode.view.contentOffset.y + self.scrollNode.view.contentSize.height
|
var apparentHeight = -self.scrollNode.view.contentOffset.y + self.scrollNode.view.contentSize.height
|
||||||
apparentHeight = max(apparentHeight, 44.0)
|
apparentHeight = max(apparentHeight, 44.0)
|
||||||
apparentHeight = min(apparentHeight, containerSize.height + 100.0)
|
apparentHeight = min(apparentHeight, containerSize.height + 100.0)
|
||||||
self.apparentHeight = apparentHeight
|
self.apparentHeight = apparentHeight
|
||||||
|
|
||||||
return (containerSize, apparentHeight)
|
return (containerSize.height, apparentHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class ItemsNode: ASDisplayNode, ContextControllerItemsNode {
|
final class ItemsNode: ASDisplayNode, ContextControllerItemsNode, UIGestureRecognizerDelegate {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let availableReactions: AvailableReactions?
|
private let availableReactions: AvailableReactions?
|
||||||
|
private let message: EngineMessage
|
||||||
|
private let readStats: MessageReadStats?
|
||||||
private let reactions: [(String?, Int)]
|
private let reactions: [(String?, Int)]
|
||||||
private let requestUpdate: (ContainedViewLayoutTransition) -> Void
|
private let requestUpdate: (ContainedViewLayoutTransition) -> Void
|
||||||
private let requestUpdateApparentHeight: (ContainedViewLayoutTransition) -> Void
|
private let requestUpdateApparentHeight: (ContainedViewLayoutTransition) -> Void
|
||||||
@ -718,9 +725,15 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
private var backButtonNode: BackButtonNode?
|
private var backButtonNode: BackButtonNode?
|
||||||
private var separatorNode: ASDisplayNode?
|
private var separatorNode: ASDisplayNode?
|
||||||
private var tabListNode: ReactionTabListNode?
|
private var tabListNode: ReactionTabListNode?
|
||||||
private var currentTabNode: ReactionsTabNode
|
|
||||||
|
|
||||||
private var dismissedTabNode: ReactionsTabNode?
|
private var currentTabIndex: Int = 0
|
||||||
|
private var visibleTabNodes: [Int: ReactionsTabNode] = [:]
|
||||||
|
|
||||||
|
private struct InteractiveTransitionState {
|
||||||
|
var toIndex: Int
|
||||||
|
var progress: CGFloat
|
||||||
|
}
|
||||||
|
private var interactiveTransitionState: InteractiveTransitionState?
|
||||||
|
|
||||||
private let openPeer: (PeerId) -> Void
|
private let openPeer: (PeerId) -> Void
|
||||||
|
|
||||||
@ -739,14 +752,16 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.availableReactions = availableReactions
|
self.availableReactions = availableReactions
|
||||||
|
self.message = message
|
||||||
|
self.readStats = readStats
|
||||||
self.openPeer = openPeer
|
self.openPeer = openPeer
|
||||||
self.presentationData = context.sharedContext.currentPresentationData.with({ $0 })
|
self.presentationData = context.sharedContext.currentPresentationData.with({ $0 })
|
||||||
|
|
||||||
self.requestUpdate = requestUpdate
|
self.requestUpdate = requestUpdate
|
||||||
self.requestUpdateApparentHeight = requestUpdateApparentHeight
|
self.requestUpdateApparentHeight = requestUpdateApparentHeight
|
||||||
|
|
||||||
var requestUpdateTab: ((ReactionsTabNode, ContainedViewLayoutTransition) -> Void)?
|
//var requestUpdateTab: ((ReactionsTabNode, ContainedViewLayoutTransition) -> Void)?
|
||||||
var requestUpdateTabApparentHeight: ((ReactionsTabNode, ContainedViewLayoutTransition) -> Void)?
|
//var requestUpdateTabApparentHeight: ((ReactionsTabNode, ContainedViewLayoutTransition) -> Void)?
|
||||||
|
|
||||||
if let back = back {
|
if let back = back {
|
||||||
self.backButtonNode = BackButtonNode()
|
self.backButtonNode = BackButtonNode()
|
||||||
@ -775,23 +790,6 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
|
|
||||||
self.reactions = reactions
|
self.reactions = reactions
|
||||||
|
|
||||||
self.currentTabNode = ReactionsTabNode(
|
|
||||||
context: context,
|
|
||||||
availableReactions: availableReactions,
|
|
||||||
message: message,
|
|
||||||
reaction: reaction,
|
|
||||||
readStats: readStats,
|
|
||||||
requestUpdate: { tab, transition in
|
|
||||||
requestUpdateTab?(tab, transition)
|
|
||||||
},
|
|
||||||
requestUpdateApparentHeight: { tab, transition in
|
|
||||||
requestUpdateTabApparentHeight?(tab, transition)
|
|
||||||
},
|
|
||||||
openPeer: { id in
|
|
||||||
openPeer(id)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
if self.backButtonNode != nil || self.tabListNode != nil {
|
if self.backButtonNode != nil || self.tabListNode != nil {
|
||||||
@ -807,15 +805,21 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
if let separatorNode = self.separatorNode {
|
if let separatorNode = self.separatorNode {
|
||||||
self.addSubnode(separatorNode)
|
self.addSubnode(separatorNode)
|
||||||
}
|
}
|
||||||
self.addSubnode(self.currentTabNode)
|
|
||||||
|
|
||||||
self.tabListNode?.action = { [weak self] reaction in
|
self.tabListNode?.action = { [weak self] reaction in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if strongSelf.currentTabNode.reaction != reaction {
|
guard let tabIndex = strongSelf.reactions.firstIndex(where: { $0.0 == reaction }) else {
|
||||||
strongSelf.dismissedTabNode = strongSelf.currentTabNode
|
return
|
||||||
let currentTabNode = ReactionsTabNode(
|
}
|
||||||
|
guard strongSelf.currentTabIndex != tabIndex else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.tabListNode?.scrollToTabReaction = ReactionTabListNode.ScrollToTabReaction(value: reaction)
|
||||||
|
strongSelf.currentTabIndex = tabIndex
|
||||||
|
|
||||||
|
/*let currentTabNode = ReactionsTabNode(
|
||||||
context: context,
|
context: context,
|
||||||
availableReactions: availableReactions,
|
availableReactions: availableReactions,
|
||||||
message: message,
|
message: message,
|
||||||
@ -832,16 +836,15 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
strongSelf.currentTabNode = currentTabNode
|
strongSelf.currentTabNode = currentTabNode
|
||||||
strongSelf.addSubnode(currentTabNode)
|
strongSelf.addSubnode(currentTabNode)*/
|
||||||
strongSelf.requestUpdate(.animated(duration: 0.45, curve: .spring))
|
strongSelf.requestUpdate(.animated(duration: 0.45, curve: .spring))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
requestUpdateTab = { [weak self] tab, transition in
|
/*requestUpdateTab = { [weak self] tab, transition in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if strongSelf.currentTabNode == tab {
|
if strongSelf.visibleTabNodes.contains(where: { $0.value === tab }) {
|
||||||
strongSelf.requestUpdate(transition)
|
strongSelf.requestUpdate(transition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -850,9 +853,67 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if strongSelf.currentTabNode == tab {
|
if strongSelf.visibleTabNodes.contains(where: { $0.value === tab }) {
|
||||||
strongSelf.requestUpdateApparentHeight(transition)
|
strongSelf.requestUpdateApparentHeight(transition)
|
||||||
}
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), allowedDirections: { [weak self] point in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
if strongSelf.currentTabIndex == 0 {
|
||||||
|
return .left
|
||||||
|
}
|
||||||
|
return [.left, .right]
|
||||||
|
})
|
||||||
|
panRecognizer.delegate = self
|
||||||
|
self.view.addGestureRecognizer(panRecognizer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
|
if let _ = otherGestureRecognizer as? InteractiveTransitionGestureRecognizer {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if let _ = otherGestureRecognizer as? UIPanGestureRecognizer {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||||
|
switch recognizer.state {
|
||||||
|
case .began:
|
||||||
|
break
|
||||||
|
case .changed:
|
||||||
|
let translation = recognizer.translation(in: self.view)
|
||||||
|
if !self.bounds.isEmpty {
|
||||||
|
let progress = translation.x / self.bounds.width
|
||||||
|
var toIndex: Int
|
||||||
|
if progress < 0.0 {
|
||||||
|
toIndex = self.currentTabIndex + 1
|
||||||
|
} else {
|
||||||
|
toIndex = self.currentTabIndex - 1
|
||||||
|
}
|
||||||
|
toIndex = max(0, min(toIndex, self.reactions.count - 1))
|
||||||
|
self.interactiveTransitionState = InteractiveTransitionState(toIndex: toIndex, progress: abs(progress))
|
||||||
|
self.requestUpdate(.immediate)
|
||||||
|
}
|
||||||
|
case .cancelled, .ended:
|
||||||
|
if let interactiveTransitionState = self.interactiveTransitionState {
|
||||||
|
self.interactiveTransitionState = nil
|
||||||
|
if interactiveTransitionState.progress >= 0.2 {
|
||||||
|
self.currentTabIndex = interactiveTransitionState.toIndex
|
||||||
|
self.tabListNode?.scrollToTabReaction = ReactionTabListNode.ScrollToTabReaction(value: self.reactions[self.currentTabIndex].0)
|
||||||
|
}
|
||||||
|
self.requestUpdate(.animated(duration: 0.45, curve: .spring))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -868,7 +929,8 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
}
|
}
|
||||||
if let tabListNode = self.tabListNode {
|
if let tabListNode = self.tabListNode {
|
||||||
let tabListFrame = CGRect(origin: CGPoint(x: 0.0, y: topContentHeight), size: CGSize(width: constrainedSize.width, height: 44.0))
|
let tabListFrame = CGRect(origin: CGPoint(x: 0.0, y: topContentHeight), size: CGSize(width: constrainedSize.width, height: 44.0))
|
||||||
tabListNode.update(size: tabListFrame.size, presentationData: self.presentationData, selectedReaction: self.currentTabNode.reaction, transition: transition)
|
let selectedReaction: String? = self.reactions[self.currentTabIndex].0
|
||||||
|
tabListNode.update(size: tabListFrame.size, presentationData: self.presentationData, selectedReaction: selectedReaction, transition: transition)
|
||||||
transition.updateFrame(node: tabListNode, frame: tabListFrame)
|
transition.updateFrame(node: tabListNode, frame: tabListFrame)
|
||||||
topContentHeight += tabListFrame.height
|
topContentHeight += tabListFrame.height
|
||||||
}
|
}
|
||||||
@ -879,7 +941,108 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
topContentHeight += separatorFrame.height
|
topContentHeight += separatorFrame.height
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentTabTransition = transition
|
var tabLayouts: [Int: (height: CGFloat, apparentHeight: CGFloat)] = [:]
|
||||||
|
|
||||||
|
var visibleIndices: [Int] = []
|
||||||
|
visibleIndices.append(self.currentTabIndex)
|
||||||
|
if let interactiveTransitionState = self.interactiveTransitionState {
|
||||||
|
visibleIndices.append(interactiveTransitionState.toIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
let previousVisibleTabFrames: [(Int, CGRect)] = self.visibleTabNodes.map { key, value -> (Int, CGRect) in
|
||||||
|
return (key, value.frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
for index in visibleIndices {
|
||||||
|
var tabTransition = transition
|
||||||
|
let tabNode: ReactionsTabNode
|
||||||
|
var initialReferenceFrame: CGRect?
|
||||||
|
if let current = self.visibleTabNodes[index] {
|
||||||
|
tabNode = current
|
||||||
|
} else {
|
||||||
|
for (previousIndex, previousFrame) in previousVisibleTabFrames {
|
||||||
|
if index > previousIndex {
|
||||||
|
initialReferenceFrame = previousFrame.offsetBy(dx: constrainedSize.width, dy: 0.0)
|
||||||
|
} else {
|
||||||
|
initialReferenceFrame = previousFrame.offsetBy(dx: -constrainedSize.width, dy: 0.0)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
tabNode = ReactionsTabNode(
|
||||||
|
context: self.context,
|
||||||
|
availableReactions: self.availableReactions,
|
||||||
|
message: self.message,
|
||||||
|
reaction: self.reactions[index].0,
|
||||||
|
readStats: self.reactions[index].0 == nil ? self.readStats : nil,
|
||||||
|
requestUpdate: { [weak self] tab, transition in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if strongSelf.visibleTabNodes.contains(where: { $0.value === tab }) {
|
||||||
|
var transition = transition
|
||||||
|
if strongSelf.interactiveTransitionState != nil {
|
||||||
|
transition = .immediate
|
||||||
|
}
|
||||||
|
strongSelf.requestUpdate(transition)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
requestUpdateApparentHeight: { [weak self] tab, transition in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if strongSelf.visibleTabNodes.contains(where: { $0.value === tab }) {
|
||||||
|
var transition = transition
|
||||||
|
if strongSelf.interactiveTransitionState != nil {
|
||||||
|
transition = .immediate
|
||||||
|
}
|
||||||
|
strongSelf.requestUpdateApparentHeight(transition)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openPeer: self.openPeer
|
||||||
|
)
|
||||||
|
self.addSubnode(tabNode)
|
||||||
|
self.visibleTabNodes[index] = tabNode
|
||||||
|
tabTransition = .immediate
|
||||||
|
}
|
||||||
|
|
||||||
|
let tabLayout = tabNode.update(presentationData: presentationData, constrainedSize: CGSize(width: constrainedSize.width, height: constrainedSize.height - topContentHeight), transition: tabTransition)
|
||||||
|
tabLayouts[index] = tabLayout
|
||||||
|
let currentFractionalTabIndex: CGFloat
|
||||||
|
if let interactiveTransitionState = self.interactiveTransitionState {
|
||||||
|
currentFractionalTabIndex = CGFloat(self.currentTabIndex) * (1.0 - interactiveTransitionState.progress) + CGFloat(interactiveTransitionState.toIndex) * interactiveTransitionState.progress
|
||||||
|
} else {
|
||||||
|
currentFractionalTabIndex = CGFloat(self.currentTabIndex)
|
||||||
|
}
|
||||||
|
let xOffset: CGFloat = (CGFloat(index) - currentFractionalTabIndex) * constrainedSize.width
|
||||||
|
let tabFrame = CGRect(origin: CGPoint(x: xOffset, y: topContentHeight), size: CGSize(width: constrainedSize.width, height: tabLayout.height + 100.0))
|
||||||
|
tabTransition.updateFrame(node: tabNode, frame: tabFrame)
|
||||||
|
if let initialReferenceFrame = initialReferenceFrame {
|
||||||
|
transition.animatePositionAdditive(node: tabNode, offset: CGPoint(x: initialReferenceFrame.minX - tabFrame.minX, y: 0.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var removedIndices: [Int] = []
|
||||||
|
for (index, tabNode) in self.visibleTabNodes {
|
||||||
|
if tabLayouts[index] == nil {
|
||||||
|
removedIndices.append(index)
|
||||||
|
|
||||||
|
var xOffset: CGFloat
|
||||||
|
if index > self.currentTabIndex {
|
||||||
|
xOffset = constrainedSize.width
|
||||||
|
} else {
|
||||||
|
xOffset = -constrainedSize.width
|
||||||
|
}
|
||||||
|
transition.updateFrame(node: tabNode, frame: CGRect(origin: CGPoint(x: xOffset, y: tabNode.frame.minY), size: tabNode.bounds.size), completion: { [weak tabNode] _ in
|
||||||
|
tabNode?.removeFromSupernode()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for index in removedIndices {
|
||||||
|
self.visibleTabNodes.removeValue(forKey: index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*var currentTabTransition = transition
|
||||||
if self.currentTabNode.bounds.isEmpty {
|
if self.currentTabNode.bounds.isEmpty {
|
||||||
currentTabTransition = .immediate
|
currentTabTransition = .immediate
|
||||||
}
|
}
|
||||||
@ -900,12 +1063,22 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
})
|
})
|
||||||
self.currentTabNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
self.currentTabNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
var contentSize = CGSize(width: constrainedSize.width, height: topContentHeight)
|
||||||
|
var apparentHeight = topContentHeight
|
||||||
|
|
||||||
|
if let interactiveTransitionState = self.interactiveTransitionState, let fromTabLayout = tabLayouts[self.currentTabIndex], let toTabLayout = tabLayouts[interactiveTransitionState.toIndex] {
|
||||||
|
let megedTabLayoutHeight = fromTabLayout.height * (1.0 - interactiveTransitionState.progress) + toTabLayout.height * interactiveTransitionState.progress
|
||||||
|
let megedTabLayoutApparentHeight = fromTabLayout.apparentHeight * (1.0 - interactiveTransitionState.progress) + toTabLayout.apparentHeight * interactiveTransitionState.progress
|
||||||
|
|
||||||
|
contentSize.height += megedTabLayoutHeight
|
||||||
|
apparentHeight += megedTabLayoutApparentHeight
|
||||||
|
} else if let tabLayout = tabLayouts[self.currentTabIndex] {
|
||||||
|
contentSize.height += tabLayout.height
|
||||||
|
apparentHeight += tabLayout.apparentHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
let contentSize = CGSize(width: currentTabLayout.size.width, height: topContentHeight + currentTabLayout.size.height)
|
|
||||||
|
|
||||||
let apparentHeight = topContentHeight + currentTabLayout.apparentHeight
|
|
||||||
|
|
||||||
return (contentSize, apparentHeight)
|
return (contentSize, apparentHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -591,6 +591,7 @@ private final class ContactsTabBarContextExtractedContentSource: ContextExtracte
|
|||||||
let keepInPlace: Bool = true
|
let keepInPlace: Bool = true
|
||||||
let ignoreContentTouches: Bool = true
|
let ignoreContentTouches: Bool = true
|
||||||
let blurBackground: Bool = true
|
let blurBackground: Bool = true
|
||||||
|
let centerActionsHorizontally: Bool = true
|
||||||
|
|
||||||
private let controller: ViewController
|
private let controller: ViewController
|
||||||
private let sourceNode: ContextExtractedContentContainingNode
|
private let sourceNode: ContextExtractedContentContainingNode
|
||||||
|
@ -677,12 +677,12 @@ func makeContextControllerActionsStackItem(items: ContextController.Items) -> Co
|
|||||||
}
|
}
|
||||||
|
|
||||||
final class ContextControllerActionsStackNode: ASDisplayNode {
|
final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||||
final class NavigationContainer: ASDisplayNode {
|
final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||||
var requestUpdate: ((ContainedViewLayoutTransition) -> Void)?
|
var requestUpdate: ((ContainedViewLayoutTransition) -> Void)?
|
||||||
var requestPop: (() -> Void)?
|
var requestPop: (() -> Void)?
|
||||||
var transitionFraction: CGFloat = 0.0
|
var transitionFraction: CGFloat = 0.0
|
||||||
|
|
||||||
private var panRecognizer: UIPanGestureRecognizer?
|
private var panRecognizer: InteractiveTransitionGestureRecognizer?
|
||||||
|
|
||||||
var isNavigationEnabled: Bool = false {
|
var isNavigationEnabled: Bool = false {
|
||||||
didSet {
|
didSet {
|
||||||
@ -696,9 +696,30 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
|||||||
self.clipsToBounds = true
|
self.clipsToBounds = true
|
||||||
self.cornerRadius = 14.0
|
self.cornerRadius = 14.0
|
||||||
|
|
||||||
let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
|
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), allowedDirections: { [weak self] point in
|
||||||
self.panRecognizer = panRecognizer
|
guard let strongSelf = self else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
let _ = strongSelf
|
||||||
|
return [.right]
|
||||||
|
})
|
||||||
|
panRecognizer.delegate = self
|
||||||
self.view.addGestureRecognizer(panRecognizer)
|
self.view.addGestureRecognizer(panRecognizer)
|
||||||
|
self.panRecognizer = panRecognizer
|
||||||
|
}
|
||||||
|
|
||||||
|
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
|
if let _ = otherGestureRecognizer as? InteractiveTransitionGestureRecognizer {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if let _ = otherGestureRecognizer as? UIPanGestureRecognizer {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||||
|
@ -480,7 +480,11 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
|||||||
|
|
||||||
let actionsSize = self.actionsStackNode.bounds.size
|
let actionsSize = self.actionsStackNode.bounds.size
|
||||||
|
|
||||||
let actionsPositionDeltaXDistance: CGFloat = 0.0
|
var actionsPositionDeltaXDistance: CGFloat = 0.0
|
||||||
|
if self.source.centerActionsHorizontally {
|
||||||
|
actionsPositionDeltaXDistance = currentContentScreenFrame.midX - self.actionsStackNode.frame.midX
|
||||||
|
}
|
||||||
|
|
||||||
let actionsVerticalTransitionDirection: CGFloat
|
let actionsVerticalTransitionDirection: CGFloat
|
||||||
if contentNode.frame.minY < self.actionsStackNode.frame.minY {
|
if contentNode.frame.minY < self.actionsStackNode.frame.minY {
|
||||||
actionsVerticalTransitionDirection = -1.0
|
actionsVerticalTransitionDirection = -1.0
|
||||||
@ -643,7 +647,10 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
|||||||
|
|
||||||
let actionsSize = self.actionsStackNode.bounds.size
|
let actionsSize = self.actionsStackNode.bounds.size
|
||||||
|
|
||||||
let actionsPositionDeltaXDistance: CGFloat = 0.0
|
var actionsPositionDeltaXDistance: CGFloat = 0.0
|
||||||
|
if self.source.centerActionsHorizontally {
|
||||||
|
actionsPositionDeltaXDistance = currentContentScreenFrame.midX - self.actionsStackNode.frame.midX
|
||||||
|
}
|
||||||
let actionsPositionDeltaYDistance = -animationInContentDistance + actionsVerticalTransitionDirection * actionsSize.height / 2.0 - contentActionsSpacing
|
let actionsPositionDeltaYDistance = -animationInContentDistance + actionsVerticalTransitionDirection * actionsSize.height / 2.0 - contentActionsSpacing
|
||||||
self.actionsStackNode.layer.animate(
|
self.actionsStackNode.layer.animate(
|
||||||
from: NSValue(cgPoint: CGPoint()),
|
from: NSValue(cgPoint: CGPoint()),
|
||||||
|
@ -452,17 +452,25 @@ class TabBarNode: ASDisplayNode {
|
|||||||
if let selectedIndex = self.selectedIndex, selectedIndex == i {
|
if let selectedIndex = self.selectedIndex, selectedIndex == i {
|
||||||
let (textImage, contentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
let (textImage, contentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
||||||
let (image, imageContentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedTextColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
|
let (image, imageContentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedTextColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
|
||||||
|
let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
||||||
|
let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
|
||||||
node.textImageNode.image = textImage
|
node.textImageNode.image = textImage
|
||||||
node.imageNode.image = image
|
node.imageNode.image = image
|
||||||
|
node.contextTextImageNode.image = contextTextImage
|
||||||
|
node.contextImageNode.image = contextImage
|
||||||
node.accessibilityLabel = item.item.title
|
node.accessibilityLabel = item.item.title
|
||||||
node.contentWidth = max(contentWidth, imageContentWidth)
|
node.contentWidth = max(contentWidth, imageContentWidth)
|
||||||
node.isSelected = true
|
node.isSelected = true
|
||||||
} else {
|
} else {
|
||||||
let (textImage, contentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
let (textImage, contentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
||||||
let (image, imageContentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
|
let (image, imageContentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
|
||||||
|
let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
||||||
|
let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
|
||||||
node.textImageNode.image = textImage
|
node.textImageNode.image = textImage
|
||||||
node.accessibilityLabel = item.item.title
|
node.accessibilityLabel = item.item.title
|
||||||
node.imageNode.image = image
|
node.imageNode.image = image
|
||||||
|
node.contextTextImageNode.image = contextTextImage
|
||||||
|
node.contextImageNode.image = contextImage
|
||||||
node.contentWidth = max(contentWidth, imageContentWidth)
|
node.contentWidth = max(contentWidth, imageContentWidth)
|
||||||
node.isSelected = false
|
node.isSelected = false
|
||||||
}
|
}
|
||||||
|
@ -7402,6 +7402,7 @@ private final class SettingsTabBarContextExtractedContentSource: ContextExtracte
|
|||||||
let keepInPlace: Bool = true
|
let keepInPlace: Bool = true
|
||||||
let ignoreContentTouches: Bool = true
|
let ignoreContentTouches: Bool = true
|
||||||
let blurBackground: Bool = true
|
let blurBackground: Bool = true
|
||||||
|
let centerActionsHorizontally: Bool = true
|
||||||
|
|
||||||
private let controller: ViewController
|
private let controller: ViewController
|
||||||
private let sourceNode: ContextExtractedContentContainingNode
|
private let sourceNode: ContextExtractedContentContainingNode
|
||||||
|
Loading…
x
Reference in New Issue
Block a user