mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Search filters improvements
This commit is contained in:
parent
7a8d5e27f2
commit
d59f695e6f
@ -5743,6 +5743,7 @@ Any member of this group will be able to see messages in the channel.";
|
||||
|
||||
"Call.AccountIsLoggedOnCurrentDevice" = "Sorry, you can't call %@ because that account is logged in to Telegram on the device you're using for the call.";
|
||||
|
||||
"ChatList.Search.FilterChats" = "Chats";
|
||||
"ChatList.Search.FilterMedia" = "Media";
|
||||
"ChatList.Search.FilterLinks" = "Links";
|
||||
"ChatList.Search.FilterFiles" = "Files";
|
||||
|
@ -106,7 +106,11 @@ extension CameraOutput: AVCaptureMetadataOutputObjectsDelegate {
|
||||
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
|
||||
let codes: [CameraCode] = metadataObjects.filter { $0.type == .qr }.compactMap { object in
|
||||
if let object = object as? AVMetadataMachineReadableCodeObject, let stringValue = object.stringValue, !stringValue.isEmpty {
|
||||
#if targetEnvironment(simulator)
|
||||
return CameraCode(type: .qr, message: stringValue, corners: [CGPoint(), CGPoint(), CGPoint(), CGPoint()])
|
||||
#else
|
||||
return CameraCode(type: .qr, message: stringValue, corners: object.corners)
|
||||
#endif
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
@ -109,7 +109,6 @@ public final class ChatListSearchRecentPeersNode: ASDisplayNode {
|
||||
private var strings: PresentationStrings
|
||||
private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings)>
|
||||
private let mode: HorizontalPeerItemMode
|
||||
private let sectionHeaderNode: ListSectionHeaderNode
|
||||
private let listView: ListView
|
||||
private let share: Bool
|
||||
|
||||
@ -133,15 +132,11 @@ public final class ChatListSearchRecentPeersNode: ASDisplayNode {
|
||||
self.peerContextAction = peerContextAction
|
||||
self.isPeerSelected = isPeerSelected
|
||||
|
||||
self.sectionHeaderNode = ListSectionHeaderNode(theme: theme)
|
||||
self.sectionHeaderNode.title = strings.DialogList_RecentTitlePeople.uppercased()
|
||||
|
||||
self.listView = ListView()
|
||||
self.listView.transform = CATransform3DMakeRotation(-CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.sectionHeaderNode)
|
||||
self.addSubnode(self.listView)
|
||||
|
||||
let peersDisposable = DisposableSet()
|
||||
@ -249,20 +244,14 @@ public final class ChatListSearchRecentPeersNode: ASDisplayNode {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.themeAndStringsPromise.set(.single((self.theme, self.strings)))
|
||||
|
||||
self.sectionHeaderNode.title = strings.DialogList_RecentTitlePeople.uppercased()
|
||||
self.sectionHeaderNode.updateTheme(theme: theme)
|
||||
}
|
||||
}
|
||||
|
||||
override public func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
||||
return CGSize(width: constrainedSize.width, height: 114.0)
|
||||
return CGSize(width: constrainedSize.width, height: 86.0)
|
||||
}
|
||||
|
||||
public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat) {
|
||||
self.sectionHeaderNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: 28.0))
|
||||
self.sectionHeaderNode.updateLayout(size: CGSize(width: size.width, height: 28.0), leftInset: leftInset, rightInset: rightInset)
|
||||
|
||||
var insets = UIEdgeInsets()
|
||||
insets.top += leftInset
|
||||
insets.bottom += rightInset
|
||||
@ -277,7 +266,7 @@ public final class ChatListSearchRecentPeersNode: ASDisplayNode {
|
||||
|
||||
|
||||
self.listView.bounds = CGRect(x: 0.0, y: 0.0, width: 92.0, height: size.width)
|
||||
self.listView.position = CGPoint(x: size.width / 2.0, y: 92.0 / 2.0 + 28.0)
|
||||
self.listView.position = CGPoint(x: size.width / 2.0, y: 92.0 / 2.0)
|
||||
self.listView.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: CGSize(width: 92.0, height: size.width), insets: insets, duration: 0.0, curve: .Default(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
self.itemCustomWidthValuePromise.set(itemCustomWidth)
|
||||
}
|
||||
|
@ -1683,39 +1683,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
||||
}
|
||||
|
||||
if let searchContentNode = strongSelf.searchContentNode {
|
||||
var updatedDisplayFiltersPanelImpl: ((Bool) -> Void)?
|
||||
|
||||
if let filterContainerNodeAndActivate = strongSelf.chatListDisplayNode.activateSearch(placeholderNode: searchContentNode.placeholderNode, navigationController: strongSelf.navigationController as? NavigationController, updatedDisplayFiltersPanel: { display in
|
||||
updatedDisplayFiltersPanelImpl?(display)
|
||||
}) {
|
||||
if let filterContainerNodeAndActivate = strongSelf.chatListDisplayNode.activateSearch(placeholderNode: searchContentNode.placeholderNode, navigationController: strongSelf.navigationController as? NavigationController) {
|
||||
let (filterContainerNode, activate) = filterContainerNodeAndActivate
|
||||
strongSelf.navigationBar?.setSecondaryContentNode(filterContainerNode, animated: false)
|
||||
if let parentController = strongSelf.parent as? TabBarController {
|
||||
parentController.navigationBar?.setSecondaryContentNode(filterContainerNode, animated: true)
|
||||
}
|
||||
activate()
|
||||
|
||||
var currentDisplay = true
|
||||
updatedDisplayFiltersPanelImpl = { [weak self, weak filterContainerNode] display in
|
||||
guard let strongSelf = self, let strongFilterContainerNode = filterContainerNode else {
|
||||
return
|
||||
}
|
||||
if currentDisplay != display {
|
||||
currentDisplay = display
|
||||
|
||||
let node = display ? strongFilterContainerNode : nil
|
||||
strongSelf.navigationBar?.setSecondaryContentNode(node, animated: false)
|
||||
if let parentController = strongSelf.parent as? TabBarController {
|
||||
parentController.navigationBar?.setSecondaryContentNode(node, animated: true)
|
||||
}
|
||||
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring)
|
||||
if let layout = strongSelf.validLayout {
|
||||
strongSelf.containerLayoutUpdated(layout, transition: transition)
|
||||
(strongSelf.parent as? TabBarController)?.updateLayout(transition: transition)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1147,7 +1147,7 @@ final class ChatListControllerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
func activateSearch(placeholderNode: SearchBarPlaceholderNode, navigationController: NavigationController?, updatedDisplayFiltersPanel: ((Bool) -> Void)?) -> (ASDisplayNode, () -> Void)? {
|
||||
func activateSearch(placeholderNode: SearchBarPlaceholderNode, navigationController: NavigationController?) -> (ASDisplayNode, () -> Void)? {
|
||||
guard let (containerLayout, _, _, cleanNavigationBarHeight) = self.containerLayout, let navigationBar = self.navigationBar, self.searchDisplayController == nil else {
|
||||
return nil
|
||||
}
|
||||
@ -1169,9 +1169,7 @@ final class ChatListControllerNode: ASDisplayNode {
|
||||
self?.controller?.present(c, in: .window(.root), with: a)
|
||||
}, presentInGlobalOverlay: { [weak self] c, a in
|
||||
self?.controller?.presentInGlobalOverlay(c, with: a)
|
||||
}, navigationController: navigationController, updatedDisplayFiltersPanel: { display in
|
||||
updatedDisplayFiltersPanel?(display)
|
||||
})
|
||||
}, navigationController: navigationController)
|
||||
|
||||
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: contentNode, cancel: { [weak self] in
|
||||
if let requestDeactivateSearch = self?.requestDeactivateSearch {
|
||||
|
@ -837,8 +837,6 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
|
||||
|
||||
self.scrollNode.view.contentSize = CGSize(width: leftOffset, height: size.height)
|
||||
|
||||
var previousFrame: CGRect?
|
||||
var nextFrame: CGRect?
|
||||
var selectedFrame: CGRect?
|
||||
if let selectedFilter = selectedFilter, let currentIndex = reorderedFilters.firstIndex(where: { $0.id == selectedFilter }) {
|
||||
func interpolateFrame(from fromValue: CGRect, to toValue: CGRect, t: CGFloat) -> CGRect {
|
||||
|
@ -94,7 +94,7 @@ class ChatListRecentPeersListItemNode: ListViewItemNode {
|
||||
let currentItem = self.item
|
||||
|
||||
return { [weak self] item, params, last in
|
||||
let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: 124.0), insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0))
|
||||
let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: 96.0), insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0))
|
||||
|
||||
return (nodeLayout, { [weak self] in
|
||||
var updatedTheme: PresentationTheme?
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,6 +8,7 @@ import TelegramCore
|
||||
import TelegramPresentationData
|
||||
|
||||
enum ChatListSearchFilter: Equatable {
|
||||
case chats
|
||||
case media
|
||||
case links
|
||||
case files
|
||||
@ -18,16 +19,18 @@ enum ChatListSearchFilter: Equatable {
|
||||
|
||||
var id: Int32 {
|
||||
switch self {
|
||||
case .media:
|
||||
case .chats:
|
||||
return 0
|
||||
case .links:
|
||||
case .media:
|
||||
return 1
|
||||
case .files:
|
||||
case .links:
|
||||
return 2
|
||||
case .music:
|
||||
case .files:
|
||||
return 3
|
||||
case .voice:
|
||||
case .music:
|
||||
return 4
|
||||
case .voice:
|
||||
return 5
|
||||
case let .peer(peerId, _, _, _):
|
||||
return peerId.id
|
||||
case let .date(date, _):
|
||||
@ -41,12 +44,10 @@ private final class ItemNode: ASDisplayNode {
|
||||
|
||||
private let iconNode: ASImageNode
|
||||
private let titleNode: ImmediateTextNode
|
||||
private let titleActiveNode: ImmediateTextNode
|
||||
private let buttonNode: HighlightTrackingButtonNode
|
||||
|
||||
private var selectionFraction: CGFloat = 0.0
|
||||
private(set) var unreadCount: Int = 0
|
||||
|
||||
private var isReordering: Bool = false
|
||||
|
||||
private var theme: PresentationTheme?
|
||||
|
||||
@ -63,11 +64,17 @@ private final class ItemNode: ASDisplayNode {
|
||||
self.titleNode.displaysAsynchronously = false
|
||||
self.titleNode.insets = UIEdgeInsets(top: titleInset, left: 0.0, bottom: titleInset, right: 0.0)
|
||||
|
||||
self.titleActiveNode = ImmediateTextNode()
|
||||
self.titleActiveNode.displaysAsynchronously = false
|
||||
self.titleActiveNode.insets = UIEdgeInsets(top: titleInset, left: 0.0, bottom: titleInset, right: 0.0)
|
||||
self.titleActiveNode.alpha = 0.0
|
||||
|
||||
self.buttonNode = HighlightTrackingButtonNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.titleActiveNode)
|
||||
self.addSubnode(self.iconNode)
|
||||
self.addSubnode(self.buttonNode)
|
||||
|
||||
@ -95,27 +102,32 @@ private final class ItemNode: ASDisplayNode {
|
||||
self.pressed()
|
||||
}
|
||||
|
||||
func update(type: ChatListSearchFilter, presentationData: PresentationData, transition: ContainedViewLayoutTransition) {
|
||||
func update(type: ChatListSearchFilter, presentationData: PresentationData, selectionFraction: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.selectionFraction = selectionFraction
|
||||
|
||||
let title: String
|
||||
let icon: UIImage?
|
||||
|
||||
let color = presentationData.theme.list.itemSecondaryTextColor
|
||||
switch type {
|
||||
case .chats:
|
||||
title = presentationData.strings.ChatList_Search_FilterChats
|
||||
icon = nil
|
||||
case .media:
|
||||
title = presentationData.strings.ChatList_Search_FilterMedia
|
||||
icon = generateTintedImage(image: UIImage(bundleImageName: "Chat List/Search/Media"), color: color)
|
||||
icon = nil
|
||||
case .links:
|
||||
title = presentationData.strings.ChatList_Search_FilterLinks
|
||||
icon = generateTintedImage(image: UIImage(bundleImageName: "Chat List/Search/Links"), color: color)
|
||||
icon = nil
|
||||
case .files:
|
||||
title = presentationData.strings.ChatList_Search_FilterFiles
|
||||
icon = generateTintedImage(image: UIImage(bundleImageName: "Chat List/Search/Files"), color: color)
|
||||
icon = nil
|
||||
case .music:
|
||||
title = presentationData.strings.ChatList_Search_FilterMusic
|
||||
icon = generateTintedImage(image: UIImage(bundleImageName: "Chat List/Search/Music"), color: color)
|
||||
icon = nil
|
||||
case .voice:
|
||||
title = presentationData.strings.ChatList_Search_FilterVoice
|
||||
icon = generateTintedImage(image: UIImage(bundleImageName: "Chat List/Search/Voice"), color: color)
|
||||
icon = nil
|
||||
case let .peer(peerId, isGroup, displayTitle, _):
|
||||
title = displayTitle
|
||||
let image: UIImage?
|
||||
@ -133,6 +145,12 @@ private final class ItemNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.medium(14.0), textColor: color)
|
||||
self.titleActiveNode.attributedText = NSAttributedString(string: title, font: Font.medium(14.0), textColor: presentationData.theme.list.itemAccentColor)
|
||||
|
||||
let selectionAlpha: CGFloat = selectionFraction * selectionFraction
|
||||
let deselectionAlpha: CGFloat = 1.0// - selectionFraction
|
||||
transition.updateAlpha(node: self.titleNode, alpha: deselectionAlpha)
|
||||
transition.updateAlpha(node: self.titleActiveNode, alpha: selectionAlpha)
|
||||
|
||||
if self.theme !== presentationData.theme {
|
||||
self.theme = presentationData.theme
|
||||
@ -141,14 +159,17 @@ private final class ItemNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
func updateLayout(height: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
let iconInset: CGFloat = 22.0
|
||||
var iconInset: CGFloat = 0.0
|
||||
if let image = self.iconNode.image {
|
||||
iconInset = 22.0
|
||||
self.iconNode.frame = CGRect(x: 0.0, y: floorToScreenPixels((height - image.size.height) / 2.0), width: image.size.width, height: image.size.height)
|
||||
}
|
||||
|
||||
let titleSize = self.titleNode.updateLayout(CGSize(width: 160.0, height: .greatestFiniteMagnitude))
|
||||
let _ = self.titleActiveNode.updateLayout(CGSize(width: 160.0, height: .greatestFiniteMagnitude))
|
||||
let titleFrame = CGRect(origin: CGPoint(x: -self.titleNode.insets.left + iconInset, y: floor((height - titleSize.height) / 2.0)), size: titleSize)
|
||||
self.titleNode.frame = titleFrame
|
||||
self.titleActiveNode.frame = titleFrame
|
||||
|
||||
return titleSize.width - self.titleNode.insets.left - self.titleNode.insets.right + iconInset
|
||||
}
|
||||
@ -177,15 +198,23 @@ enum ChatListSearchFilterEntry: Equatable {
|
||||
|
||||
final class ChatListSearchFiltersContainerNode: ASDisplayNode {
|
||||
private let scrollNode: ASScrollNode
|
||||
private let selectedLineNode: ASImageNode
|
||||
private var itemNodes: [ChatListSearchFilterEntryId: ItemNode] = [:]
|
||||
|
||||
var filterPressed: ((ChatListSearchFilter) -> Void)?
|
||||
|
||||
private var currentParams: (size: CGSize, sideInset: CGFloat, filters: [ChatListSearchFilterEntry], presentationData: PresentationData)?
|
||||
private var currentParams: (size: CGSize, sideInset: CGFloat, filters: [ChatListSearchFilterEntry], selectedFilter: ChatListSearchFilterEntryId?, transitionFraction: CGFloat, presentationData: PresentationData)?
|
||||
|
||||
private var previousSelectedAbsFrame: CGRect?
|
||||
private var previousSelectedFrame: CGRect?
|
||||
|
||||
override init() {
|
||||
self.scrollNode = ASScrollNode()
|
||||
|
||||
self.selectedLineNode = ASImageNode()
|
||||
self.selectedLineNode.displaysAsynchronously = false
|
||||
self.selectedLineNode.displayWithoutProcessing = true
|
||||
|
||||
super.init()
|
||||
|
||||
self.scrollNode.view.showsHorizontalScrollIndicator = false
|
||||
@ -197,21 +226,31 @@ final class ChatListSearchFiltersContainerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
self.addSubnode(self.scrollNode)
|
||||
self.addSubnode(self.selectedLineNode)
|
||||
}
|
||||
|
||||
func cancelAnimations() {
|
||||
self.scrollNode.layer.removeAllAnimations()
|
||||
}
|
||||
|
||||
func update(size: CGSize, sideInset: CGFloat, filters: [ChatListSearchFilterEntry], presentationData: PresentationData, transition proposedTransition: ContainedViewLayoutTransition) {
|
||||
func update(size: CGSize, sideInset: CGFloat, filters: [ChatListSearchFilterEntry], selectedFilter: ChatListSearchFilterEntryId?, transitionFraction: CGFloat, presentationData: PresentationData, transition proposedTransition: ContainedViewLayoutTransition) {
|
||||
let isFirstTime = self.currentParams == nil
|
||||
let transition: ContainedViewLayoutTransition = isFirstTime ? .immediate : proposedTransition
|
||||
|
||||
var focusOnSelectedFilter = self.currentParams?.selectedFilter != selectedFilter
|
||||
let previousScrollBounds = self.scrollNode.bounds
|
||||
let previousContentWidth = self.scrollNode.view.contentSize.width
|
||||
|
||||
if self.currentParams?.presentationData.theme !== presentationData.theme {
|
||||
self.backgroundColor = presentationData.theme.rootController.navigationBar.backgroundColor
|
||||
self.selectedLineNode.image = generateImage(CGSize(width: 8.0, height: 4.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(presentationData.theme.list.itemAccentColor.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.width)))
|
||||
})?.stretchableImage(withLeftCapWidth: 4, topCapHeight: 1)
|
||||
}
|
||||
|
||||
self.currentParams = (size: size, sideInset: sideInset, filters: filters, presentationData: presentationData)
|
||||
self.currentParams = (size: size, sideInset: sideInset, filters: filters, selectedFilter: selectedFilter, transitionFraction: transitionFraction, presentationData: presentationData)
|
||||
|
||||
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
@ -229,7 +268,19 @@ final class ChatListSearchFiltersContainerNode: ASDisplayNode {
|
||||
})
|
||||
self.itemNodes[filter.id] = itemNode
|
||||
}
|
||||
itemNode.update(type: type, presentationData: presentationData, transition: itemNodeTransition)
|
||||
|
||||
let selectionFraction: CGFloat
|
||||
if selectedFilter == filter.id {
|
||||
selectionFraction = 1.0 - abs(transitionFraction)
|
||||
} else if i != 0 && selectedFilter == filters[i - 1].id {
|
||||
selectionFraction = max(0.0, -transitionFraction)
|
||||
} else if i != filters.count - 1 && selectedFilter == filters[i + 1].id {
|
||||
selectionFraction = max(0.0, transitionFraction)
|
||||
} else {
|
||||
selectionFraction = 0.0
|
||||
}
|
||||
|
||||
itemNode.update(type: type, presentationData: presentationData, selectionFraction: selectionFraction, transition: itemNodeTransition)
|
||||
}
|
||||
}
|
||||
|
||||
@ -253,6 +304,7 @@ final class ChatListSearchFiltersContainerNode: ASDisplayNode {
|
||||
|
||||
var tabSizes: [(ChatListSearchFilterEntryId, CGSize, ItemNode, Bool)] = []
|
||||
var totalRawTabSize: CGFloat = 0.0
|
||||
var selectionFrames: [CGRect] = []
|
||||
|
||||
for filter in filters {
|
||||
guard let itemNode = self.itemNodes[filter.id] else {
|
||||
@ -308,6 +360,8 @@ final class ChatListSearchFiltersContainerNode: ASDisplayNode {
|
||||
paneNode.updateArea(size: paneFrame.size, sideInset: spacing / 2.0, transition: itemNodeTransition)
|
||||
paneNode.hitTestSlop = UIEdgeInsets(top: 0.0, left: -spacing / 2.0, bottom: 0.0, right: -spacing / 2.0)
|
||||
|
||||
selectionFrames.append(paneFrame)
|
||||
|
||||
leftOffset += paneNodeSize.width + spacing
|
||||
}
|
||||
leftOffset -= spacing
|
||||
@ -315,6 +369,66 @@ final class ChatListSearchFiltersContainerNode: ASDisplayNode {
|
||||
|
||||
self.scrollNode.view.contentSize = CGSize(width: leftOffset, height: size.height)
|
||||
|
||||
var selectedFrame: CGRect?
|
||||
if let selectedFilter = selectedFilter, let currentIndex = filters.firstIndex(where: { $0.id == selectedFilter }) {
|
||||
func interpolateFrame(from fromValue: CGRect, to toValue: CGRect, t: CGFloat) -> CGRect {
|
||||
return CGRect(x: floorToScreenPixels(toValue.origin.x * t + fromValue.origin.x * (1.0 - t)), y: floorToScreenPixels(toValue.origin.y * t + fromValue.origin.y * (1.0 - t)), width: floorToScreenPixels(toValue.size.width * t + fromValue.size.width * (1.0 - t)), height: floorToScreenPixels(toValue.size.height * t + fromValue.size.height * (1.0 - t)))
|
||||
}
|
||||
|
||||
if currentIndex != 0 && transitionFraction > 0.0 {
|
||||
let currentFrame = selectionFrames[currentIndex]
|
||||
let previousFrame = selectionFrames[currentIndex - 1]
|
||||
selectedFrame = interpolateFrame(from: currentFrame, to: previousFrame, t: abs(transitionFraction))
|
||||
} else if currentIndex != filters.count - 1 && transitionFraction < 0.0 {
|
||||
let currentFrame = selectionFrames[currentIndex]
|
||||
let previousFrame = selectionFrames[currentIndex + 1]
|
||||
selectedFrame = interpolateFrame(from: currentFrame, to: previousFrame, t: abs(transitionFraction))
|
||||
} else {
|
||||
selectedFrame = selectionFrames[currentIndex]
|
||||
}
|
||||
}
|
||||
|
||||
if let selectedFrame = selectedFrame {
|
||||
let wasAdded = self.selectedLineNode.isHidden
|
||||
self.selectedLineNode.isHidden = false
|
||||
let lineFrame = CGRect(origin: CGPoint(x: selectedFrame.minX, y: size.height - 4.0), size: CGSize(width: selectedFrame.width, height: 4.0))
|
||||
if wasAdded {
|
||||
self.selectedLineNode.frame = lineFrame
|
||||
self.selectedLineNode.alpha = 0.0
|
||||
} else {
|
||||
transition.updateFrame(node: self.selectedLineNode, frame: lineFrame)
|
||||
}
|
||||
transition.updateAlpha(node: self.selectedLineNode, alpha: 1.0)
|
||||
|
||||
if let previousSelectedFrame = self.previousSelectedFrame {
|
||||
let previousContentOffsetX = max(0.0, min(previousContentWidth - previousScrollBounds.width, floor(previousSelectedFrame.midX - previousScrollBounds.width / 2.0)))
|
||||
if abs(previousContentOffsetX - previousScrollBounds.minX) < 1.0 {
|
||||
focusOnSelectedFilter = true
|
||||
}
|
||||
}
|
||||
|
||||
if focusOnSelectedFilter {
|
||||
let updatedBounds: CGRect
|
||||
if transitionFraction.isZero && selectedFilter == filters.first?.id {
|
||||
updatedBounds = CGRect(origin: CGPoint(), size: self.scrollNode.bounds.size)
|
||||
} else if transitionFraction.isZero && selectedFilter == filters.last?.id {
|
||||
updatedBounds = CGRect(origin: CGPoint(x: max(0.0, self.scrollNode.view.contentSize.width - self.scrollNode.bounds.width), y: 0.0), size: self.scrollNode.bounds.size)
|
||||
} else {
|
||||
let contentOffsetX = max(0.0, min(self.scrollNode.view.contentSize.width - self.scrollNode.bounds.width, floor(selectedFrame.midX - self.scrollNode.bounds.width / 2.0)))
|
||||
updatedBounds = CGRect(origin: CGPoint(x: contentOffsetX, y: 0.0), size: self.scrollNode.bounds.size)
|
||||
}
|
||||
self.scrollNode.bounds = updatedBounds
|
||||
}
|
||||
transition.animateHorizontalOffsetAdditive(node: self.scrollNode, offset: previousScrollBounds.minX - self.scrollNode.bounds.minX)
|
||||
|
||||
self.previousSelectedAbsFrame = selectedFrame.offsetBy(dx: -self.scrollNode.bounds.minX, dy: 0.0)
|
||||
self.previousSelectedFrame = selectedFrame
|
||||
} else {
|
||||
self.selectedLineNode.isHidden = true
|
||||
self.previousSelectedAbsFrame = nil
|
||||
self.previousSelectedFrame = nil
|
||||
}
|
||||
|
||||
if updated && self.scrollNode.view.contentOffset.x > 0.0 {
|
||||
self.scrollNode.view.contentOffset = CGPoint()
|
||||
}
|
||||
|
2042
submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift
Normal file
2042
submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift
Normal file
File diff suppressed because it is too large
Load Diff
@ -117,6 +117,7 @@ final class ChatListSearchMessageSelectionPanelNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
func update(layout: ContainerViewLayout, presentationData: PresentationData, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
self.validLayout = layout
|
||||
if presentationData.theme !== self.theme {
|
||||
self.theme = presentationData.theme
|
||||
|
||||
|
@ -0,0 +1,539 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import Postbox
|
||||
import SyncCore
|
||||
import TelegramCore
|
||||
import AccountContext
|
||||
import ContextUI
|
||||
|
||||
protocol ChatListSearchPaneNode: ASDisplayNode {
|
||||
var isReady: Signal<Bool, NoError> { get }
|
||||
|
||||
func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition)
|
||||
func scrollToTop() -> Bool
|
||||
func cancelPreviewGestures()
|
||||
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
||||
func addToTransitionSurface(view: UIView)
|
||||
func updateHiddenMedia()
|
||||
func updateSelectedMessages(animated: Bool)
|
||||
func previewViewAndActionAtLocation(_ location: CGPoint) -> (UIView, CGRect, Any)?
|
||||
var searchCurrentMessages: [Message]? { get }
|
||||
}
|
||||
|
||||
final class ChatListSearchPaneWrapper {
|
||||
let key: ChatListSearchPaneKey
|
||||
let node: ChatListSearchPaneNode
|
||||
var isAnimatingOut: Bool = false
|
||||
private var appliedParams: (CGSize, CGFloat, CGFloat, CGFloat, PresentationData)?
|
||||
|
||||
init(key: ChatListSearchPaneKey, node: ChatListSearchPaneNode) {
|
||||
self.key = key
|
||||
self.node = node
|
||||
}
|
||||
|
||||
func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
|
||||
if let (currentSize, currentSideInset, currentBottomInset, visibleHeight, currentPresentationData) = self.appliedParams {
|
||||
if currentSize == size && currentSideInset == sideInset && currentBottomInset == bottomInset && currentPresentationData === presentationData {
|
||||
return
|
||||
}
|
||||
}
|
||||
self.appliedParams = (size, sideInset, bottomInset, visibleHeight, presentationData)
|
||||
self.node.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: synchronous, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
enum ChatListSearchPaneKey {
|
||||
case chats
|
||||
case media
|
||||
case links
|
||||
case files
|
||||
case music
|
||||
case voice
|
||||
}
|
||||
|
||||
private let availablePanes: [ChatListSearchPaneKey] = [.chats, .media, .links, .files, .music, .voice]
|
||||
|
||||
struct ChatListSearchPaneSpecifier: Equatable {
|
||||
var key: ChatListSearchPaneKey
|
||||
var title: String
|
||||
}
|
||||
|
||||
private func interpolateFrame(from fromValue: CGRect, to toValue: CGRect, t: CGFloat) -> CGRect {
|
||||
return CGRect(x: floorToScreenPixels(toValue.origin.x * t + fromValue.origin.x * (1.0 - t)), y: floorToScreenPixels(toValue.origin.y * t + fromValue.origin.y * (1.0 - t)), width: floorToScreenPixels(toValue.size.width * t + fromValue.size.width * (1.0 - t)), height: floorToScreenPixels(toValue.size.height * t + fromValue.size.height * (1.0 - t)))
|
||||
}
|
||||
|
||||
private final class ChatListSearchPendingPane {
|
||||
let pane: ChatListSearchPaneWrapper
|
||||
private var disposable: Disposable?
|
||||
var isReady: Bool = false
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
interaction: ChatListSearchInteraction,
|
||||
navigationController: NavigationController?,
|
||||
peersFilter: ChatListNodePeersFilter,
|
||||
searchQuery: Signal<String?, NoError>,
|
||||
searchOptions: Signal<ChatListSearchOptions?, NoError>,
|
||||
key: ChatListSearchPaneKey,
|
||||
hasBecomeReady: @escaping (ChatListSearchPaneKey) -> Void
|
||||
) {
|
||||
let paneNode: ChatListSearchPaneNode
|
||||
switch key {
|
||||
case .chats:
|
||||
paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, tagMask: nil, peersFilter: peersFilter, searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController)
|
||||
case .media:
|
||||
paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, tagMask: .photoOrVideo, peersFilter: [], searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController)
|
||||
case .files:
|
||||
paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, tagMask: .file, peersFilter: [], searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController)
|
||||
case .links:
|
||||
paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, tagMask: .webPage, peersFilter: [], searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController)
|
||||
case .voice:
|
||||
paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, tagMask: .voiceOrInstantVideo, peersFilter: [], searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController)
|
||||
case .music:
|
||||
paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, tagMask: .music, peersFilter: [], searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController)
|
||||
}
|
||||
|
||||
self.pane = ChatListSearchPaneWrapper(key: key, node: paneNode)
|
||||
self.disposable = (paneNode.isReady
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
self?.isReady = true
|
||||
hasBecomeReady(key)
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.disposable?.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
final class ChatListSearchPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
private let context: AccountContext
|
||||
private let peersFilter: ChatListNodePeersFilter
|
||||
private let searchQuery: Signal<String?, NoError>
|
||||
private let searchOptions: Signal<ChatListSearchOptions?, NoError>
|
||||
private let navigationController: NavigationController?
|
||||
var interaction: ChatListSearchInteraction?
|
||||
|
||||
private let coveringBackgroundNode: ASDisplayNode
|
||||
private let separatorNode: ASDisplayNode
|
||||
|
||||
let isReady = Promise<Bool>()
|
||||
var didSetIsReady = false
|
||||
|
||||
private var currentParams: (size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData)?
|
||||
|
||||
private(set) var currentPaneKey: ChatListSearchPaneKey?
|
||||
var pendingSwitchToPaneKey: ChatListSearchPaneKey?
|
||||
|
||||
var currentPane: ChatListSearchPaneWrapper? {
|
||||
if let currentPaneKey = self.currentPaneKey {
|
||||
return self.currentPanes[currentPaneKey]
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private var currentPanes: [ChatListSearchPaneKey: ChatListSearchPaneWrapper] = [:]
|
||||
private var pendingPanes: [ChatListSearchPaneKey: ChatListSearchPendingPane] = [:]
|
||||
|
||||
private var transitionFraction: CGFloat = 0.0
|
||||
|
||||
var openPeerContextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
|
||||
var currentPaneUpdated: ((ChatListSearchPaneKey?, CGFloat, ContainedViewLayoutTransition) -> Void)?
|
||||
var requestExpandTabs: (() -> Bool)?
|
||||
|
||||
private var currentAvailablePanes: [ChatListSearchPaneKey]?
|
||||
|
||||
init(context: AccountContext, peersFilter: ChatListNodePeersFilter, searchQuery: Signal<String?, NoError>, searchOptions: Signal<ChatListSearchOptions?, NoError>, navigationController: NavigationController?) {
|
||||
self.context = context
|
||||
self.peersFilter = peersFilter
|
||||
self.searchQuery = searchQuery
|
||||
self.searchOptions = searchOptions
|
||||
self.navigationController = navigationController
|
||||
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.isLayerBacked = true
|
||||
|
||||
self.coveringBackgroundNode = ASDisplayNode()
|
||||
self.coveringBackgroundNode.isLayerBacked = true
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.separatorNode)
|
||||
self.addSubnode(self.coveringBackgroundNode)
|
||||
}
|
||||
|
||||
func requestSelectPane(_ key: ChatListSearchPaneKey) {
|
||||
if self.currentPaneKey == key {
|
||||
if let requestExpandTabs = self.requestExpandTabs, requestExpandTabs() {
|
||||
} else {
|
||||
let _ = self.currentPane?.node.scrollToTop()
|
||||
}
|
||||
return
|
||||
}
|
||||
if self.currentPanes[key] != nil {
|
||||
self.currentPaneKey = key
|
||||
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData) = self.currentParams {
|
||||
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
} else if self.pendingSwitchToPaneKey != key {
|
||||
self.pendingSwitchToPaneKey = key
|
||||
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData) = self.currentParams {
|
||||
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), allowedDirections: { [weak self] point in
|
||||
guard let strongSelf = self, let currentPaneKey = strongSelf.currentPaneKey, let index = availablePanes.firstIndex(of: currentPaneKey) else {
|
||||
return []
|
||||
}
|
||||
if index == 0 {
|
||||
return .left
|
||||
}
|
||||
return [.left, .right]
|
||||
})
|
||||
panRecognizer.delegate = self
|
||||
panRecognizer.delaysTouchesBegan = false
|
||||
panRecognizer.cancelsTouchesInView = true
|
||||
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:
|
||||
func cancelContextGestures(view: UIView) {
|
||||
if let gestureRecognizers = view.gestureRecognizers {
|
||||
for gesture in gestureRecognizers {
|
||||
if let gesture = gesture as? ContextGesture {
|
||||
gesture.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
for subview in view.subviews {
|
||||
cancelContextGestures(view: subview)
|
||||
}
|
||||
}
|
||||
|
||||
cancelContextGestures(view: self.view)
|
||||
case .changed:
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData) = self.currentParams, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) {
|
||||
let translation = recognizer.translation(in: self.view)
|
||||
var transitionFraction = translation.x / size.width
|
||||
if currentIndex <= 0 {
|
||||
transitionFraction = min(0.0, transitionFraction)
|
||||
}
|
||||
if currentIndex >= availablePanes.count - 1 {
|
||||
transitionFraction = max(0.0, transitionFraction)
|
||||
}
|
||||
self.transitionFraction = transitionFraction
|
||||
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, transition: .immediate)
|
||||
}
|
||||
case .cancelled, .ended:
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData) = self.currentParams, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) {
|
||||
let translation = recognizer.translation(in: self.view)
|
||||
let velocity = recognizer.velocity(in: self.view)
|
||||
var directionIsToRight: Bool?
|
||||
if abs(velocity.x) > 10.0 {
|
||||
directionIsToRight = velocity.x < 0.0
|
||||
} else {
|
||||
if abs(translation.x) > size.width / 2.0 {
|
||||
directionIsToRight = translation.x > size.width / 2.0
|
||||
}
|
||||
}
|
||||
var updated = false
|
||||
if let directionIsToRight = directionIsToRight {
|
||||
var updatedIndex = currentIndex
|
||||
if directionIsToRight {
|
||||
updatedIndex = min(updatedIndex + 1, availablePanes.count - 1)
|
||||
} else {
|
||||
updatedIndex = max(updatedIndex - 1, 0)
|
||||
}
|
||||
let switchToKey = availablePanes[updatedIndex]
|
||||
if switchToKey != self.currentPaneKey && self.currentPanes[switchToKey] != nil{
|
||||
self.currentPaneKey = switchToKey
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
self.transitionFraction = 0.0
|
||||
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, transition: .animated(duration: 0.35, curve: .spring))
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func scrollToTop() -> Bool {
|
||||
if let currentPane = self.currentPane {
|
||||
return currentPane.node.scrollToTop()
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func updateHiddenMedia() {
|
||||
self.currentPane?.node.updateHiddenMedia()
|
||||
}
|
||||
|
||||
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
|
||||
return self.currentPane?.node.transitionNodeForGallery(messageId: messageId, media: media)
|
||||
}
|
||||
|
||||
func updateSelectedMessageIds(_ selectedMessageIds: Set<MessageId>?, animated: Bool) {
|
||||
for (_, pane) in self.currentPanes {
|
||||
pane.node.updateSelectedMessages(animated: animated)
|
||||
}
|
||||
for (_, pane) in self.pendingPanes {
|
||||
pane.pane.node.updateSelectedMessages(animated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, transition: ContainedViewLayoutTransition) {
|
||||
let previousAvailablePanes = self.currentAvailablePanes ?? []
|
||||
self.currentAvailablePanes = availablePanes
|
||||
|
||||
if let currentPaneKey = self.currentPaneKey, !availablePanes.contains(currentPaneKey) {
|
||||
var nextCandidatePaneKey: ChatListSearchPaneKey?
|
||||
if let index = previousAvailablePanes.firstIndex(of: currentPaneKey), index != 0 {
|
||||
for i in (0 ... index - 1).reversed() {
|
||||
if availablePanes.contains(previousAvailablePanes[i]) {
|
||||
nextCandidatePaneKey = previousAvailablePanes[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
if nextCandidatePaneKey == nil {
|
||||
nextCandidatePaneKey = availablePanes.first
|
||||
}
|
||||
|
||||
if let nextCandidatePaneKey = nextCandidatePaneKey {
|
||||
self.pendingSwitchToPaneKey = nextCandidatePaneKey
|
||||
} else {
|
||||
self.currentPaneKey = nil
|
||||
self.pendingSwitchToPaneKey = nil
|
||||
}
|
||||
} else if self.currentPaneKey == nil {
|
||||
self.pendingSwitchToPaneKey = availablePanes.first
|
||||
}
|
||||
|
||||
let currentIndex: Int?
|
||||
if let currentPaneKey = self.currentPaneKey {
|
||||
currentIndex = availablePanes.firstIndex(of: currentPaneKey)
|
||||
} else {
|
||||
currentIndex = nil
|
||||
}
|
||||
|
||||
self.currentParams = (size, sideInset, bottomInset, visibleHeight, presentationData)
|
||||
|
||||
transition.updateAlpha(node: self.coveringBackgroundNode, alpha: 0.0)
|
||||
|
||||
self.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor
|
||||
self.coveringBackgroundNode.backgroundColor = presentationData.theme.rootController.navigationBar.backgroundColor
|
||||
self.separatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
|
||||
let tabsHeight: CGFloat = 48.0
|
||||
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: size.width, height: UIScreenPixel)))
|
||||
transition.updateFrame(node: self.coveringBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: size.width, height: tabsHeight + UIScreenPixel)))
|
||||
|
||||
let paneFrame = CGRect(origin: CGPoint(x: 0.0, y: tabsHeight), size: CGSize(width: size.width, height: size.height - tabsHeight))
|
||||
|
||||
var visiblePaneIndices: [Int] = []
|
||||
var requiredPendingKeys: [ChatListSearchPaneKey] = []
|
||||
if let currentIndex = currentIndex {
|
||||
if currentIndex != 0 {
|
||||
visiblePaneIndices.append(currentIndex - 1)
|
||||
}
|
||||
visiblePaneIndices.append(currentIndex)
|
||||
if currentIndex != availablePanes.count - 1 {
|
||||
visiblePaneIndices.append(currentIndex + 1)
|
||||
}
|
||||
|
||||
for index in visiblePaneIndices {
|
||||
let key = availablePanes[index]
|
||||
if self.currentPanes[key] == nil && self.pendingPanes[key] == nil {
|
||||
requiredPendingKeys.append(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
if let pendingSwitchToPaneKey = self.pendingSwitchToPaneKey {
|
||||
if self.currentPanes[pendingSwitchToPaneKey] == nil && self.pendingPanes[pendingSwitchToPaneKey] == nil {
|
||||
if !requiredPendingKeys.contains(pendingSwitchToPaneKey) {
|
||||
requiredPendingKeys.append(pendingSwitchToPaneKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for key in requiredPendingKeys {
|
||||
if self.pendingPanes[key] == nil {
|
||||
var leftScope = false
|
||||
let pane = ChatListSearchPendingPane(
|
||||
context: self.context,
|
||||
interaction: self.interaction!,
|
||||
navigationController: self.navigationController,
|
||||
peersFilter: self.peersFilter,
|
||||
searchQuery: self.searchQuery,
|
||||
searchOptions: self.searchOptions,
|
||||
key: key,
|
||||
hasBecomeReady: { [weak self] key in
|
||||
let apply: () -> Void = {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData) = strongSelf.currentParams {
|
||||
var transition: ContainedViewLayoutTransition = .immediate
|
||||
if strongSelf.pendingSwitchToPaneKey == key && strongSelf.currentPaneKey != nil {
|
||||
transition = .animated(duration: 0.4, curve: .spring)
|
||||
}
|
||||
strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, transition: transition)
|
||||
}
|
||||
}
|
||||
if leftScope {
|
||||
apply()
|
||||
}
|
||||
}
|
||||
)
|
||||
self.pendingPanes[key] = pane
|
||||
pane.pane.node.frame = paneFrame
|
||||
pane.pane.update(size: paneFrame.size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: true, transition: .immediate)
|
||||
leftScope = true
|
||||
}
|
||||
}
|
||||
|
||||
for (key, pane) in self.pendingPanes {
|
||||
pane.pane.node.frame = paneFrame
|
||||
pane.pane.update(size: paneFrame.size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: self.currentPaneKey == nil, transition: .immediate)
|
||||
|
||||
if pane.isReady {
|
||||
self.pendingPanes.removeValue(forKey: key)
|
||||
self.currentPanes[key] = pane.pane
|
||||
}
|
||||
}
|
||||
|
||||
var paneDefaultTransition = transition
|
||||
var previousPaneKey: ChatListSearchPaneKey?
|
||||
var paneSwitchAnimationOffset: CGFloat = 0.0
|
||||
|
||||
var updatedCurrentIndex = currentIndex
|
||||
var animatePaneTransitionOffset: CGFloat?
|
||||
if let pendingSwitchToPaneKey = self.pendingSwitchToPaneKey, let pane = self.currentPanes[pendingSwitchToPaneKey] {
|
||||
self.pendingSwitchToPaneKey = nil
|
||||
previousPaneKey = self.currentPaneKey
|
||||
self.currentPaneKey = pendingSwitchToPaneKey
|
||||
updatedCurrentIndex = availablePanes.firstIndex(of: pendingSwitchToPaneKey)
|
||||
if let previousPaneKey = previousPaneKey, let previousIndex = availablePanes.firstIndex(of: previousPaneKey), let updatedCurrentIndex = updatedCurrentIndex {
|
||||
if updatedCurrentIndex < previousIndex {
|
||||
paneSwitchAnimationOffset = -size.width
|
||||
} else {
|
||||
paneSwitchAnimationOffset = size.width
|
||||
}
|
||||
}
|
||||
|
||||
paneDefaultTransition = .immediate
|
||||
}
|
||||
|
||||
for (key, pane) in self.currentPanes {
|
||||
if let index = availablePanes.firstIndex(of: key), let updatedCurrentIndex = updatedCurrentIndex {
|
||||
var paneWasAdded = false
|
||||
if pane.node.supernode == nil {
|
||||
self.addSubnode(pane.node)
|
||||
paneWasAdded = true
|
||||
}
|
||||
let indexOffset = CGFloat(index - updatedCurrentIndex)
|
||||
|
||||
let paneTransition: ContainedViewLayoutTransition = paneWasAdded ? .immediate : paneDefaultTransition
|
||||
let adjustedFrame = paneFrame.offsetBy(dx: size.width * self.transitionFraction + indexOffset * size.width, dy: 0.0)
|
||||
|
||||
let paneCompletion: () -> Void = { [weak self, weak pane] in
|
||||
guard let strongSelf = self, let pane = pane else {
|
||||
return
|
||||
}
|
||||
pane.isAnimatingOut = false
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData) = strongSelf.currentParams {
|
||||
if let currentPaneKey = strongSelf.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey), let paneIndex = availablePanes.firstIndex(of: key), abs(paneIndex - currentIndex) <= 1 {
|
||||
} else {
|
||||
if let pane = strongSelf.currentPanes.removeValue(forKey: key) {
|
||||
pane.node.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let previousPaneKey = previousPaneKey, key == previousPaneKey {
|
||||
pane.node.frame = adjustedFrame
|
||||
let isAnimatingOut = pane.isAnimatingOut
|
||||
pane.isAnimatingOut = true
|
||||
transition.animateFrame(node: pane.node, from: paneFrame, to: paneFrame.offsetBy(dx: -paneSwitchAnimationOffset, dy: 0.0), completion: isAnimatingOut ? nil : { _ in
|
||||
paneCompletion()
|
||||
})
|
||||
} else if let previousPaneKey = previousPaneKey, key == self.currentPaneKey {
|
||||
pane.node.frame = adjustedFrame
|
||||
let isAnimatingOut = pane.isAnimatingOut
|
||||
pane.isAnimatingOut = true
|
||||
transition.animatePositionAdditive(node: pane.node, offset: CGPoint(x: paneSwitchAnimationOffset, y: 0.0), completion: isAnimatingOut ? nil : {
|
||||
paneCompletion()
|
||||
})
|
||||
} else {
|
||||
let isAnimatingOut = pane.isAnimatingOut
|
||||
pane.isAnimatingOut = true
|
||||
paneTransition.updateFrame(node: pane.node, frame: adjustedFrame, completion: isAnimatingOut ? nil : { _ in
|
||||
paneCompletion()
|
||||
})
|
||||
}
|
||||
pane.update(size: paneFrame.size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: paneWasAdded, transition: paneTransition)
|
||||
}
|
||||
}
|
||||
|
||||
for (_, pane) in self.pendingPanes {
|
||||
let paneTransition: ContainedViewLayoutTransition = .immediate
|
||||
paneTransition.updateFrame(node: pane.pane.node, frame: paneFrame)
|
||||
pane.pane.update(size: paneFrame.size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: true, transition: paneTransition)
|
||||
}
|
||||
if !self.didSetIsReady {
|
||||
if let currentPaneKey = self.currentPaneKey, let currentPane = self.currentPanes[currentPaneKey] {
|
||||
self.didSetIsReady = true
|
||||
self.isReady.set(currentPane.node.isReady)
|
||||
} else if self.pendingSwitchToPaneKey == nil {
|
||||
self.didSetIsReady = true
|
||||
self.isReady.set(.single(true))
|
||||
}
|
||||
}
|
||||
|
||||
self.currentPaneUpdated?(self.currentPaneKey, self.transitionFraction, transition)
|
||||
}
|
||||
|
||||
func allCurrentMessages() -> [MessageId: Message] {
|
||||
var allMessages: [MessageId: Message] = [:]
|
||||
for (_, pane) in self.currentPanes {
|
||||
if let messages = pane.node.searchCurrentMessages {
|
||||
for message in messages {
|
||||
allMessages[message.id] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
return allMessages
|
||||
}
|
||||
}
|
@ -20,7 +20,7 @@ import LocalizedPeerData
|
||||
import TextSelectionNode
|
||||
|
||||
private let deleteImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: .white)
|
||||
private let actionImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: .white)
|
||||
private let actionImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: .white)
|
||||
|
||||
private let backwardImage = generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/BackwardButton"), color: .white)
|
||||
private let forwardImage = generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/ForwardButton"), color: .white)
|
||||
@ -439,7 +439,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
||||
}
|
||||
}
|
||||
|
||||
func setMessage(_ message: Message) {
|
||||
func setMessage(_ message: Message, displayInfo: Bool = true) {
|
||||
self.currentMessage = message
|
||||
|
||||
let canDelete: Bool
|
||||
@ -464,14 +464,18 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
||||
}
|
||||
|
||||
var authorNameText: String?
|
||||
|
||||
if let author = message.effectiveAuthor {
|
||||
authorNameText = author.displayTitle(strings: self.strings, displayOrder: self.nameOrder)
|
||||
} else if let peer = message.peers[message.id.peerId] {
|
||||
authorNameText = peer.displayTitle(strings: self.strings, displayOrder: self.nameOrder)
|
||||
}
|
||||
|
||||
let dateText = humanReadableStringForTimestamp(strings: self.strings, dateTimeFormat: self.dateTimeFormat, timestamp: message.timestamp)
|
||||
var dateText = humanReadableStringForTimestamp(strings: self.strings, dateTimeFormat: self.dateTimeFormat, timestamp: message.timestamp)
|
||||
|
||||
if !displayInfo {
|
||||
authorNameText = ""
|
||||
dateText = ""
|
||||
}
|
||||
|
||||
var messageText = NSAttributedString(string: "")
|
||||
var hasCaption = false
|
||||
@ -495,7 +499,6 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
||||
|
||||
self.actionButton.isHidden = message.containsSecretMedia || Namespaces.Message.allScheduled.contains(message.id.namespace)
|
||||
|
||||
|
||||
if self.currentMessageText != messageText || canDelete != !self.deleteButton.isHidden || canShare != !self.actionButton.isHidden || self.currentAuthorNameText != authorNameText || self.currentDateText != dateText {
|
||||
self.currentMessageText = messageText
|
||||
|
||||
|
@ -143,12 +143,12 @@ private func galleryMessageCaptionText(_ message: Message) -> String {
|
||||
return message.text
|
||||
}
|
||||
|
||||
public func galleryItemForEntry(context: AccountContext, presentationData: PresentationData, entry: MessageHistoryEntry, isCentral: Bool = false, streamVideos: Bool, loopVideos: Bool = false, hideControls: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, timecode: Double? = nil, configuration: GalleryConfiguration? = nil, tempFilePath: String? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }, openActionOptions: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }, storeMediaPlaybackState: @escaping (MessageId, Double?) -> Void = { _, _ in }, present: @escaping (ViewController, Any?) -> Void) -> GalleryItem? {
|
||||
public func galleryItemForEntry(context: AccountContext, presentationData: PresentationData, entry: MessageHistoryEntry, isCentral: Bool = false, streamVideos: Bool, loopVideos: Bool = false, hideControls: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, timecode: Double? = nil, displayInfoOnTop: Bool = false, configuration: GalleryConfiguration? = nil, tempFilePath: String? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }, openActionOptions: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }, storeMediaPlaybackState: @escaping (MessageId, Double?) -> Void = { _, _ in }, present: @escaping (ViewController, Any?) -> Void) -> GalleryItem? {
|
||||
let message = entry.message
|
||||
let location = entry.location
|
||||
if let (media, mediaImage) = mediaForMessage(message: message) {
|
||||
if let _ = media as? TelegramMediaImage {
|
||||
return ChatImageGalleryItem(context: context, presentationData: presentationData, message: message, location: location, performAction: performAction, openActionOptions: openActionOptions, present: present)
|
||||
return ChatImageGalleryItem(context: context, presentationData: presentationData, message: message, location: location, displayInfoOnTop: displayInfoOnTop, performAction: performAction, openActionOptions: openActionOptions, present: present)
|
||||
} else if let file = media as? TelegramMediaFile {
|
||||
if file.isVideo {
|
||||
let content: UniversalVideoContent
|
||||
@ -176,7 +176,7 @@ public func galleryItemForEntry(context: AccountContext, presentationData: Prese
|
||||
}
|
||||
|
||||
let caption = galleryCaptionStringWithAppliedEntities(text, entities: entities)
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: GalleryItemOriginData(title: message.effectiveAuthor?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), timestamp: message.timestamp), indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: caption, hideControls: hideControls, fromPlayingVideo: fromPlayingVideo, landscape: landscape, timecode: timecode, configuration: configuration, playbackCompleted: playbackCompleted, performAction: performAction, openActionOptions: openActionOptions, storeMediaPlaybackState: storeMediaPlaybackState, present: present)
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: GalleryItemOriginData(title: message.effectiveAuthor?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), timestamp: message.timestamp), indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: caption, displayInfoOnTop: displayInfoOnTop, hideControls: hideControls, fromPlayingVideo: fromPlayingVideo, landscape: landscape, timecode: timecode, configuration: configuration, playbackCompleted: playbackCompleted, performAction: performAction, openActionOptions: openActionOptions, storeMediaPlaybackState: storeMediaPlaybackState, present: present)
|
||||
} else {
|
||||
if let fileName = file.fileName, (fileName as NSString).pathExtension.lowercased() == "json" {
|
||||
return ChatAnimationGalleryItem(context: context, presentationData: presentationData, message: message, location: location)
|
||||
@ -187,7 +187,7 @@ public func galleryItemForEntry(context: AccountContext, presentationData: Prese
|
||||
pixelsCount = Int(dimensions.width) * Int(dimensions.height)
|
||||
}
|
||||
if (file.size == nil || file.size! < 4 * 1024 * 1024) && pixelsCount < 4096 * 4096 {
|
||||
return ChatImageGalleryItem(context: context, presentationData: presentationData, message: message, location: location, performAction: performAction, openActionOptions: openActionOptions, present: present)
|
||||
return ChatImageGalleryItem(context: context, presentationData: presentationData, message: message, location: location, displayInfoOnTop: displayInfoOnTop, performAction: performAction, openActionOptions: openActionOptions, present: present)
|
||||
} else {
|
||||
return ChatDocumentGalleryItem(context: context, presentationData: presentationData, message: message, location: location)
|
||||
}
|
||||
@ -215,7 +215,7 @@ public func galleryItemForEntry(context: AccountContext, presentationData: Prese
|
||||
}
|
||||
}
|
||||
if let content = content {
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: GalleryItemOriginData(title: message.effectiveAuthor?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), timestamp: message.timestamp), indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: NSAttributedString(string: ""), fromPlayingVideo: fromPlayingVideo, landscape: landscape, timecode: timecode, configuration: configuration, performAction: performAction, openActionOptions: openActionOptions, storeMediaPlaybackState: storeMediaPlaybackState, present: present)
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: GalleryItemOriginData(title: message.effectiveAuthor?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), timestamp: message.timestamp), indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: NSAttributedString(string: ""), displayInfoOnTop: displayInfoOnTop, fromPlayingVideo: fromPlayingVideo, landscape: landscape, timecode: timecode, configuration: configuration, performAction: performAction, openActionOptions: openActionOptions, storeMediaPlaybackState: storeMediaPlaybackState, present: present)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
@ -453,7 +453,7 @@ public class GalleryController: ViewController, StandalonePresentableController
|
||||
var entries: [MessageHistoryEntry] = []
|
||||
var index = messages.count
|
||||
for message in messages.reversed() {
|
||||
entries.append(MessageHistoryEntry(message: message, isRead: false, location: MessageHistoryEntryLocation(index: Int(totalCount) - index, count: Int(totalCount)), monthLocation: nil, attributes: MutableMessageHistoryEntryAttributes(authorIsContact: false)))
|
||||
entries.append(MessageHistoryEntry(message: message, isRead: false, location: nil, monthLocation: nil, attributes: MutableMessageHistoryEntryAttributes(authorIsContact: false)))
|
||||
index -= 1
|
||||
}
|
||||
return GalleryMessageHistoryView.entries(entries, hasMore, false)
|
||||
@ -469,6 +469,11 @@ public class GalleryController: ViewController, StandalonePresentableController
|
||||
semaphore = nil
|
||||
}
|
||||
|
||||
var displayInfoOnTop = false
|
||||
if case .custom = source {
|
||||
displayInfoOnTop = true
|
||||
}
|
||||
|
||||
let syncResult = Atomic<(Bool, (() -> Void)?)>(value: (false, nil))
|
||||
self.disposable.set(combineLatest(messageView, self.context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])).start(next: { [weak self] view, preferencesView in
|
||||
let f: () -> Void = {
|
||||
@ -524,7 +529,7 @@ public class GalleryController: ViewController, StandalonePresentableController
|
||||
if entry.message.stableId == strongSelf.centralEntryStableId {
|
||||
isCentral = true
|
||||
}
|
||||
if let item = galleryItemForEntry(context: context, presentationData: strongSelf.presentationData, entry: entry, isCentral: isCentral, streamVideos: streamSingleVideo, fromPlayingVideo: isCentral && fromPlayingVideo, landscape: isCentral && landscape, timecode: isCentral ? timecode : nil, configuration: configuration, performAction: strongSelf.performAction, openActionOptions: strongSelf.openActionOptions, storeMediaPlaybackState: strongSelf.actionInteraction?.storeMediaPlaybackState ?? { _, _ in }, present: { [weak self] c, a in
|
||||
if let item = galleryItemForEntry(context: context, presentationData: strongSelf.presentationData, entry: entry, isCentral: isCentral, streamVideos: streamSingleVideo, fromPlayingVideo: isCentral && fromPlayingVideo, landscape: isCentral && landscape, timecode: isCentral ? timecode : nil, displayInfoOnTop: displayInfoOnTop, configuration: configuration, performAction: strongSelf.performAction, openActionOptions: strongSelf.openActionOptions, storeMediaPlaybackState: strongSelf.actionInteraction?.storeMediaPlaybackState ?? { _, _ in }, present: { [weak self] c, a in
|
||||
if let strongSelf = self {
|
||||
strongSelf.presentInGlobalOverlay(c, with: a)
|
||||
}
|
||||
@ -961,6 +966,11 @@ public class GalleryController: ViewController, StandalonePresentableController
|
||||
return baseNavigationController
|
||||
}
|
||||
|
||||
var displayInfoOnTop = false
|
||||
if case .custom = source {
|
||||
displayInfoOnTop = true
|
||||
}
|
||||
|
||||
var items: [GalleryItem] = []
|
||||
var centralItemIndex: Int?
|
||||
for entry in self.entries {
|
||||
@ -968,7 +978,7 @@ public class GalleryController: ViewController, StandalonePresentableController
|
||||
if entry.message.stableId == self.centralEntryStableId {
|
||||
isCentral = true
|
||||
}
|
||||
if let item = galleryItemForEntry(context: self.context, presentationData: self.presentationData, entry: entry, streamVideos: self.streamVideos, fromPlayingVideo: isCentral && self.fromPlayingVideo, landscape: isCentral && self.landscape, timecode: isCentral ? self.timecode : nil, configuration: self.configuration, performAction: self.performAction, openActionOptions: self.openActionOptions, storeMediaPlaybackState: self.actionInteraction?.storeMediaPlaybackState ?? { _, _ in }, present: { [weak self] c, a in
|
||||
if let item = galleryItemForEntry(context: self.context, presentationData: self.presentationData, entry: entry, streamVideos: self.streamVideos, fromPlayingVideo: isCentral && self.fromPlayingVideo, landscape: isCentral && self.landscape, timecode: isCentral ? self.timecode : nil, displayInfoOnTop: displayInfoOnTop, configuration: self.configuration, performAction: self.performAction, openActionOptions: self.openActionOptions, storeMediaPlaybackState: self.actionInteraction?.storeMediaPlaybackState ?? { _, _ in }, present: { [weak self] c, a in
|
||||
if let strongSelf = self {
|
||||
strongSelf.presentInGlobalOverlay(c, with: a)
|
||||
}
|
||||
@ -1048,7 +1058,7 @@ public class GalleryController: ViewController, StandalonePresentableController
|
||||
if entry.message.stableId == strongSelf.centralEntryStableId {
|
||||
isCentral = true
|
||||
}
|
||||
if let item = galleryItemForEntry(context: strongSelf.context, presentationData: strongSelf.presentationData, entry: entry, isCentral: isCentral, streamVideos: false, fromPlayingVideo: isCentral && strongSelf.fromPlayingVideo, landscape: isCentral && strongSelf.landscape, timecode: isCentral ? strongSelf.timecode : nil, configuration: strongSelf.configuration, performAction: strongSelf.performAction, openActionOptions: strongSelf.openActionOptions, storeMediaPlaybackState: strongSelf.actionInteraction?.storeMediaPlaybackState ?? { _, _ in }, present: { [weak self] c, a in
|
||||
if let item = galleryItemForEntry(context: strongSelf.context, presentationData: strongSelf.presentationData, entry: entry, isCentral: isCentral, streamVideos: false, fromPlayingVideo: isCentral && strongSelf.fromPlayingVideo, landscape: isCentral && strongSelf.landscape, timecode: isCentral ? strongSelf.timecode : nil, displayInfoOnTop: displayInfoOnTop, configuration: strongSelf.configuration, performAction: strongSelf.performAction, openActionOptions: strongSelf.openActionOptions, storeMediaPlaybackState: strongSelf.actionInteraction?.storeMediaPlaybackState ?? { _, _ in }, present: { [weak self] c, a in
|
||||
if let strongSelf = self {
|
||||
strongSelf.presentInGlobalOverlay(c, with: a)
|
||||
}
|
||||
@ -1078,7 +1088,7 @@ public class GalleryController: ViewController, StandalonePresentableController
|
||||
var entries: [MessageHistoryEntry] = []
|
||||
var index = messages.count
|
||||
for message in messages.reversed() {
|
||||
entries.append(MessageHistoryEntry(message: message, isRead: false, location: MessageHistoryEntryLocation(index: Int(totalCount) - index, count: Int(totalCount)), monthLocation: nil, attributes: MutableMessageHistoryEntryAttributes(authorIsContact: false)))
|
||||
entries.append(MessageHistoryEntry(message: message, isRead: false, location: nil, monthLocation: nil, attributes: MutableMessageHistoryEntryAttributes(authorIsContact: false)))
|
||||
index -= 1
|
||||
}
|
||||
|
||||
@ -1100,7 +1110,7 @@ public class GalleryController: ViewController, StandalonePresentableController
|
||||
if entry.message.stableId == strongSelf.centralEntryStableId {
|
||||
isCentral = true
|
||||
}
|
||||
if let item = galleryItemForEntry(context: strongSelf.context, presentationData: strongSelf.presentationData, entry: entry, isCentral: isCentral, streamVideos: false, fromPlayingVideo: isCentral && strongSelf.fromPlayingVideo, landscape: isCentral && strongSelf.landscape, timecode: isCentral ? strongSelf.timecode : nil, configuration: strongSelf.configuration, performAction: strongSelf.performAction, openActionOptions: strongSelf.openActionOptions, storeMediaPlaybackState: strongSelf.actionInteraction?.storeMediaPlaybackState ?? { _, _ in }, present: { [weak self] c, a in
|
||||
if let item = galleryItemForEntry(context: strongSelf.context, presentationData: strongSelf.presentationData, entry: entry, isCentral: isCentral, streamVideos: false, fromPlayingVideo: isCentral && strongSelf.fromPlayingVideo, landscape: isCentral && strongSelf.landscape, timecode: isCentral ? strongSelf.timecode : nil, displayInfoOnTop: displayInfoOnTop, configuration: strongSelf.configuration, performAction: strongSelf.performAction, openActionOptions: strongSelf.openActionOptions, storeMediaPlaybackState: strongSelf.actionInteraction?.storeMediaPlaybackState ?? { _, _ in }, present: { [weak self] c, a in
|
||||
if let strongSelf = self {
|
||||
strongSelf.presentInGlobalOverlay(c, with: a)
|
||||
}
|
||||
|
62
submodules/GalleryUI/Sources/GalleryTitleView.swift
Normal file
62
submodules/GalleryUI/Sources/GalleryTitleView.swift
Normal file
@ -0,0 +1,62 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import Postbox
|
||||
import TelegramPresentationData
|
||||
import TelegramStringFormatting
|
||||
|
||||
private let titleFont = Font.medium(15.0)
|
||||
private let dateFont = Font.regular(14.0)
|
||||
|
||||
final class GalleryTitleView: UIView, NavigationBarTitleView {
|
||||
private let authorNameNode: ASTextNode
|
||||
private let dateNode: ASTextNode
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.authorNameNode = ASTextNode()
|
||||
self.authorNameNode.displaysAsynchronously = false
|
||||
self.authorNameNode.maximumNumberOfLines = 1
|
||||
|
||||
self.dateNode = ASTextNode()
|
||||
self.dateNode.displaysAsynchronously = false
|
||||
self.dateNode.maximumNumberOfLines = 1
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubnode(self.authorNameNode)
|
||||
self.addSubnode(self.dateNode)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func setMessage(_ message: Message, presentationData: PresentationData, accountPeerId: PeerId) {
|
||||
let authorNameText = stringForFullAuthorName(message: message, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, accountPeerId: accountPeerId)
|
||||
let dateText = humanReadableStringForTimestamp(strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, timestamp: message.timestamp)
|
||||
|
||||
self.authorNameNode.attributedText = NSAttributedString(string: authorNameText, font: titleFont, textColor: .white)
|
||||
self.dateNode.attributedText = NSAttributedString(string: dateText, font: dateFont, textColor: .white)
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition) {
|
||||
let leftInset: CGFloat = 0.0
|
||||
let rightInset: CGFloat = 0.0
|
||||
|
||||
let authorNameSize = self.authorNameNode.measure(CGSize(width: size.width - 44.0 * 2.0 - 8.0 * 2.0 - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude))
|
||||
let dateSize = self.dateNode.measure(CGSize(width: size.width - 44.0 * 2.0 - 8.0 * 2.0, height: CGFloat.greatestFiniteMagnitude))
|
||||
|
||||
if authorNameSize.height.isZero {
|
||||
self.dateNode.frame = CGRect(origin: CGPoint(x: floor((size.width - dateSize.width) / 2.0), y: floor((size.height - dateSize.height) / 2.0)), size: dateSize)
|
||||
} else {
|
||||
let labelsSpacing: CGFloat = 0.0
|
||||
self.authorNameNode.frame = CGRect(origin: CGPoint(x: floor((size.width - authorNameSize.width) / 2.0), y: floor((size.height - dateSize.height - authorNameSize.height - labelsSpacing) / 2.0)), size: authorNameSize)
|
||||
self.dateNode.frame = CGRect(origin: CGPoint(x: floor((size.width - dateSize.width) / 2.0), y: floor((size.height - dateSize.height - authorNameSize.height - labelsSpacing) / 2.0) + authorNameSize.height + labelsSpacing), size: dateSize)
|
||||
}
|
||||
}
|
||||
|
||||
func animateLayoutTransition() {
|
||||
|
||||
}
|
||||
}
|
@ -87,15 +87,17 @@ class ChatImageGalleryItem: GalleryItem {
|
||||
let presentationData: PresentationData
|
||||
let message: Message
|
||||
let location: MessageHistoryEntryLocation?
|
||||
let displayInfoOnTop: Bool
|
||||
let performAction: (GalleryControllerInteractionTapAction) -> Void
|
||||
let openActionOptions: (GalleryControllerInteractionTapAction) -> Void
|
||||
let present: (ViewController, Any?) -> Void
|
||||
|
||||
init(context: AccountContext, presentationData: PresentationData, message: Message, location: MessageHistoryEntryLocation?, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void, openActionOptions: @escaping (GalleryControllerInteractionTapAction) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
init(context: AccountContext, presentationData: PresentationData, message: Message, location: MessageHistoryEntryLocation?, displayInfoOnTop: Bool, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void, openActionOptions: @escaping (GalleryControllerInteractionTapAction) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
self.context = context
|
||||
self.presentationData = presentationData
|
||||
self.message = message
|
||||
self.location = location
|
||||
self.displayInfoOnTop = displayInfoOnTop
|
||||
self.performAction = performAction
|
||||
self.openActionOptions = openActionOptions
|
||||
self.present = present
|
||||
@ -126,7 +128,10 @@ class ChatImageGalleryItem: GalleryItem {
|
||||
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").0))
|
||||
}
|
||||
|
||||
node.setMessage(self.message)
|
||||
if self.displayInfoOnTop {
|
||||
node.titleContentView?.setMessage(self.message, presentationData: self.presentationData, accountPeerId: self.context.account.peerId)
|
||||
}
|
||||
node.setMessage(self.message, displayInfo: !self.displayInfoOnTop)
|
||||
|
||||
return node
|
||||
}
|
||||
@ -135,7 +140,10 @@ class ChatImageGalleryItem: GalleryItem {
|
||||
if let node = node as? ChatImageGalleryItemNode, let location = self.location {
|
||||
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").0))
|
||||
|
||||
node.setMessage(self.message)
|
||||
if self.displayInfoOnTop {
|
||||
node.titleContentView?.setMessage(self.message, presentationData: self.presentationData, accountPeerId: self.context.account.peerId)
|
||||
}
|
||||
node.setMessage(self.message, displayInfo: !self.displayInfoOnTop)
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,10 +175,12 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
private var tilingNode: TilingNode?
|
||||
fileprivate let _ready = Promise<Void>()
|
||||
fileprivate let _title = Promise<String>()
|
||||
fileprivate let _titleView = Promise<UIView?>()
|
||||
fileprivate let _rightBarButtonItems = Promise<[UIBarButtonItem]?>(nil)
|
||||
private let statusNodeContainer: HighlightableButtonNode
|
||||
private let statusNode: RadialStatusNode
|
||||
private let footerContentNode: ChatItemGalleryFooterContentNode
|
||||
fileprivate var titleContentView: GalleryTitleView?
|
||||
|
||||
private var contextAndMedia: (AccountContext, AnyMediaReference)?
|
||||
|
||||
@ -207,6 +217,9 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
self.statusNodeContainer.addTarget(self, action: #selector(self.statusPressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.statusNodeContainer.isUserInteractionEnabled = false
|
||||
|
||||
self.titleContentView = GalleryTitleView(frame: CGRect())
|
||||
self._titleView.set(.single(self.titleContentView))
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -227,8 +240,8 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
transition.updateFrame(node: self.statusNode, frame: CGRect(origin: CGPoint(), size: statusSize))
|
||||
}
|
||||
|
||||
fileprivate func setMessage(_ message: Message) {
|
||||
self.footerContentNode.setMessage(message)
|
||||
fileprivate func setMessage(_ message: Message, displayInfo: Bool) {
|
||||
self.footerContentNode.setMessage(message, displayInfo: displayInfo)
|
||||
}
|
||||
|
||||
fileprivate func setImage(imageReference: ImageMediaReference) {
|
||||
@ -579,6 +592,10 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
return self._title.get()
|
||||
}
|
||||
|
||||
override func titleView() -> Signal<UIView?, NoError> {
|
||||
return self._titleView.get()
|
||||
}
|
||||
|
||||
override func rightBarButtonItems() -> Signal<[UIBarButtonItem]?, NoError> {
|
||||
return self._rightBarButtonItems.get()
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ public class UniversalVideoGalleryItem: GalleryItem {
|
||||
let contentInfo: UniversalVideoGalleryItemContentInfo?
|
||||
let caption: NSAttributedString
|
||||
let credit: NSAttributedString?
|
||||
let displayInfoOnTop: Bool
|
||||
let hideControls: Bool
|
||||
let fromPlayingVideo: Bool
|
||||
let landscape: Bool
|
||||
@ -45,7 +46,7 @@ public class UniversalVideoGalleryItem: GalleryItem {
|
||||
let storeMediaPlaybackState: (MessageId, Double?) -> Void
|
||||
let present: (ViewController, Any?) -> Void
|
||||
|
||||
public init(context: AccountContext, presentationData: PresentationData, content: UniversalVideoContent, originData: GalleryItemOriginData?, indexData: GalleryItemIndexData?, contentInfo: UniversalVideoGalleryItemContentInfo?, caption: NSAttributedString, credit: NSAttributedString? = nil, hideControls: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, timecode: Double? = nil, configuration: GalleryConfiguration? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void, openActionOptions: @escaping (GalleryControllerInteractionTapAction) -> Void, storeMediaPlaybackState: @escaping (MessageId, Double?) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
public init(context: AccountContext, presentationData: PresentationData, content: UniversalVideoContent, originData: GalleryItemOriginData?, indexData: GalleryItemIndexData?, contentInfo: UniversalVideoGalleryItemContentInfo?, caption: NSAttributedString, credit: NSAttributedString? = nil, displayInfoOnTop: Bool = false, hideControls: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, timecode: Double? = nil, configuration: GalleryConfiguration? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void, openActionOptions: @escaping (GalleryControllerInteractionTapAction) -> Void, storeMediaPlaybackState: @escaping (MessageId, Double?) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
self.context = context
|
||||
self.presentationData = presentationData
|
||||
self.content = content
|
||||
@ -54,6 +55,7 @@ public class UniversalVideoGalleryItem: GalleryItem {
|
||||
self.contentInfo = contentInfo
|
||||
self.caption = caption
|
||||
self.credit = credit
|
||||
self.displayInfoOnTop = displayInfoOnTop
|
||||
self.hideControls = hideControls
|
||||
self.fromPlayingVideo = fromPlayingVideo
|
||||
self.landscape = landscape
|
||||
@ -75,6 +77,10 @@ public class UniversalVideoGalleryItem: GalleryItem {
|
||||
|
||||
node.setupItem(self)
|
||||
|
||||
if self.displayInfoOnTop, case let .message(message) = self.contentInfo {
|
||||
node.titleContentView?.setMessage(message, presentationData: self.presentationData, accountPeerId: self.context.account.peerId)
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
@ -85,6 +91,10 @@ public class UniversalVideoGalleryItem: GalleryItem {
|
||||
}
|
||||
|
||||
node.setupItem(self)
|
||||
|
||||
if self.displayInfoOnTop, case let .message(message) = self.contentInfo {
|
||||
node.titleContentView?.setMessage(message, presentationData: self.presentationData, accountPeerId: self.context.account.peerId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -249,6 +259,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
fileprivate let _titleView = Promise<UIView?>()
|
||||
fileprivate let _rightBarButtonItems = Promise<[UIBarButtonItem]?>()
|
||||
|
||||
fileprivate var titleContentView: GalleryTitleView?
|
||||
private let scrubberView: ChatVideoGalleryItemScrubberView
|
||||
private let footerContentNode: ChatItemGalleryFooterContentNode
|
||||
private let overlayContentNode: UniversalVideoGalleryItemOverlayNode
|
||||
@ -309,7 +320,6 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
self.statusNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 50.0, height: 50.0))
|
||||
|
||||
self._title.set(.single(""))
|
||||
self._titleView.set(.single(nil))
|
||||
|
||||
super.init()
|
||||
|
||||
@ -412,6 +422,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
strongSelf.pictureInPictureButtonPressed()
|
||||
return true
|
||||
}
|
||||
|
||||
self.titleContentView = GalleryTitleView(frame: CGRect())
|
||||
self._titleView.set(.single(self.titleContentView))
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -756,7 +769,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
if let contentInfo = item.contentInfo {
|
||||
switch contentInfo {
|
||||
case let .message(message):
|
||||
self.footerContentNode.setMessage(message)
|
||||
self.footerContentNode.setMessage(message, displayInfo: !item.displayInfoOnTop)
|
||||
case let .webPage(webPage, media, _):
|
||||
self.footerContentNode.setWebPage(webPage, media: media)
|
||||
}
|
||||
|
@ -88,18 +88,15 @@ public final class HashtagSearchController: TelegramBaseController {
|
||||
}, openMessageContextMenu: { message, bool, node, rect, gesture in
|
||||
|
||||
}, toggleMessagesSelection: { messageId, selected in
|
||||
|
||||
}, openUrl: { url, _, _, message in
|
||||
}, openInstantPage: { message, data in
|
||||
|
||||
}, longTap: { action, message in
|
||||
|
||||
}, getHiddenMedia: {
|
||||
return [:]
|
||||
})
|
||||
|
||||
let firstTime = previousEntries == nil
|
||||
let transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: entries, displayingResults: true, isEmpty: entries.isEmpty, isLoading: false, animated: false, searchQuery: "", context: strongSelf.context, presentationData: strongSelf.presentationData, enableHeaders: false, filter: [], interaction: interaction, listInteraction: listInteraction, peerContextAction: nil, toggleExpandLocalResults: {
|
||||
let transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: entries, displayingResults: true, isEmpty: entries.isEmpty, isLoading: false, animated: false, searchQuery: "", context: strongSelf.context, presentationData: strongSelf.presentationData, enableHeaders: false, filter: [], tagMask: nil, interaction: interaction, listInteraction: listInteraction, peerContextAction: nil, toggleExpandLocalResults: {
|
||||
}, toggleExpandGlobalResults: {
|
||||
}, searchPeer: { _ in
|
||||
|
||||
|
@ -14,7 +14,7 @@ import ShareController
|
||||
import GalleryUI
|
||||
import AppBundle
|
||||
|
||||
private let actionImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: .white)
|
||||
private let actionImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: .white)
|
||||
|
||||
private let textFont = Font.regular(16.0)
|
||||
|
||||
|
@ -1120,7 +1120,7 @@
|
||||
[_playerItemDisposable setDisposable:[[itemSignal deliverOn:[SQueue mainQueue]] startWithNext:^(AVPlayerItem *playerItem)
|
||||
{
|
||||
__strong TGMediaPickerGalleryVideoItemView *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
if (strongSelf == nil || ![playerItem isKindOfClass:[AVPlayerItem class]])
|
||||
return;
|
||||
|
||||
strongSelf->_player = [AVPlayer playerWithPlayerItem:playerItem];
|
||||
|
@ -87,6 +87,7 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
||||
_textView.autocorrectionType = UITextAutocorrectionTypeNo;
|
||||
_textView.spellCheckingType = UITextSpellCheckingTypeNo;
|
||||
_textView.font = [UIFont boldSystemFontOfSize:_baseFontSize];
|
||||
_textView.typingAttributes = @{NSFontAttributeName: _textView.font};
|
||||
// _textView.frameWidthInset = floor(_baseFontSize * 0.03);
|
||||
|
||||
[self setSwatch:entity.swatch];
|
||||
@ -175,6 +176,7 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
||||
{
|
||||
_font = font;
|
||||
_textView.font = [UIFont boldSystemFontOfSize:_baseFontSize];
|
||||
_textView.typingAttributes = @{NSFontAttributeName: _textView.font};
|
||||
// _textView.frameWidthInset = floor(_baseFontSize * 0.03);
|
||||
|
||||
[self sizeToFit];
|
||||
@ -480,6 +482,9 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
||||
|
||||
|
||||
@implementation TGPhotoTextView
|
||||
{
|
||||
UIFont *_font;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
@ -554,10 +559,29 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
||||
|
||||
- (void)setFont:(UIFont *)font {
|
||||
[super setFont:font];
|
||||
_font = font;
|
||||
|
||||
self.layoutManager.textContainers.firstObject.lineFragmentPadding = floor(font.pointSize * 0.3);
|
||||
}
|
||||
|
||||
- (void)insertText:(NSString *)text {
|
||||
[self fixTypingAttributes];
|
||||
[super insertText:text];
|
||||
[self fixTypingAttributes];
|
||||
}
|
||||
|
||||
- (void)paste:(id)sender {
|
||||
[self fixTypingAttributes];
|
||||
[super paste:sender];
|
||||
[self fixTypingAttributes];
|
||||
}
|
||||
|
||||
- (void)fixTypingAttributes {
|
||||
if (_font != nil) {
|
||||
self.typingAttributes = @{NSFontAttributeName: _font};
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -802,8 +826,6 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
||||
return [_impl attributesAtIndex:location effectiveRange:range];
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str {
|
||||
[self beginEditing];
|
||||
[_impl replaceCharactersInRange:range withString:str];
|
||||
|
@ -89,36 +89,6 @@ private func extensionImage(fileExtension: String?) -> UIImage? {
|
||||
}
|
||||
private let extensionFont = Font.with(size: 15.0, design: .round, traits: [.bold])
|
||||
|
||||
func fullAuthorString(for item: ListMessageItem) -> String {
|
||||
var authorString = ""
|
||||
if let author = item.message.author, [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(item.message.id.peerId.namespace) {
|
||||
var authorName = ""
|
||||
if author.id == item.context.account.peerId {
|
||||
authorName = item.presentationData.strings.DialogList_You
|
||||
} else {
|
||||
authorName = author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
|
||||
}
|
||||
if let peer = item.message.peers[item.message.id.peerId], author.id != peer.id {
|
||||
authorString = "\(authorName) → \(peer.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder))"
|
||||
} else {
|
||||
authorString = authorName
|
||||
}
|
||||
} else if let peer = item.message.peers[item.message.id.peerId] {
|
||||
if item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
authorString = peer.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
|
||||
} else {
|
||||
if item.message.id.peerId == item.context.account.peerId {
|
||||
authorString = item.presentationData.strings.DialogList_SavedMessages
|
||||
} else if item.message.flags.contains(.Incoming) {
|
||||
authorString = peer.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
|
||||
} else {
|
||||
authorString = "\(item.presentationData.strings.DialogList_You) → \(peer.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder))"
|
||||
}
|
||||
}
|
||||
}
|
||||
return authorString
|
||||
}
|
||||
|
||||
private struct FetchControls {
|
||||
let fetch: () -> Void
|
||||
let cancel: () -> Void
|
||||
@ -418,7 +388,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
||||
}
|
||||
|
||||
if item.isGlobalSearchResult {
|
||||
let authorString = fullAuthorString(for: item)
|
||||
let authorString = stringForFullAuthorName(message: item.message, strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, accountPeerId: item.context.account.peerId)
|
||||
if descriptionString.isEmpty {
|
||||
descriptionString = authorString
|
||||
} else {
|
||||
@ -458,7 +428,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
||||
}
|
||||
|
||||
if item.isGlobalSearchResult {
|
||||
authorName = fullAuthorString(for: item)
|
||||
authorName = stringForFullAuthorName(message: item.message, strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, accountPeerId: item.context.account.peerId)
|
||||
}
|
||||
|
||||
titleText = NSAttributedString(string: authorName, font: audioTitleFont, textColor: item.presentationData.theme.theme.list.itemPrimaryTextColor)
|
||||
@ -511,7 +481,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
||||
}
|
||||
|
||||
if item.isGlobalSearchResult {
|
||||
let authorString = fullAuthorString(for: item)
|
||||
let authorString = stringForFullAuthorName(message: item.message, strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, accountPeerId: item.context.account.peerId)
|
||||
if descriptionString.isEmpty {
|
||||
descriptionString = authorString
|
||||
} else {
|
||||
|
@ -11,9 +11,9 @@ import AccountContext
|
||||
import TelegramUIPreferences
|
||||
|
||||
public final class ListMessageItemInteraction {
|
||||
let openMessage: (Message, ChatControllerInteractionOpenMessageMode) -> Bool
|
||||
let openMessageContextMenu: (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void
|
||||
let toggleMessagesSelection: ([MessageId], Bool) -> Void
|
||||
public let openMessage: (Message, ChatControllerInteractionOpenMessageMode) -> Bool
|
||||
public let openMessageContextMenu: (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void
|
||||
public let toggleMessagesSelection: ([MessageId], Bool) -> Void
|
||||
let openUrl: (String, Bool, Bool?, Message?) -> Void
|
||||
let openInstantPage: (Message, ChatMessageItemAssociatedData?) -> Void
|
||||
let longTap: (ChatControllerInteractionLongTapAction, Message?) -> Void
|
||||
|
@ -479,7 +479,7 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
|
||||
|
||||
var authorString = ""
|
||||
if item.isGlobalSearchResult {
|
||||
authorString = fullAuthorString(for: item)
|
||||
authorString = stringForFullAuthorName(message: item.message, strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, accountPeerId: item.context.account.peerId)
|
||||
}
|
||||
|
||||
let authorText = NSAttributedString(string: authorString, font: authorFont, textColor: item.presentationData.theme.theme.list.itemSecondaryTextColor)
|
||||
|
@ -48,7 +48,7 @@ public enum LocationActionListItemIcon: Equatable {
|
||||
}
|
||||
|
||||
private func generateLocationIcon(theme: PresentationTheme) -> UIImage {
|
||||
return generateImage(CGSize(width: 40.0, height: 40.0), contextGenerator: { size, context in
|
||||
return generateImage(CGSize(width: 40.0, height: 40.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(theme.chat.inputPanel.actionControlFillColor.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
|
||||
@ -64,7 +64,7 @@ private func generateLocationIcon(theme: PresentationTheme) -> UIImage {
|
||||
}
|
||||
|
||||
private func generateLiveLocationIcon(theme: PresentationTheme) -> UIImage {
|
||||
return generateImage(CGSize(width: 40.0, height: 40.0), contextGenerator: { size, context in
|
||||
return generateImage(CGSize(width: 40.0, height: 40.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(UIColor(rgb: 0x6cc139).cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
|
||||
|
@ -10,7 +10,7 @@ private let panelSize = CGSize(width: 46.0, height: 90.0)
|
||||
|
||||
private func generateBackgroundImage(theme: PresentationTheme) -> UIImage? {
|
||||
let cornerRadius: CGFloat = 9.0
|
||||
return generateImage(CGSize(width: (cornerRadius + panelInset) * 2.0, height: (cornerRadius + panelInset) * 2.0), contextGenerator: { size, context in
|
||||
return generateImage(CGSize(width: (cornerRadius + panelInset) * 2.0, height: (cornerRadius + panelInset) * 2.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.setShadow(offset: CGSize(), blur: 10.0, color: UIColor(rgb: 0x000000, alpha: 0.2).cgColor)
|
||||
@ -22,7 +22,7 @@ private func generateBackgroundImage(theme: PresentationTheme) -> UIImage? {
|
||||
}
|
||||
|
||||
private func generateShadowImage(theme: PresentationTheme, highlighted: Bool) -> UIImage? {
|
||||
return generateImage(CGSize(width: 26.0, height: 14.0), contextGenerator: { size, context in
|
||||
return generateImage(CGSize(width: 26.0, height: 14.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.setShadow(offset: CGSize(), blur: 10.0, color: UIColor(rgb: 0x000000, alpha: 0.2).cgColor)
|
||||
|
@ -15,7 +15,7 @@ import GalleryUI
|
||||
import AppBundle
|
||||
|
||||
private let deleteImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: .white)
|
||||
private let actionImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: .white)
|
||||
private let actionImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: .white)
|
||||
|
||||
private let nameFont = Font.medium(15.0)
|
||||
private let dateFont = Font.regular(14.0)
|
||||
|
@ -134,7 +134,7 @@ public final class SearchDisplayController {
|
||||
insertSubnode(self.backgroundNode, false)
|
||||
self.backgroundNode.addSubnode(self.contentNode)
|
||||
|
||||
self.contentNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
self.contentNode.frame = CGRect(origin: CGPoint(x: 20.0, y: 20.0), size: layout.size)
|
||||
|
||||
self.contentNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: layout.safeInsets, statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), navigationBarHeight: navigationBarHeight, transition: .immediate)
|
||||
|
||||
@ -149,7 +149,7 @@ public final class SearchDisplayController {
|
||||
let contentNodePosition = self.contentNode.layer.position
|
||||
|
||||
// self.contentNode.layer.animatePosition(from: CGPoint(x: contentNodePosition.x, y: contentNodePosition.y + (initialTextBackgroundFrame.maxY + 8.0 - contentNavigationBarHeight)), to: contentNodePosition, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
self.backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
|
||||
self.backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
|
||||
self.backgroundNode.layer.animateScale(from: 0.85, to: 1.0, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
self.searchBar.placeholderString = placeholder.placeholderString
|
||||
}
|
||||
|
@ -38,8 +38,8 @@ final class ThemeGridSelectionPanelNode: ASDisplayNode {
|
||||
|
||||
self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
|
||||
self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled])
|
||||
self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
|
||||
self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled])
|
||||
self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
|
||||
self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled])
|
||||
|
||||
super.init()
|
||||
|
||||
@ -56,8 +56,8 @@ final class ThemeGridSelectionPanelNode: ASDisplayNode {
|
||||
|
||||
self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
|
||||
self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled])
|
||||
self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
|
||||
self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled])
|
||||
self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
|
||||
self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,7 +139,7 @@ public final class ThemePreviewController: ViewController {
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
|
||||
if !isPreview {
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: self.previewTheme.rootController.navigationBar.accentTextColor), style: .plain, target: self, action: #selector(self.actionPressed))
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: self.previewTheme.rootController.navigationBar.accentTextColor), style: .plain, target: self, action: #selector(self.actionPressed))
|
||||
}
|
||||
|
||||
self.disposable = (combineLatest(self.theme.get(), self.presentationTheme.get())
|
||||
|
@ -227,7 +227,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
let contentSize: CGSize
|
||||
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let defaultAction = UIBarButtonItem(image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: presentationData.theme.rootController.navigationBar.accentTextColor), style: .plain, target: self, action: #selector(self.actionPressed))
|
||||
let defaultAction = UIBarButtonItem(image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: presentationData.theme.rootController.navigationBar.accentTextColor), style: .plain, target: self, action: #selector(self.actionPressed))
|
||||
let progressAction = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: presentationData.theme.rootController.navigationBar.accentTextColor))
|
||||
|
||||
var isBlurrable = true
|
||||
|
@ -17,7 +17,7 @@ final class ShareControllerRecentPeersGridItem: GridItem {
|
||||
let controllerInteraction: ShareControllerInteraction
|
||||
|
||||
let section: GridSection? = nil
|
||||
let fillsRowWithHeight: (CGFloat, Bool)? = (130.0, true)
|
||||
let fillsRowWithHeight: (CGFloat, Bool)? = (102.0, true)
|
||||
|
||||
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, controllerInteraction: ShareControllerInteraction) {
|
||||
self.context = context
|
||||
|
@ -238,7 +238,7 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q
|
||||
} else {
|
||||
let lowerBound = state?.main.messages.last.flatMap({ $0.index })
|
||||
let signal: Signal<Api.messages.Messages, MTRpcError>
|
||||
if peer.id.namespace == Namespaces.Peer.CloudChannel && tags == nil && minDate == nil && maxDate == nil {
|
||||
if peer.id.namespace == Namespaces.Peer.CloudChannel && query.isEmpty && tags == nil && minDate == nil && maxDate == nil {
|
||||
signal = account.network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: lowerBound?.id.id ?? 0, offsetDate: 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0))
|
||||
} else {
|
||||
signal = account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: query, fromId: fromInputUser, topMsgId: topMsgId?.id, filter: filter, minDate: minDate ?? 0, maxDate: maxDate ?? (Int32.max - 1), offsetId: lowerBound?.id.id ?? 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0))
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -44,7 +44,10 @@ public struct PresentationResourcesRootController {
|
||||
}
|
||||
|
||||
public static func navigationShareIcon(_ theme: PresentationTheme) -> UIImage? {
|
||||
return theme.image(PresentationResourceKey.navigationShareIcon.rawValue, generateShareButtonImage)
|
||||
return theme.image(PresentationResourceKey.navigationShareIcon.rawValue, { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: theme.rootController.navigationBar.accentTextColor)
|
||||
})
|
||||
// return theme.image(PresentationResourceKey.navigationShareIcon.rawValue, generateShareButtonImage)
|
||||
}
|
||||
|
||||
public static func navigationCallIcon(_ theme: PresentationTheme) -> UIImage? {
|
||||
|
@ -0,0 +1,35 @@
|
||||
import Foundation
|
||||
import Postbox
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import SyncCore
|
||||
|
||||
public func stringForFullAuthorName(message: Message, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, accountPeerId: PeerId) -> String {
|
||||
var authorString = ""
|
||||
if let author = message.author, [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(message.id.peerId.namespace) {
|
||||
var authorName = ""
|
||||
if author.id == accountPeerId {
|
||||
authorName = strings.DialogList_You
|
||||
} else {
|
||||
authorName = author.compactDisplayTitle
|
||||
}
|
||||
if let peer = message.peers[message.id.peerId], author.id != peer.id {
|
||||
authorString = "\(authorName) → \(peer.displayTitle(strings: strings, displayOrder: nameDisplayOrder))"
|
||||
} else {
|
||||
authorString = authorName
|
||||
}
|
||||
} else if let peer = message.peers[message.id.peerId] {
|
||||
if message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
authorString = peer.displayTitle(strings: strings, displayOrder: nameDisplayOrder)
|
||||
} else {
|
||||
if message.id.peerId == accountPeerId {
|
||||
authorString = strings.DialogList_SavedMessages
|
||||
} else if message.flags.contains(.Incoming) {
|
||||
authorString = peer.displayTitle(strings: strings, displayOrder: nameDisplayOrder)
|
||||
} else {
|
||||
authorString = "\(strings.DialogList_You) → \(peer.displayTitle(strings: strings, displayOrder: nameDisplayOrder))"
|
||||
}
|
||||
}
|
||||
}
|
||||
return authorString
|
||||
}
|
Binary file not shown.
@ -318,7 +318,7 @@ final class PeerInfoPaneTabsContainerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
var selectedFrame: CGRect?
|
||||
if let selectedPane = selectedPane, let currentIndex = paneList.index(where: { $0.key == selectedPane }) {
|
||||
if let selectedPane = selectedPane, let currentIndex = paneList.firstIndex(where: { $0.key == selectedPane }) {
|
||||
if currentIndex != 0 && transitionFraction > 0.0 {
|
||||
let currentFrame = selectionFrames[currentIndex]
|
||||
let previousFrame = selectionFrames[currentIndex - 1]
|
||||
@ -425,7 +425,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
||||
private let coveringBackgroundNode: ASDisplayNode
|
||||
private let separatorNode: ASDisplayNode
|
||||
private let tabsContainerNode: PeerInfoPaneTabsContainerNode
|
||||
private let tapsSeparatorNode: ASDisplayNode
|
||||
private let tabsSeparatorNode: ASDisplayNode
|
||||
|
||||
let isReady = Promise<Bool>()
|
||||
var didSetIsReady = false
|
||||
@ -471,15 +471,15 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
||||
|
||||
self.tabsContainerNode = PeerInfoPaneTabsContainerNode()
|
||||
|
||||
self.tapsSeparatorNode = ASDisplayNode()
|
||||
self.tapsSeparatorNode.isLayerBacked = true
|
||||
self.tabsSeparatorNode = ASDisplayNode()
|
||||
self.tabsSeparatorNode.isLayerBacked = true
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.separatorNode)
|
||||
self.addSubnode(self.coveringBackgroundNode)
|
||||
self.addSubnode(self.tabsContainerNode)
|
||||
self.addSubnode(self.tapsSeparatorNode)
|
||||
self.addSubnode(self.tabsSeparatorNode)
|
||||
|
||||
self.tabsContainerNode.requestSelectPane = { [weak self] key in
|
||||
guard let strongSelf = self else {
|
||||
@ -488,7 +488,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
||||
if strongSelf.currentPaneKey == key {
|
||||
if let requestExpandTabs = strongSelf.requestExpandTabs, requestExpandTabs() {
|
||||
} else {
|
||||
strongSelf.currentPane?.node.scrollToTop()
|
||||
let _ = strongSelf.currentPane?.node.scrollToTop()
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -563,7 +563,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
||||
|
||||
cancelContextGestures(view: self.view)
|
||||
case .changed:
|
||||
if let (size, sideInset, bottomInset, visibleHeight, expansionFraction, presentationData, data) = self.currentParams, let availablePanes = data?.availablePanes, availablePanes.count > 1, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.index(of: currentPaneKey) {
|
||||
if let (size, sideInset, bottomInset, visibleHeight, expansionFraction, presentationData, data) = self.currentParams, let availablePanes = data?.availablePanes, availablePanes.count > 1, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) {
|
||||
let translation = recognizer.translation(in: self.view)
|
||||
var transitionFraction = translation.x / size.width
|
||||
if currentIndex <= 0 {
|
||||
@ -576,7 +576,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
||||
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .immediate)
|
||||
}
|
||||
case .cancelled, .ended:
|
||||
if let (size, sideInset, bottomInset, visibleHeight, expansionFraction, presentationData, data) = self.currentParams, let availablePanes = data?.availablePanes, availablePanes.count > 1, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.index(of: currentPaneKey) {
|
||||
if let (size, sideInset, bottomInset, visibleHeight, expansionFraction, presentationData, data) = self.currentParams, let availablePanes = data?.availablePanes, availablePanes.count > 1, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) {
|
||||
let translation = recognizer.translation(in: self.view)
|
||||
let velocity = recognizer.velocity(in: self.view)
|
||||
var directionIsToRight: Bool?
|
||||
@ -648,7 +648,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
||||
|
||||
if let currentPaneKey = self.currentPaneKey, !availablePanes.contains(currentPaneKey) {
|
||||
var nextCandidatePaneKey: PeerInfoPaneKey?
|
||||
if let index = previousAvailablePanes.index(of: currentPaneKey), index != 0 {
|
||||
if let index = previousAvailablePanes.firstIndex(of: currentPaneKey), index != 0 {
|
||||
for i in (0 ... index - 1).reversed() {
|
||||
if availablePanes.contains(previousAvailablePanes[i]) {
|
||||
nextCandidatePaneKey = previousAvailablePanes[i]
|
||||
@ -671,7 +671,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
||||
|
||||
let currentIndex: Int?
|
||||
if let currentPaneKey = self.currentPaneKey {
|
||||
currentIndex = availablePanes.index(of: currentPaneKey)
|
||||
currentIndex = availablePanes.firstIndex(of: currentPaneKey)
|
||||
} else {
|
||||
currentIndex = nil
|
||||
}
|
||||
@ -683,14 +683,14 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
||||
self.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor
|
||||
self.coveringBackgroundNode.backgroundColor = presentationData.theme.rootController.navigationBar.backgroundColor
|
||||
self.separatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
self.tapsSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
self.tabsSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
|
||||
let tabsHeight: CGFloat = 48.0
|
||||
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: size.width, height: UIScreenPixel)))
|
||||
transition.updateFrame(node: self.coveringBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: size.width, height: tabsHeight + UIScreenPixel)))
|
||||
|
||||
transition.updateFrame(node: self.tapsSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: tabsHeight - UIScreenPixel), size: CGSize(width: size.width, height: UIScreenPixel)))
|
||||
transition.updateFrame(node: self.tabsSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: tabsHeight - UIScreenPixel), size: CGSize(width: size.width, height: UIScreenPixel)))
|
||||
|
||||
let paneFrame = CGRect(origin: CGPoint(x: 0.0, y: tabsHeight), size: CGSize(width: size.width, height: size.height - tabsHeight))
|
||||
|
||||
@ -706,7 +706,6 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
||||
}
|
||||
|
||||
for index in visiblePaneIndices {
|
||||
let indexOffset = CGFloat(index - currentIndex)
|
||||
let key = availablePanes[index]
|
||||
if self.currentPanes[key] == nil && self.pendingPanes[key] == nil {
|
||||
requiredPendingKeys.append(key)
|
||||
@ -781,8 +780,8 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
||||
self.pendingSwitchToPaneKey = nil
|
||||
previousPaneKey = self.currentPaneKey
|
||||
self.currentPaneKey = pendingSwitchToPaneKey
|
||||
updatedCurrentIndex = availablePanes.index(of: pendingSwitchToPaneKey)
|
||||
if let previousPaneKey = previousPaneKey, let previousIndex = availablePanes.index(of: previousPaneKey), let updatedCurrentIndex = updatedCurrentIndex {
|
||||
updatedCurrentIndex = availablePanes.firstIndex(of: pendingSwitchToPaneKey)
|
||||
if let previousPaneKey = previousPaneKey, let previousIndex = availablePanes.firstIndex(of: previousPaneKey), let updatedCurrentIndex = updatedCurrentIndex {
|
||||
if updatedCurrentIndex < previousIndex {
|
||||
paneSwitchAnimationOffset = -size.width
|
||||
} else {
|
||||
@ -794,7 +793,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
||||
}
|
||||
|
||||
for (key, pane) in self.currentPanes {
|
||||
if let index = availablePanes.index(of: key), let updatedCurrentIndex = updatedCurrentIndex {
|
||||
if let index = availablePanes.firstIndex(of: key), let updatedCurrentIndex = updatedCurrentIndex {
|
||||
var paneWasAdded = false
|
||||
if pane.node.supernode == nil {
|
||||
self.addSubnode(pane.node)
|
||||
@ -811,7 +810,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
||||
}
|
||||
pane.isAnimatingOut = false
|
||||
if let (size, sideInset, bottomInset, visibleHeight, expansionFraction, presentationData, data) = strongSelf.currentParams {
|
||||
if let availablePanes = data?.availablePanes, let currentPaneKey = strongSelf.currentPaneKey, let currentIndex = availablePanes.index(of: currentPaneKey), let paneIndex = availablePanes.index(of: key), abs(paneIndex - currentIndex) <= 1 {
|
||||
if let availablePanes = data?.availablePanes, let currentPaneKey = strongSelf.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey), let paneIndex = availablePanes.firstIndex(of: key), abs(paneIndex - currentIndex) <= 1 {
|
||||
} else {
|
||||
if let pane = strongSelf.currentPanes.removeValue(forKey: key) {
|
||||
//print("remove \(key)")
|
||||
|
Loading…
x
Reference in New Issue
Block a user