mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Poll and sticker pack improvements
This commit is contained in:
parent
f0c1f3ff3e
commit
203acd8567
@ -1200,7 +1200,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
||||
|
||||
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 8.0), size: CGSize())
|
||||
|
||||
parentController.present(TooltipScreen(text: text, icon: .chatListPress, location: location, shouldDismissOnTouch: { point in
|
||||
parentController.present(TooltipScreen(text: text, icon: .chatListPress, location: .point(location), shouldDismissOnTouch: { point in
|
||||
guard let strongSelf = self, let parentController = strongSelf.parent as? TabBarController else {
|
||||
return .dismiss(consume: false)
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ public extension ContainerViewLayout {
|
||||
func insets(options: ContainerViewLayoutInsetOptions) -> UIEdgeInsets {
|
||||
var insets = self.intrinsicInsets
|
||||
if let statusBarHeight = self.statusBarHeight, options.contains(.statusBar) {
|
||||
insets.top += statusBarHeight
|
||||
insets.top = max(statusBarHeight, insets.top)
|
||||
}
|
||||
if let inputHeight = self.inputHeight, options.contains(.input) {
|
||||
insets.bottom = max(inputHeight, insets.bottom)
|
||||
|
@ -56,7 +56,7 @@ public final class PresentationContext {
|
||||
|
||||
private var presentationDisposables = DisposableSet()
|
||||
|
||||
var topLevelSubview: () -> UIView? = { nil }
|
||||
public var topLevelSubview: () -> UIView? = { nil }
|
||||
|
||||
var isCurrentlyOpaque: Bool {
|
||||
for (controller, _) in self.controllers {
|
||||
|
@ -548,16 +548,6 @@ private func continueSynchronizeInstalledStickerPacks(transaction: Transaction,
|
||||
for info in resultingCollectionInfos {
|
||||
resultingInfos[info.0.id] = info
|
||||
}
|
||||
for (id, info) in resultingInfos {
|
||||
if info.0.shortName.lowercased() == "thevirus" {
|
||||
print("id1 = \(id)")
|
||||
}
|
||||
}
|
||||
for (id, info) in localInfos {
|
||||
if info.shortName.lowercased() == "thevirus" {
|
||||
print("id1 = \(id)")
|
||||
}
|
||||
}
|
||||
|
||||
let resolvedItems = resolveStickerPacks(network: network, remoteInfos: resultingInfos, localInfos: localInfos)
|
||||
|
||||
|
@ -373,6 +373,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
super.init(context: context, navigationBarPresentationData: navigationBarPresentationData, mediaAccessoryPanelVisibility: mediaAccessoryPanelVisibility, locationBroadcastPanelSource: locationBroadcastPanelSource)
|
||||
|
||||
self.automaticallyControlPresentationContextLayout = false
|
||||
self.blocksBackgroundWhenInOverlay = true
|
||||
|
||||
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
|
||||
@ -1950,7 +1951,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
var foundItemNode: ListViewItemNode?
|
||||
|
||||
/*var foundItemNode: ListViewItemNode?
|
||||
strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView {
|
||||
if sourceNode.view.isDescendant(of: itemNode.view) {
|
||||
@ -1959,6 +1961,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
if let foundItemNode = foundItemNode {
|
||||
|
||||
let absoluteFrame = sourceNode.view.convert(sourceNode.bounds, to: strongSelf.view).insetBy(dx: 0.0, dy: -4.0).offsetBy(dx: 0.0, dy: 0.0)
|
||||
let tooltipScreen = TooltipScreen(text: solution.text, textEntities: solution.entities, icon: nil, location: absoluteFrame, shouldDismissOnTouch: { point in
|
||||
return .dismiss(consume: absoluteFrame.contains(point))
|
||||
@ -2012,7 +2015,82 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
strongSelf.currentMessageTooltipScreens.append((tooltipScreen, foundItemNode))
|
||||
strongSelf.present(tooltipScreen, in: .current)
|
||||
}*/
|
||||
|
||||
var found = false
|
||||
strongSelf.forEachController({ controller in
|
||||
if let controller = controller as? TooltipScreen {
|
||||
if controller.text == solution.text && controller.textEntities == solution.entities {
|
||||
found = true
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
if found {
|
||||
return
|
||||
}
|
||||
|
||||
let tooltipScreen = TooltipScreen(text: solution.text, textEntities: solution.entities, icon: .info, location: .top, shouldDismissOnTouch: { point in
|
||||
return .ignore
|
||||
}, openActiveTextItem: { item, action in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
switch item {
|
||||
case let .url(url):
|
||||
switch action {
|
||||
case .tap:
|
||||
strongSelf.openUrl(url, concealed: false)
|
||||
case .longTap:
|
||||
strongSelf.controllerInteraction?.longTap(.url(url), nil)
|
||||
}
|
||||
case let .mention(peerId, mention):
|
||||
switch action {
|
||||
case .tap:
|
||||
strongSelf.controllerInteraction?.openPeer(peerId, .default, nil)
|
||||
case .longTap:
|
||||
strongSelf.controllerInteraction?.longTap(.peerMention(peerId, mention), nil)
|
||||
}
|
||||
case let .textMention(mention):
|
||||
switch action {
|
||||
case .tap:
|
||||
strongSelf.controllerInteraction?.openPeerMention(mention)
|
||||
case .longTap:
|
||||
strongSelf.controllerInteraction?.longTap(.mention(mention), nil)
|
||||
}
|
||||
case let .botCommand(command):
|
||||
switch action {
|
||||
case .tap:
|
||||
strongSelf.controllerInteraction?.sendBotCommand(nil, command)
|
||||
case .longTap:
|
||||
strongSelf.controllerInteraction?.longTap(.command(command), nil)
|
||||
}
|
||||
case let .hashtag(hashtag):
|
||||
switch action {
|
||||
case .tap:
|
||||
strongSelf.controllerInteraction?.openHashtag(nil, hashtag)
|
||||
case .longTap:
|
||||
strongSelf.controllerInteraction?.longTap(.hashtag(hashtag), nil)
|
||||
}
|
||||
}
|
||||
})
|
||||
/*tooltipScreen.becameDismissed = { tooltipScreen in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.currentMessageTooltipScreens.removeAll(where: { $0.0 === tooltipScreen })
|
||||
}
|
||||
strongSelf.currentMessageTooltipScreens.append((tooltipScreen, foundItemNode))*/
|
||||
|
||||
strongSelf.forEachController({ controller in
|
||||
if let controller = controller as? TooltipScreen {
|
||||
controller.dismiss()
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
strongSelf.present(tooltipScreen, in: .current)
|
||||
}, requestMessageUpdate: { [weak self] id in
|
||||
if let strongSelf = self {
|
||||
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id)
|
||||
|
@ -247,6 +247,13 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
super.init()
|
||||
|
||||
self.controller?.presentationContext.topLevelSubview = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return nil
|
||||
}
|
||||
return strongSelf.titleAccessoryPanelContainer.view
|
||||
}
|
||||
|
||||
self.setViewBlock({
|
||||
return ChatControllerNodeView()
|
||||
})
|
||||
@ -1087,6 +1094,10 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
listInsets.top = listInsets.top + messageActionSheetControllerAdditionalInset
|
||||
}
|
||||
|
||||
var childredLayout = layout
|
||||
childredLayout.intrinsicInsets = UIEdgeInsets(top: listInsets.bottom, left: listInsets.right, bottom: listInsets.top, right: listInsets.left)
|
||||
self.controller?.presentationContext.containerLayoutUpdated(childredLayout, transition: transition)
|
||||
|
||||
listViewTransaction(ListViewUpdateSizeAndInsets(size: contentBounds.size, insets: listInsets, scrollIndicatorInsets: listScrollIndicatorInsets, duration: duration, curve: curve, ensureTopInsetForOverlayHighlightedItems: ensureTopInsetForOverlayHighlightedItems), additionalScrollDistance, scrollToTop, { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.notifyTransitionCompletionListeners(transition: transition)
|
||||
|
@ -1579,10 +1579,14 @@ final class ChatMediaInputNode: ChatInputNode {
|
||||
placeholderNode = self.gifPane.searchPlaceholderNode
|
||||
paneIsEmpty = self.gifPane.isEmpty
|
||||
case .sticker:
|
||||
paneIsEmpty = true
|
||||
self.stickerPane.gridNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? PaneSearchBarPlaceholderNode {
|
||||
placeholderNode = itemNode
|
||||
}
|
||||
if let _ = itemNode as? ChatMediaInputStickerGridItemNode {
|
||||
paneIsEmpty = false
|
||||
}
|
||||
}
|
||||
case .trending:
|
||||
/*self.trendingPane.gridNode.forEachItemNode { itemNode in
|
||||
|
@ -87,7 +87,7 @@ final class TrendingPanePackEntry: Identifiable, Comparable {
|
||||
|
||||
func item(account: Account, interaction: TrendingPaneInteraction, grid: Bool) -> GridItem {
|
||||
let info = self.info
|
||||
return StickerPaneSearchGlobalItem(account: account, theme: self.theme, strings: self.strings, info: self.info, topItems: self.topItems, grid: grid, topSeparator: self.topSeparator, installed: self.installed, unread: self.unread, open: {
|
||||
return StickerPaneSearchGlobalItem(account: account, theme: self.theme, strings: self.strings, listAppearance: false, info: self.info, topItems: self.topItems, grid: grid, topSeparator: self.topSeparator, installed: self.installed, unread: self.unread, open: {
|
||||
interaction.openPack(info)
|
||||
}, install: {
|
||||
interaction.installPack(info)
|
||||
|
@ -1009,6 +1009,10 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
let message = item.message
|
||||
|
||||
let incoming = item.message.effectivelyIncoming(item.context.account.peerId)
|
||||
var isBotChat: Bool = false
|
||||
if let peer = item.message.peers[item.message.id.peerId] as? TelegramUser, peer.botInfo != nil {
|
||||
isBotChat = true
|
||||
}
|
||||
|
||||
let additionalTextRightInset: CGFloat = 24.0
|
||||
|
||||
@ -1120,8 +1124,11 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
let (typeLayout, typeApply) = makeTypeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: typeText, font: labelsFont, textColor: messageTheme.secondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let votersString: String
|
||||
if let poll = poll, let totalVoters = poll.results.totalVoters {
|
||||
let votersString: String?
|
||||
|
||||
if isBotChat {
|
||||
votersString = nil
|
||||
} else if let poll = poll, let totalVoters = poll.results.totalVoters {
|
||||
switch poll.kind {
|
||||
case .poll:
|
||||
if totalVoters == 0 {
|
||||
@ -1139,7 +1146,7 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
} else {
|
||||
votersString = " "
|
||||
}
|
||||
let (votersLayout, votersApply) = makeVotersLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: votersString, font: labelsFont, textColor: messageTheme.secondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: textInsets))
|
||||
let (votersLayout, votersApply) = makeVotersLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: votersString ?? "", font: labelsFont, textColor: messageTheme.secondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: textInsets))
|
||||
|
||||
let (buttonSubmitInactiveTextLayout, buttonSubmitInactiveTextApply) = makeSubmitInactiveTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.MessagePoll_SubmitVote, font: Font.regular(17.0), textColor: messageTheme.accentControlDisabledColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: textInsets))
|
||||
let (buttonSubmitActiveTextLayout, buttonSubmitActiveTextApply) = makeSubmitActiveTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.MessagePoll_SubmitVote, font: Font.regular(17.0), textColor: messageTheme.polls.bar), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: textInsets))
|
||||
@ -1274,7 +1281,11 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
let optionsVotersSpacing: CGFloat = 11.0
|
||||
let optionsButtonSpacing: CGFloat = 9.0
|
||||
let votersBottomSpacing: CGFloat = 11.0
|
||||
resultSize.height += optionsVotersSpacing + votersLayout.size.height + votersBottomSpacing
|
||||
if votersString != nil {
|
||||
resultSize.height += optionsVotersSpacing + votersLayout.size.height + votersBottomSpacing
|
||||
} else {
|
||||
resultSize.height += 26.0
|
||||
}
|
||||
|
||||
let buttonSubmitInactiveTextFrame = CGRect(origin: CGPoint(x: floor((resultSize.width - buttonSubmitInactiveTextLayout.size.width) / 2.0), y: optionsButtonSpacing), size: buttonSubmitInactiveTextLayout.size)
|
||||
let buttonSubmitActiveTextFrame = CGRect(origin: CGPoint(x: floor((resultSize.width - buttonSubmitActiveTextLayout.size.width) / 2.0), y: optionsButtonSpacing), size: buttonSubmitActiveTextLayout.size)
|
||||
@ -1437,7 +1448,7 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
strongSelf.timerNode = timerNode
|
||||
strongSelf.addSubnode(timerNode)
|
||||
timerNode.reachedTimeout = {
|
||||
guard let strongSelf = self, let item = strongSelf.item else {
|
||||
guard let strongSelf = self, let _ = strongSelf.item else {
|
||||
return
|
||||
}
|
||||
//item.controllerInteraction.requestMessageUpdate(item.message.id)
|
||||
@ -1502,6 +1513,7 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
strongSelf.avatarsNode.frame = avatarsFrame
|
||||
strongSelf.avatarsNode.updateLayout(size: avatarsFrame.size)
|
||||
strongSelf.avatarsNode.update(context: item.context, peers: avatarPeers, synchronousLoad: synchronousLoad)
|
||||
strongSelf.avatarsNode.isHidden = isBotChat
|
||||
let alphaTransition: ContainedViewLayoutTransition
|
||||
if animation.isAnimated {
|
||||
alphaTransition = .animated(duration: 0.25, curve: .easeInOut)
|
||||
@ -1542,6 +1554,11 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
return
|
||||
}
|
||||
|
||||
var isBotChat: Bool = false
|
||||
if let peer = item.message.peers[item.message.id.peerId] as? TelegramUser, peer.botInfo != nil {
|
||||
isBotChat = true
|
||||
}
|
||||
|
||||
let disableAllActions = false
|
||||
|
||||
var hasSelection = false
|
||||
@ -1593,8 +1610,14 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
} else {
|
||||
if case .public = poll.publicity, hasResults, !disableAllActions {
|
||||
self.votersNode.isHidden = true
|
||||
self.buttonViewResultsTextNode.isHidden = false
|
||||
self.buttonNode.isHidden = false
|
||||
|
||||
if isBotChat {
|
||||
self.buttonViewResultsTextNode.isHidden = true
|
||||
self.buttonNode.isHidden = true
|
||||
} else {
|
||||
self.buttonViewResultsTextNode.isHidden = false
|
||||
self.buttonNode.isHidden = false
|
||||
}
|
||||
|
||||
if Namespaces.Message.allScheduled.contains(item.message.id.namespace) {
|
||||
self.buttonNode.isUserInteractionEnabled = false
|
||||
@ -1650,11 +1673,16 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
return .none
|
||||
}
|
||||
} else {
|
||||
var isBotChat: Bool = false
|
||||
if let item = self.item, let peer = item.message.peers[item.message.id.peerId] as? TelegramUser, peer.botInfo != nil {
|
||||
isBotChat = true
|
||||
}
|
||||
|
||||
for optionNode in self.optionNodes {
|
||||
if optionNode.frame.contains(point), case .tap = gesture {
|
||||
if optionNode.isUserInteractionEnabled {
|
||||
return .ignore
|
||||
} else if let result = optionNode.currentResult, let item = self.item, !Namespaces.Message.allScheduled.contains(item.message.id.namespace), let poll = self.poll, let option = optionNode.option {
|
||||
} else if let result = optionNode.currentResult, let item = self.item, !Namespaces.Message.allScheduled.contains(item.message.id.namespace), let poll = self.poll, let option = optionNode.option, !isBotChat {
|
||||
switch poll.publicity {
|
||||
case .anonymous:
|
||||
let string: String
|
||||
|
@ -89,7 +89,7 @@ private final class FeaturedPackEntry: Identifiable, Comparable {
|
||||
|
||||
func item(account: Account, interaction: FeaturedInteraction, grid: Bool) -> GridItem {
|
||||
let info = self.info
|
||||
return StickerPaneSearchGlobalItem(account: account, theme: self.theme, strings: self.strings, info: self.info, topItems: self.topItems, grid: grid, topSeparator: self.topSeparator, installed: self.installed, unread: self.unread, open: {
|
||||
return StickerPaneSearchGlobalItem(account: account, theme: self.theme, strings: self.strings, listAppearance: true, info: self.info, topItems: self.topItems, grid: grid, topSeparator: self.topSeparator, installed: self.installed, unread: self.unread, open: {
|
||||
interaction.openPack(info)
|
||||
}, install: {
|
||||
interaction.installPack(info, !self.installed)
|
||||
@ -303,6 +303,9 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
|
||||
self.searchNode?.updateActivity = { [weak self] activity in
|
||||
self?.controller?.searchNavigationNode?.setActivity(activity)
|
||||
}
|
||||
self.searchNode?.deactivateSearchBar = { [weak self] in
|
||||
self?.view.endEditing(true)
|
||||
}
|
||||
|
||||
let interaction = FeaturedInteraction(
|
||||
installPack: { [weak self] info, install in
|
||||
@ -478,6 +481,15 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
|
||||
self.loadMoreDisposable.dispose()
|
||||
}
|
||||
|
||||
func updatePresentationData(presentationData: PresentationData) {
|
||||
self.presentationData = presentationData
|
||||
|
||||
self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
||||
|
||||
self.searchNode?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings)
|
||||
|
||||
}
|
||||
|
||||
private func loadMore() {
|
||||
if self.isLoadingMore || !self.canLoadMore {
|
||||
return
|
||||
@ -506,6 +518,8 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||
|
||||
self.view.addGestureRecognizer(PeekControllerGestureRecognizer(contentAtPoint: { [weak self] point in
|
||||
guard let strongSelf = self else {
|
||||
return nil
|
||||
@ -667,7 +681,7 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
|
||||
if let searchNode = self.searchNode {
|
||||
let searchNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top))
|
||||
transition.updateFrame(node: searchNode, frame: searchNodeFrame)
|
||||
searchNode.updateLayout(size: searchNodeFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: insets.bottom, inputHeight: layout.inputHeight ?? 0.0, deviceMetrics: layout.deviceMetrics, transition: transition)
|
||||
searchNode.updateLayout(size: searchNodeFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: insets.bottom + layout.safeInsets.bottom, inputHeight: layout.inputHeight ?? 0.0, deviceMetrics: layout.deviceMetrics, transition: transition)
|
||||
}
|
||||
|
||||
let itemSize: CGSize
|
||||
@ -677,7 +691,7 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
|
||||
itemSize = CGSize(width: layout.size.width, height: 128.0)
|
||||
}
|
||||
|
||||
self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: layout.size, insets: UIEdgeInsets(top: insets.top, left: layout.safeInsets.left, bottom: insets.bottom, right: layout.safeInsets.right), preloadSize: 300.0, type: .fixed(itemSize: itemSize, fillWidth: nil, lineSpacing: 0.0, itemSpacing: nil)), transition: transition), itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil), completion: { _ in })
|
||||
self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: layout.size, insets: UIEdgeInsets(top: insets.top, left: layout.safeInsets.left, bottom: insets.bottom + layout.safeInsets.bottom, right: layout.safeInsets.right), preloadSize: 300.0, type: .fixed(itemSize: itemSize, fillWidth: nil, lineSpacing: 0.0, itemSpacing: nil)), transition: transition), itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil), completion: { _ in })
|
||||
|
||||
transition.updateFrame(node: self.gridNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: layout.size.height)))
|
||||
|
||||
@ -751,6 +765,7 @@ final class FeaturedStickersScreen: ViewController {
|
||||
}
|
||||
|
||||
private var presentationData: PresentationData
|
||||
private var presentationDataDisposable: Disposable?
|
||||
|
||||
private let _ready = Promise<Bool>()
|
||||
override public var ready: Promise<Bool> {
|
||||
@ -777,12 +792,36 @@ final class FeaturedStickersScreen: ViewController {
|
||||
self.searchNavigationNode = searchNavigationNode
|
||||
|
||||
self.navigationBar?.setContentNode(searchNavigationNode, animated: false)
|
||||
|
||||
self.presentationDataDisposable = (context.sharedContext.presentationData
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||
if let strongSelf = self {
|
||||
let previous = strongSelf.presentationData
|
||||
strongSelf.presentationData = presentationData
|
||||
|
||||
if previous.theme !== presentationData.theme || previous.strings !== presentationData.strings {
|
||||
strongSelf.updatePresentationData()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
required init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.presentationDataDisposable?.dispose()
|
||||
}
|
||||
|
||||
private func updatePresentationData() {
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
|
||||
self.searchNavigationNode?.updatePresentationData(theme: self.presentationData.theme, strings: self.presentationData.strings)
|
||||
|
||||
self.controllerNode.updatePresentationData(presentationData: presentationData)
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = FeaturedStickersScreenNode(
|
||||
context: self.context,
|
||||
@ -816,8 +855,8 @@ final class FeaturedStickersScreen: ViewController {
|
||||
}
|
||||
|
||||
private final class SearchNavigationContentNode: NavigationBarContentNode {
|
||||
private let theme: PresentationTheme
|
||||
private let strings: PresentationStrings
|
||||
private var theme: PresentationTheme
|
||||
private var strings: PresentationStrings
|
||||
|
||||
private let cancel: () -> Void
|
||||
|
||||
@ -851,6 +890,13 @@ private final class SearchNavigationContentNode: NavigationBarContentNode {
|
||||
}
|
||||
}
|
||||
|
||||
func updatePresentationData(theme: PresentationTheme, strings: PresentationStrings) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
|
||||
self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: theme), strings: strings)
|
||||
}
|
||||
|
||||
func setQueryUpdated(_ f: @escaping (String, String?) -> Void) {
|
||||
self.queryUpdated = f
|
||||
}
|
||||
@ -951,7 +997,7 @@ private enum FeaturedSearchEntry: Identifiable, Comparable {
|
||||
interaction.sendSticker(.standalone(media: stickerItem.file), node, rect)
|
||||
})
|
||||
case let .global(_, info, topItems, installed, topSeparator):
|
||||
return StickerPaneSearchGlobalItem(account: account, theme: theme, strings: strings, info: info, topItems: topItems, grid: false, topSeparator: topSeparator, installed: installed, unread: false, open: {
|
||||
return StickerPaneSearchGlobalItem(account: account, theme: theme, strings: strings, listAppearance: false, info: info, topItems: topItems, grid: false, topSeparator: topSeparator, installed: installed, unread: false, open: {
|
||||
interaction.open(info)
|
||||
}, install: {
|
||||
interaction.install(info, topItems, !installed)
|
||||
|
@ -104,7 +104,7 @@ private enum StickerSearchEntry: Identifiable, Comparable {
|
||||
interaction.sendSticker(.standalone(media: stickerItem.file), node, rect)
|
||||
})
|
||||
case let .global(_, info, topItems, installed, topSeparator):
|
||||
return StickerPaneSearchGlobalItem(account: account, theme: theme, strings: strings, info: info, topItems: topItems, grid: false, topSeparator: topSeparator, installed: installed, unread: false, open: {
|
||||
return StickerPaneSearchGlobalItem(account: account, theme: theme, strings: strings, listAppearance: false, info: info, topItems: topItems, grid: false, topSeparator: topSeparator, installed: installed, unread: false, open: {
|
||||
interaction.open(info)
|
||||
}, install: {
|
||||
interaction.install(info, topItems, !installed)
|
||||
|
@ -37,6 +37,7 @@ final class StickerPaneSearchGlobalItem: GridItem {
|
||||
let account: Account
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let listAppearance: Bool
|
||||
let info: StickerPackCollectionInfo
|
||||
let topItems: [StickerPackItem]
|
||||
let grid: Bool
|
||||
@ -53,10 +54,11 @@ final class StickerPaneSearchGlobalItem: GridItem {
|
||||
return self.grid ? nil : (128.0 + (self.topSeparator ? 12.0 : 0.0))
|
||||
}
|
||||
|
||||
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, info: StickerPackCollectionInfo, topItems: [StickerPackItem], grid: Bool, topSeparator: Bool, installed: Bool, installing: Bool = false, unread: Bool, open: @escaping () -> Void, install: @escaping () -> Void, getItemIsPreviewed: @escaping (StickerPackItem) -> Bool) {
|
||||
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, listAppearance: Bool, info: StickerPackCollectionInfo, topItems: [StickerPackItem], grid: Bool, topSeparator: Bool, installed: Bool, installing: Bool = false, unread: Bool, open: @escaping () -> Void, install: @escaping () -> Void, getItemIsPreviewed: @escaping (StickerPackItem) -> Bool) {
|
||||
self.account = account
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.listAppearance = listAppearance
|
||||
self.info = info
|
||||
self.topItems = topItems
|
||||
self.grid = grid
|
||||
@ -95,6 +97,9 @@ class StickerPaneSearchGlobalItemNode: GridItemNode {
|
||||
private let installTextNode: TextNode
|
||||
private let installBackgroundNode: ASImageNode
|
||||
private let installButtonNode: HighlightTrackingButtonNode
|
||||
private let uninstallTextNode: TextNode
|
||||
private let uninstallBackgroundNode: ASImageNode
|
||||
private let uninstallButtonNode: HighlightTrackingButtonNode
|
||||
private var itemNodes: [TrendingTopItemNode]
|
||||
private let topSeparatorNode: ASDisplayNode
|
||||
|
||||
@ -149,6 +154,18 @@ class StickerPaneSearchGlobalItemNode: GridItemNode {
|
||||
|
||||
self.installButtonNode = HighlightTrackingButtonNode()
|
||||
|
||||
self.uninstallTextNode = TextNode()
|
||||
self.uninstallTextNode.isUserInteractionEnabled = false
|
||||
self.uninstallTextNode.contentMode = .left
|
||||
self.uninstallTextNode.contentsScale = UIScreen.main.scale
|
||||
|
||||
self.uninstallBackgroundNode = ASImageNode()
|
||||
self.uninstallBackgroundNode.isLayerBacked = true
|
||||
self.uninstallBackgroundNode.displayWithoutProcessing = true
|
||||
self.uninstallBackgroundNode.displaysAsynchronously = false
|
||||
|
||||
self.uninstallButtonNode = HighlightTrackingButtonNode()
|
||||
|
||||
self.topSeparatorNode = ASDisplayNode()
|
||||
self.topSeparatorNode.isLayerBacked = true
|
||||
|
||||
@ -162,6 +179,9 @@ class StickerPaneSearchGlobalItemNode: GridItemNode {
|
||||
self.addSubnode(self.installBackgroundNode)
|
||||
self.addSubnode(self.installTextNode)
|
||||
self.addSubnode(self.installButtonNode)
|
||||
self.addSubnode(self.uninstallBackgroundNode)
|
||||
self.addSubnode(self.uninstallTextNode)
|
||||
self.addSubnode(self.uninstallButtonNode)
|
||||
self.addSubnode(self.topSeparatorNode)
|
||||
|
||||
self.installButtonNode.highligthedChanged = { [weak self] highlighted in
|
||||
@ -179,8 +199,24 @@ class StickerPaneSearchGlobalItemNode: GridItemNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.installButtonNode.addTarget(self, action: #selector(self.installPressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.uninstallButtonNode.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
strongSelf.uninstallBackgroundNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.uninstallBackgroundNode.alpha = 0.4
|
||||
strongSelf.uninstallTextNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.uninstallTextNode.alpha = 0.4
|
||||
} else {
|
||||
strongSelf.uninstallBackgroundNode.alpha = 1.0
|
||||
strongSelf.uninstallBackgroundNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
strongSelf.uninstallTextNode.alpha = 1.0
|
||||
strongSelf.uninstallTextNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.uninstallButtonNode.addTarget(self, action: #selector(self.installPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -219,9 +255,14 @@ class StickerPaneSearchGlobalItemNode: GridItemNode {
|
||||
|
||||
self.topSeparatorNode.isHidden = !item.topSeparator
|
||||
self.topSeparatorNode.frame = CGRect(origin: CGPoint(x: 16.0, y: 16.0), size: CGSize(width: params.width - 16.0 * 2.0, height: UIScreenPixel))
|
||||
self.topSeparatorNode.backgroundColor = item.theme.chat.inputMediaPanel.stickersSectionTextColor.withAlphaComponent(0.3)
|
||||
if item.listAppearance {
|
||||
self.topSeparatorNode.backgroundColor = item.theme.list.itemPlainSeparatorColor
|
||||
} else {
|
||||
self.topSeparatorNode.backgroundColor = item.theme.chat.inputMediaPanel.stickersSectionTextColor.withAlphaComponent(0.3)
|
||||
}
|
||||
|
||||
let makeInstallLayout = TextNode.asyncLayout(self.installTextNode)
|
||||
let makeUninstallLayout = TextNode.asyncLayout(self.uninstallTextNode)
|
||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let makeDescriptionLayout = TextNode.asyncLayout(self.descriptionNode)
|
||||
|
||||
@ -229,43 +270,57 @@ class StickerPaneSearchGlobalItemNode: GridItemNode {
|
||||
self.appliedItem = item
|
||||
|
||||
var updateButtonBackgroundImage: UIImage?
|
||||
if currentItem?.theme !== item.theme || currentItem?.installed != item.installed {
|
||||
if item.installed {
|
||||
updateButtonBackgroundImage = PresentationResourcesChat.chatInputMediaPanelAddedPackButtonImage(item.theme)
|
||||
} else {
|
||||
updateButtonBackgroundImage = PresentationResourcesChat.chatInputMediaPanelAddPackButtonImage(item.theme)
|
||||
}
|
||||
var updateUninstallButtonBackgroundImage: UIImage?
|
||||
if currentItem?.theme !== item.theme {
|
||||
updateUninstallButtonBackgroundImage = PresentationResourcesChat.chatInputMediaPanelAddedPackButtonImage(item.theme)
|
||||
updateButtonBackgroundImage = PresentationResourcesChat.chatInputMediaPanelAddPackButtonImage(item.theme)
|
||||
}
|
||||
let unreadImage = PresentationResourcesItemList.stickerUnreadDotImage(item.theme)
|
||||
|
||||
let leftInset: CGFloat = 14.0
|
||||
let rightInset: CGFloat = 16.0
|
||||
|
||||
let (installLayout, installApply) = makeInstallLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.installed ? item.strings.Stickers_Installed : item.strings.Stickers_Install, font: buttonFont, textColor: item.installed ? item.theme.list.itemCheckColors.fillColor : item.theme.list.itemCheckColors.foregroundColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (installLayout, installApply) = makeInstallLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.strings.Stickers_Install, font: buttonFont, textColor: item.theme.list.itemCheckColors.foregroundColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.info.title, font: titleFont, textColor: item.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - leftInset - rightInset - 20.0 - installLayout.size.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (uninstallLayout, uninstallApply) = makeUninstallLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.strings.Stickers_Installed, font: buttonFont, textColor: item.theme.list.itemCheckColors.fillColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.info.title, font: titleFont, textColor: item.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - leftInset - rightInset - 20.0 - max(installLayout.size.width, uninstallLayout.size.width), height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (descriptionLayout, descriptionApply) = makeDescriptionLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.strings.StickerPack_StickerCount(item.info.count), font: statusFont, textColor: item.theme.chat.inputMediaPanel.stickersSectionTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - leftInset - rightInset - 20.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let strongSelf = self
|
||||
|
||||
let _ = installApply()
|
||||
let _ = uninstallApply()
|
||||
let _ = titleApply()
|
||||
let _ = descriptionApply()
|
||||
|
||||
if let updateButtonBackgroundImage = updateButtonBackgroundImage {
|
||||
strongSelf.installBackgroundNode.image = updateButtonBackgroundImage
|
||||
}
|
||||
if let updateUninstallButtonBackgroundImage = updateUninstallButtonBackgroundImage {
|
||||
strongSelf.uninstallBackgroundNode.image = updateUninstallButtonBackgroundImage
|
||||
}
|
||||
|
||||
let installWidth: CGFloat = installLayout.size.width + 32.0
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: params.width - params.rightInset - rightInset - installWidth, y: 4.0 + topOffset), size: CGSize(width: installWidth, height: 28.0))
|
||||
strongSelf.installBackgroundNode.frame = buttonFrame
|
||||
strongSelf.installTextNode.frame = CGRect(origin: CGPoint(x: buttonFrame.minX + floor((buttonFrame.width - installLayout.size.width) / 2.0), y: buttonFrame.minY + floor((buttonFrame.height - installLayout.size.height) / 2.0) + 1.0), size: installLayout.size)
|
||||
strongSelf.installButtonNode.frame = buttonFrame
|
||||
|
||||
let uninstallWidth: CGFloat = uninstallLayout.size.width + 32.0
|
||||
let uninstallButtonFrame = CGRect(origin: CGPoint(x: params.width - params.rightInset - rightInset - uninstallWidth, y: 4.0 + topOffset), size: CGSize(width: uninstallWidth, height: 28.0))
|
||||
strongSelf.uninstallBackgroundNode.frame = uninstallButtonFrame
|
||||
strongSelf.uninstallTextNode.frame = CGRect(origin: CGPoint(x: uninstallButtonFrame.minX + floor((uninstallButtonFrame.width - uninstallLayout.size.width) / 2.0), y: uninstallButtonFrame.minY + floor((uninstallButtonFrame.height - uninstallLayout.size.height) / 2.0) + 1.0), size: uninstallLayout.size)
|
||||
strongSelf.uninstallButtonNode.frame = uninstallButtonFrame
|
||||
|
||||
strongSelf.installButtonNode.isHidden = false
|
||||
strongSelf.installBackgroundNode.isHidden = false
|
||||
strongSelf.installTextNode.isHidden = false
|
||||
strongSelf.installButtonNode.isHidden = item.installed
|
||||
strongSelf.installBackgroundNode.isHidden = item.installed
|
||||
strongSelf.installTextNode.isHidden = item.installed
|
||||
|
||||
strongSelf.uninstallButtonNode.isHidden = !item.installed
|
||||
strongSelf.uninstallBackgroundNode.isHidden = !item.installed
|
||||
strongSelf.uninstallTextNode.isHidden = !item.installed
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: params.leftInset + leftInset, y: 2.0 + topOffset), size: titleLayout.size)
|
||||
strongSelf.titleNode.frame = titleFrame
|
||||
|
@ -25,7 +25,7 @@ public enum TooltipActiveTextAction {
|
||||
|
||||
private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
private let icon: TooltipScreen.Icon?
|
||||
private let location: CGRect
|
||||
private let location: TooltipScreen.Location
|
||||
private let shouldDismissOnTouch: (CGPoint) -> TooltipScreen.DismissOnTouch
|
||||
private let requestDismiss: () -> Void
|
||||
|
||||
@ -41,7 +41,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
init(text: String, textEntities: [MessageTextEntity], icon: TooltipScreen.Icon?, location: CGRect, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, requestDismiss: @escaping () -> Void, openActiveTextItem: @escaping (TooltipActiveTextItem, TooltipActiveTextAction) -> Void) {
|
||||
init(text: String, textEntities: [MessageTextEntity], icon: TooltipScreen.Icon?, location: TooltipScreen.Location, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, requestDismiss: @escaping () -> Void, openActiveTextItem: @escaping (TooltipActiveTextItem, TooltipActiveTextAction) -> Void) {
|
||||
self.icon = icon
|
||||
self.location = location
|
||||
self.shouldDismissOnTouch = shouldDismissOnTouch
|
||||
@ -184,27 +184,35 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: containerWidth - contentInset * 2.0 - animationSize.width - animationSpacing, height: .greatestFiniteMagnitude))
|
||||
|
||||
let backgroundWidth = textSize.width + contentInset * 2.0 + animationSize.width + animationSpacing
|
||||
var backgroundFrame: CGRect
|
||||
|
||||
let backgroundHeight = max(animationSize.height, textSize.height) + contentVerticalInset * 2.0
|
||||
var backgroundFrame = CGRect(origin: CGPoint(x: self.location.midX - backgroundWidth / 2.0, y: self.location.minY - bottomInset - backgroundHeight), size: CGSize(width: backgroundWidth, height: backgroundHeight))
|
||||
if backgroundFrame.minX < sideInset {
|
||||
backgroundFrame.origin.x = sideInset
|
||||
}
|
||||
if backgroundFrame.maxX > layout.size.width - sideInset {
|
||||
backgroundFrame.origin.x = layout.size.width - sideInset - backgroundFrame.width
|
||||
}
|
||||
|
||||
var invertArrow = false
|
||||
if backgroundFrame.minY < layout.insets(options: .statusBar).top {
|
||||
backgroundFrame.origin.y = self.location.maxY + bottomInset
|
||||
invertArrow = true
|
||||
switch self.location {
|
||||
case let .point(rect):
|
||||
let backgroundWidth = textSize.width + contentInset * 2.0 + animationSize.width + animationSpacing
|
||||
backgroundFrame = CGRect(origin: CGPoint(x: rect.midX - backgroundWidth / 2.0, y: rect.minY - bottomInset - backgroundHeight), size: CGSize(width: backgroundWidth, height: backgroundHeight))
|
||||
if backgroundFrame.minX < sideInset {
|
||||
backgroundFrame.origin.x = sideInset
|
||||
}
|
||||
if backgroundFrame.maxX > layout.size.width - sideInset {
|
||||
backgroundFrame.origin.x = layout.size.width - sideInset - backgroundFrame.width
|
||||
}
|
||||
if backgroundFrame.minY < layout.insets(options: .statusBar).top {
|
||||
backgroundFrame.origin.y = rect.maxY + bottomInset
|
||||
invertArrow = true
|
||||
}
|
||||
self.isArrowInverted = invertArrow
|
||||
case .top:
|
||||
backgroundFrame = CGRect(origin: CGPoint(x: sideInset, y: layout.insets(options: [.statusBar]).top + 13.0), size: CGSize(width: layout.size.width - sideInset * 2.0, height: backgroundHeight))
|
||||
}
|
||||
self.isArrowInverted = invertArrow
|
||||
|
||||
transition.updateFrame(node: self.containerNode, frame: backgroundFrame)
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
|
||||
if let image = self.arrowNode.image {
|
||||
if let image = self.arrowNode.image, case let .point(rect) = self.location {
|
||||
let arrowSize = image.size
|
||||
let arrowCenterX = self.location.midX
|
||||
let arrowCenterX = rect.midX
|
||||
|
||||
let arrowFrame: CGRect
|
||||
|
||||
@ -219,11 +227,13 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
ContainedViewLayoutTransition.immediate.updateTransformScale(node: self.arrowContainer, scale: CGPoint(x: 1.0, y: invertArrow ? -1.0 : 1.0))
|
||||
|
||||
self.arrowNode.frame = CGRect(origin: CGPoint(), size: arrowFrame.size)
|
||||
} else {
|
||||
self.arrowNode.isHidden = true
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: contentInset + animationSize.width + animationSpacing, y: floor((backgroundHeight - textSize.height) / 2.0)), size: textSize))
|
||||
|
||||
transition.updateFrame(node: self.animatedStickerNode, frame: CGRect(origin: CGPoint(x: contentInset - animationInset, y: floor((backgroundHeight - animationSize.height) / 2.0) - animationInset), size: CGSize(width: animationSize.width + animationInset * 2.0, height: animationSize.height + animationInset * 2.0)))
|
||||
transition.updateFrame(node: self.animatedStickerNode, frame: CGRect(origin: CGPoint(x: contentInset - animationInset, y: contentVerticalInset - animationInset), size: CGSize(width: animationSize.width + animationInset * 2.0, height: animationSize.height + animationInset * 2.0)))
|
||||
self.animatedStickerNode.updateLayout(size: CGSize(width: animationSize.width + animationInset * 2.0, height: animationSize.height + animationInset * 2.0))
|
||||
}
|
||||
|
||||
@ -254,10 +264,19 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
}
|
||||
|
||||
func animateIn() {
|
||||
self.containerNode.layer.animateSpring(from: NSNumber(value: Float(0.01)), to: NSNumber(value: Float(1.0)), keyPath: "transform.scale", duration: 0.4, damping: 105.0)
|
||||
let arrowY: CGFloat = self.isArrowInverted ? self.arrowContainer.frame.minY : self.arrowContainer.frame.maxY
|
||||
self.containerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: self.arrowContainer.frame.midX - self.containerNode.bounds.width / 2.0, y: arrowY - self.containerNode.bounds.height / 2.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: 0.4, damping: 105.0, additive: true)
|
||||
self.containerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
switch self.location {
|
||||
case .top:
|
||||
self.containerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.containerNode.layer.animateScale(from: 0.96, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
if let _ = self.validLayout {
|
||||
self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -13.0 - self.backgroundNode.frame.height), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
}
|
||||
case .point:
|
||||
self.containerNode.layer.animateSpring(from: NSNumber(value: Float(0.01)), to: NSNumber(value: Float(1.0)), keyPath: "transform.scale", duration: 0.4, damping: 105.0)
|
||||
let arrowY: CGFloat = self.isArrowInverted ? self.arrowContainer.frame.minY : self.arrowContainer.frame.maxY
|
||||
self.containerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: self.arrowContainer.frame.midX - self.containerNode.bounds.width / 2.0, y: arrowY - self.containerNode.bounds.height / 2.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: 0.4, damping: 105.0, additive: true)
|
||||
self.containerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
|
||||
let animationDelay: Double
|
||||
switch self.icon {
|
||||
@ -275,13 +294,24 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
}
|
||||
|
||||
func animateOut(completion: @escaping () -> Void) {
|
||||
self.containerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
self.containerNode.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false)
|
||||
|
||||
let arrowY: CGFloat = self.isArrowInverted ? self.arrowContainer.frame.minY : self.arrowContainer.frame.maxY
|
||||
self.containerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: self.arrowContainer.frame.midX - self.containerNode.bounds.width / 2.0, y: arrowY - self.containerNode.bounds.height / 2.0), duration: 0.2, removeOnCompletion: false, additive: true)
|
||||
switch self.location {
|
||||
case .top:
|
||||
self.containerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
self.containerNode.layer.animateScale(from: 1.0, to: 0.96, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||
if let _ = self.validLayout {
|
||||
self.containerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -13.0 - self.backgroundNode.frame.height), duration: 0.3, removeOnCompletion: false, additive: true)
|
||||
}
|
||||
case .point:
|
||||
self.containerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
self.containerNode.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false)
|
||||
|
||||
let arrowY: CGFloat = self.isArrowInverted ? self.arrowContainer.frame.minY : self.arrowContainer.frame.maxY
|
||||
self.containerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: self.arrowContainer.frame.midX - self.containerNode.bounds.width / 2.0, y: arrowY - self.containerNode.bounds.height / 2.0), duration: 0.2, removeOnCompletion: false, additive: true)
|
||||
}
|
||||
}
|
||||
|
||||
func addRelativeScrollingOffset(_ value: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
@ -308,10 +338,15 @@ public final class TooltipScreen: ViewController {
|
||||
case dismiss(consume: Bool)
|
||||
}
|
||||
|
||||
private let text: String
|
||||
private let textEntities: [MessageTextEntity]
|
||||
public enum Location {
|
||||
case point(CGRect)
|
||||
case top
|
||||
}
|
||||
|
||||
public let text: String
|
||||
public let textEntities: [MessageTextEntity]
|
||||
private let icon: TooltipScreen.Icon?
|
||||
private let location: CGRect
|
||||
private let location: TooltipScreen.Location
|
||||
private let shouldDismissOnTouch: (CGPoint) -> TooltipScreen.DismissOnTouch
|
||||
private let openActiveTextItem: (TooltipActiveTextItem, TooltipActiveTextAction) -> Void
|
||||
|
||||
@ -324,7 +359,7 @@ public final class TooltipScreen: ViewController {
|
||||
|
||||
public var becameDismissed: ((TooltipScreen) -> Void)?
|
||||
|
||||
public init(text: String, textEntities: [MessageTextEntity] = [], icon: TooltipScreen.Icon?, location: CGRect, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, openActiveTextItem: @escaping (TooltipActiveTextItem, TooltipActiveTextAction) -> Void = { _, _ in }) {
|
||||
public init(text: String, textEntities: [MessageTextEntity] = [], icon: TooltipScreen.Icon?, location: TooltipScreen.Location, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, openActiveTextItem: @escaping (TooltipActiveTextItem, TooltipActiveTextAction) -> Void = { _, _ in }) {
|
||||
self.text = text
|
||||
self.textEntities = textEntities
|
||||
self.icon = icon
|
||||
@ -346,7 +381,7 @@ public final class TooltipScreen: ViewController {
|
||||
|
||||
self.controllerNode.animateIn()
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 20.0, execute: { [weak self] in
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 15.0, execute: { [weak self] in
|
||||
self?.dismiss()
|
||||
})
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user